> For the complete documentation index, see [llms.txt](https://4lec4st.gitbook.io/4lec4st/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://4lec4st.gitbook.io/4lec4st/writes-ups/tryhackme/medium/hammer.md).

# Hammer

## Información de la máquina

<figure><img src="/files/Z95kqdDPFwhi0EHUpXNV" alt=""><figcaption></figcaption></figure>

En este write-up resolvemos **Hammer**, una máquina de dificultad **medium** en TryHackMe. La máquina combina técnicas clásicas de **enumeración web**, **bypass de rate limit**, y **JWT manipulation**, lo que la hace interesante para reforzar conocimientos en entornos web con medidas de protección básicas pero explotables con ingenio.

***

## Reconocimiento

Empezaremos haciendo un primer reconocimiento para ver los puertos abiertos en el sistema.

<figure><img src="/files/YIBTol2gDoEQcxTMr7lj" alt=""><figcaption></figcaption></figure>

**1. `-p-` → Escaneo de todos los puertos (1-65535)**

Por defecto, `nmap` solo escanea los **1000 puertos más comunes**.\
Con `-p-`, forzamos a que escanee **los 65.535 puertos** de la máquina objetivo, lo que puede ser útil para encontrar servicios que corren en puertos poco convencionales.

**2. `--open` → Filtrar solo los puertos abiertos**

Cuando escaneamos un sistema, muchos puertos pueden estar cerrados o filtrados por un firewall.\
Este parámetro hace que `nmap` **solo muestre los puertos abiertos**, reduciendo el ruido y facilitando el análisis.

**3. `-sS` → Stealth Scan (Escaneo Sigiloso con SYN)**

Es conocido como **"TCP SYN Scan"**, y funciona de la siguiente manera:

* `nmap` envía un paquete **SYN** (solicitud de conexión).
* &#x20;Si el puerto está abierto, el servidor responde con **SYN-ACK**.
* En lugar de completar la conexión con un **ACK**, `nmap` responde con un **RST (Reset)**, interrumpiendo la comunicación.

**4. `--min-rate 5000` → Velocidad mínima de paquetes (5000 por segundo)**

Por defecto, `nmap` ajusta dinámicamente la velocidad de los paquetes según la respuesta de la red.\
Con `--min-rate 5000`, obligamos a `nmap` a **no enviar menos de 5000 paquetes por segundo**, acelerando el escaneo.

**5. `-n` → Deshabilitar resolución DNS**

Normalmente, `nmap` intenta resolver el nombre de dominio de las IPs escaneadas (DNS lookup).\
Con `-n`, evitamos esta resolución de nombres, lo que **acelera el escaneo** y evita que los servidores DNS registren nuestra actividad.

**6. `-Pn` → Omitir detección de host (Host Discovery Off)**

Por defecto, `nmap` envía paquetes ICMP (ping) para ver si un host está activo antes de escanearlo.\
Si un firewall bloquea los pings, `nmap` puede marcarlo como "host down" y no escanearlo.

**7. `-oG`** **→ Exportar el contenido en formato grepeable**

Todo el resultado del escaneo lo exportaremos en un archivo llamado **`allPorts` .**

<figure><img src="/files/kjDeJuiHVjugQqPx5bH5" alt=""><figcaption></figcaption></figure>

**1.** `-p22,1337` **→ Escaneo de puertos específicos**

Con `-p22,1337`, le indicamos que **solo** escanee los puertos que le indicamos

**2. `-sCV` → Escaneo de versiones y scripts básicos**

Este parámetro en realidad es **dos combinaciones en una**:

* **`-sC`** → Activa los **NSE scripts por defecto** en `nmap` (similar a usar `--script=default`).\
  Ejecuta scripts básicos para enumerar información del servicio detectado, como banners, versiones, etc.
* **`-sV`** → Realiza **fingerprinting de versiones**, intentando identificar la versión exacta del software en cada puerto abierto.

**3. `-oN` → Guardar el resultado en formato Normal**

Por defecto, `nmap` muestra los resultados en pantalla, pero con **`-oN targeted`**, guardamos el resultado en un archivo en **formato legible para humanos**.

Cuando accedemos a la página web a través del puerto 1337, lo primero que encontramos es un formulario de inicio de sesión con un diseño muy básico, sin elementos visuales complejos ni mecanismos de seguridad visibles. Este tipo de login suele ser una pista de que puede haber fallos o accesos alternativos.

<figure><img src="/files/8NwisuWk3mH4xnCsvC2p" alt=""><figcaption></figcaption></figure>

Al inspeccionar el código fuente HTML de la página, se observa un comentario o una anotación que menciona que los directorios siguen el formato `hmr_<nombre_del_directorio>`. Esta información es clave porque nos da una pauta concreta para orientar la enumeración de directorios en el servidor web.

<figure><img src="/files/qbUcEeULUGrRIYaqjomv" alt=""><figcaption></figcaption></figure>

## Fuzzing web

Para descubrir directorios ocultos en el servidor, realizamos un fuzzing con la herramienta `ffuf`. Utilizando un diccionario de posibles nombres, buscamos rutas que sigan el patrón `hmr_`. Entre los resultados, identificamos el directorio `hmr_logs`, que llama la atención porque puede contener registros o archivos sensibles.

<figure><img src="/files/QG5KNWQLUXwXemy9xAtJ" alt=""><figcaption></figcaption></figure>

Dentro del directorio `hmr_logs`, encontramos un archivo llamado `error.logs`. Al abrirlo, se listan varias rutas internas del servidor (aunque no son estrictamente necesarias para el avance), pero lo más relevante es que aparece un correo electrónico: `tester@hammer.thm`. Este dato puede servir para intentar alguna funcionalidad como el reseteo de contraseña.

<figure><img src="/files/JcgLW9TRjrNlhv1flDuk" alt=""><figcaption></figcaption></figure>

Dado que no conocemos la contraseña de `tester@hammer.thm`, optamos por la opción de recuperación o reseteo de contraseña. El sistema solicita ingresar un código de recuperación de 4 dígitos, el cual debe introducirse en un tiempo limitado. Esto implica que la aplicación genera o espera un código numérico específico antes de permitir el cambio de contraseña.

<figure><img src="/files/DeUBaFrf1J3O1AyAssZp" alt=""><figcaption></figcaption></figure>

Al capturar con BurpSuite la petición HTTP que se envía cuando intentamos introducir el código, obtenemos el formato exacto que utiliza el servidor para validar el código. Esto permite pensar en automatizar el envío masivo de códigos mediante BurpSuite Intruder para encontrar el correcto mediante fuerza bruta.

<figure><img src="/files/Ven9S4NGeRLF4oTHNdUe" alt=""><figcaption></figcaption></figure>

Al intentar la fuerza bruta directa con Intruder, el servidor responde con un error de **Rate Limit**, lo que indica que tiene un mecanismo para limitar la cantidad de peticiones por segundo desde un mismo origen.\
Normalmente, el servidor detecta esto monitoreando el número de peticiones asociadas a una constante, como la dirección IP, una cookie o algún encabezado específico.

## Rate Limit Bypass

Investigando métodos para evadir la limitación de velocidad, encontramos que técnicas como inyectar bytes nulos no son efectivas aquí. Sin embargo, sí se puede engañar al servidor haciéndole creer que cada petición proviene de un cliente distinto, por ejemplo, manipulando el header `X-Forwarded-For` con direcciones IP falsas. Esto permite eludir el sistema de detección de múltiples intentos desde una misma IP.

{% embed url="<https://book.hacktricks.wiki/en/pentesting-web/rate-limit-bypass.html>" %}

Para intentar todos los posibles códigos de recuperación, generamos un wordlist que contiene todos los números del 0000 al 9999. De este modo, podemos automatizar la prueba exhaustiva de todas las combinaciones posibles de un PIN de 4 cifras.

<figure><img src="/files/bstxSJTSR08I0Ih6F9vo" alt=""><figcaption></figcaption></figure>

Además, creamos un wordlist con IPs falsas, que luego se usarán para poblar el header `X-Forwarded-For`. Aunque no es necesario tener miles de IPs distintas, basta con un número razonable para que el servidor no detecte múltiples intentos desde la misma identidad.

<figure><img src="/files/elsgg99dRgtSiZZYtwmj" alt=""><figcaption></figcaption></figure>

Para lanzar el ataque, empleamos el siguiente comando de `ffuf`. Explicamos cada parámetro:

* **`-w 0000to9999.txt:W1`**\
  Carga el wordlist de números del 0000 al 9999, asignando cada número a la variable `W1`.
* **`-w fakes_ip.txt:W2`**\
  Carga el wordlist con las IP falsas. Cada línea corresponde a una IP distinta, asignada a la variable `W2`.
* **`-u "http://10.10.214.73:1337/reset_password.php"`**\
  Especifica la URL a la que se enviarán las peticiones POST para resetear la contraseña.
* **`-X "POST"`**\
  Indica que el método HTTP usado será POST.
* **`-d "recovery_code=W1&s=80"`**\
  Define el cuerpo de la petición POST:
  * `recovery_code=W1`: el número que queremos probar, sustituido dinámicamente.
  * `s=80`: parámetro fijo que probablemente es requerido por el servidor.
* **`-b "PHPSESSID=<SESSIONID>"`**\
  Añade la cookie de sesión PHP para mantener la autenticación durante las pruebas.
* **`-H "X-Forwarded-For: W2"`**\
  Inyecta la IP falsa en el encabezado `X-Forwarded-For` para simular que cada petición proviene de un cliente distinto.
* **`-H "Content-Type: application/x-www-form-urlencoded"`**\
  Define el tipo de contenido como formulario codificado en URL, estándar en peticiones POST.
* **`-fr "Invalid"`**\
  Filtra las respuestas que contengan la palabra “Invalid”, ya que esas son las que indican que el código introducido es incorrecto.
* **`-mode pitchfork`**\
  El modo *pitchfork* hace que ffuf combine las líneas de ambos wordlists en paralelo:
  * Línea 1 de W1 con línea 1 de W2
  * Línea 2 de W1 con línea 2 de W2
  * etc.
* **`-fw 1`**\
  Filtra las respuestas que solo tengan 1 palabra, asumiendo que no aportan información útil.
* **`-rate 100`**\
  Define un límite de 100 peticiones por segundo para mantener un ritmo controlado sin que el servidor bloquee el ataque.
* **`-o output.txt`**\
  Guarda los resultados obtenidos en el archivo `output.txt` para revisarlos posteriormente.

<figure><img src="/files/HF8ptcdM5mMARq410UdJ" alt=""><figcaption></figcaption></figure>

Después de lanzar el ataque con ffuf, eventualmente encontramos el código correcto de 4 cifras que permite completar el reseteo de la contraseña del usuario.

<figure><img src="/files/S4FUwlDlkFfSENsSiW1d" alt=""><figcaption></figcaption></figure>

Con la contraseña reseteada, accedemos al sistema con el usuario `tester@hammer.thm`. Una vez dentro, encontramos la primera flag.

<figure><img src="/files/Gqwm47RCGvhSvsQn09h4" alt=""><figcaption></figcaption></figure>

## JWT

JWT significa **JSON Web Token**, un estándar que se utiliza para transmitir información entre dos partes de forma segura mediante un objeto JSON firmado digitalmente.\
Este token suele contener **tres partes separadas por puntos**:

1. **Header (Encabezado):** indica el tipo de token (JWT) y el algoritmo de firma.
2. **Payload (Carga útil):** contiene los datos que se quieren transmitir, como el usuario, el rol, etc.
3. **Signature (Firma):** asegura que el token no ha sido manipulado, validándolo mediante un secreto o clave pública/privada

Cuando ejecutamos el comando `ls`, observamos que en el directorio `/var/www/html` existe un archivo llamado `.key`.\
Si accedemos a este archivo desde la web, el navegador nos permite descargarlo.

<figure><img src="/files/XxOnsCWKbphzferxLbx0" alt=""><figcaption></figcaption></figure>

Al abrirlo, vemos que contiene una **clave secreta**, aunque inicialmente no sabemos para qué se utiliza.

<figure><img src="/files/BOTNL0Ii8P6TGcZRdgys" alt=""><figcaption></figcaption></figure>

Revisando el **código fuente de la página web**, encontramos una **cadena de texto muy larga con puntos en medio**, lo que indica que es probablemente un **JWT**.

Esto nos hace pensar que la **clave que descargamos anteriormente es la clave secreta usada para firmar el JWT**, o en su defecto, para verificar su validez.

<figure><img src="/files/aRtP0ibbS5Vw7jHr6UKh" alt=""><figcaption></figcaption></figure>

Si inspeccionamos el contenido del JWT con la herramienta jwt.io, vemos que incluye varios datos

{% embed url="<https://jwt.io/>" %}

**kid:** indica el "Key ID", que sugiere dónde o cuál es la clave utilizada para validar el token. Aquí indica que está en `/var/www/mykey.key`.

**rol:** en este momento indica que somos **user**.

<figure><img src="/files/1VM26oIqoR3THc6yxsyn" alt=""><figcaption></figcaption></figure>

## Make JWT admin

El objetivo es **generar un nuevo JWT** con permisos elevados, en este caso el rol **admin**.\
Para hacerlo:

1. Modificamos el campo **kid** del JWT para que apunte al archivo que descargamos: `/var/www/html/`.
2. Cambiamos el campo **rol** de **user** a **admin**.
3. Firmamos el token con la **clave secreta que obtuvimos al principio**.

<figure><img src="/files/eh9iD0IQ4Wc02NY7ybEf" alt=""><figcaption></figcaption></figure>

Cuando ejecutamos un comando en la web, observamos que en las peticiones interceptadas con **Burp Suite** hay una cabecera **Authorization**, que contiene el JWT original asociado al rol **user**.

<figure><img src="/files/SjPzXAkqHfnJKAkXmV7o" alt=""><figcaption></figcaption></figure>

Si reemplazamos ese JWT por el que hemos creado con **rol admin**, y ejecutamos el comando que nos permite **leer la flag solicitada**, el servidor nos lo permitirá, porque ahora nos identificamos como **admin**.

<figure><img src="/files/ZbAzKsxM8llmWflCJWVn" alt=""><figcaption></figcaption></figure>

***

## Conclusión

La resolución de **Hammer** demuestra la importancia de una enumeración exhaustiva y de conocer técnicas para eludir medidas de protección como el **Rate Limiting**, que aunque básico, puede ser evitado manipulando cabeceras como **X-Forwarded-For**.

Además, hemos visto cómo la mala implementación de **JWT**, al permitir modificar el campo `kid` y exponer la clave secreta, puede derivar en una **escalada de privilegios crítica**. Esto refuerza la necesidad de proteger claves sensibles y de validar de forma estricta los tokens en entornos de producción.

En conjunto, esta máquina refuerza conceptos esenciales en el hacking web: **fuzzing dirigido, control de cabeceras HTTP, JWT exploitation** y la importancia de auditar tanto el frontend como el backend de una aplicación.
