
Laboratorio Docker reproducible + exploit de socket crudo para CVE-2015-3306 (copia arbitraria de archivos pre-autenticación en mod_copy de ProFTPD) — un ejercicio de aprendizaje de parcheo por diffs
Un laboratorio totalmente reproducible y un exploit escrito a mano para CVE-2015-3306 — construido como ejercicio de aprendizaje en investigación de vulnerabilidades: análisis de parches, disparo manual y desarrollo de exploits con sockets crudos.
El módulo mod_copy de ProFTPD implementa el par de comandos SITE CPFR / SITE CPTO,
que copia archivos en el lado del servidor sin
transferir datos al cliente.
En la versión 1.3.5, ninguno de los dos manejadores comprobaba si la sesión estaba autenticada. El módulo simplemente asumía "nadie enviará comandos SITE antes de iniciar sesión" — una suposición rota que se convirtió en un CVE. Cualquier cliente no autenticado podía copiar cualquier archivo legible por el proceso del servidor a cualquier ubicación escribible por él.
La corrección (commit 212d54271f, publicada en 1.3.5a) hace dos cosas:
copy_cpfrcopy_cptoauthenticated = get_param_ptr(cmd->server->conf, "authenticated", FALSE);
if (authenticated == NULL || *authenticated == FALSE) {
pr_response_add_err(R_530, _("Please login with USER and PASS"));
errno = EPERM;
return PR_ERROR(cmd);
}
CopyEngine on|off — anteriormente el módulo ni siquiera podía
desactivarse en compilaciones que lo incluían.El diff unificado completo está en patch.diff. Leer parches es
la habilidad: la corrección te dice dónde estaba la herida.
El Dockerfile compila ProFTPD 1.3.5 (la última versión vulnerable) desde
el tarball oficial de fuentes con mod_copy habilitado:
docker build -t proftpd-135 .
docker run -d --name lab135 -p 127.0.0.1:2121:21 -p 127.0.0.1:30000-30010:30000-30010 proftpd-135
docker exec lab135 chmod 777 /home/ftp
PassivePorts en proftpd.conf fija el canal de datos — la arquitectura de dos canales
de FTP (control + datos dinámicos) es la razón por la que los mapeos ingenuos de
contenedores fallan: el puerto de control funciona, el puerto de datos no.
exploit.py usa sockets crudos — nada de ftplib, porque las librerías ocultan el
protocolo, y ocultar el protocolo es exactamente contra lo que estamos luchando:
SITE CPFR /etc/segredo.txt -> 350 (no se envía USER/PASS: esto ES el bug)
SITE CPTO /home/ftp/... -> 250 (copia arbitraria realizada)
USER ftp / PASS ... -> 230 (el inicio de sesión es solo la vía de exfiltración)
PASV / RETR -> 150 -> 226 (bandera capturada)
DELE -> 250 (limpieza: sin IOC dejado atrás)
AllowOverwrite, mapeos de puertos) decide el impacto final.time.time_ns(), no time.time()), y sin depender
de estado residual.RETR responde dos veces: 150,
luego 226. Drena ambas antes del siguiente comando.-d10) te dice exactamente
qué comprobación se disparó.Solo para fines educativos y uso autorizado en laboratorio. Ejecutar esto contra sistemas que no posees o para los que no tienes permiso por escrito para probar es ilegal.