vsftpd 3.0.5 - Alta - Remota - Privilegio residual - brace deny_file omite .private/secret
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · vsftpd-deny-file-brace-child
Clase: Privilegio residual Alcance: Remoto
vsftpd 3.0.5 - Chris Evans
El ejemplo de la página de manual (vsftpd.conf.5) es deny_file={*.mp3,*.mov,.private}. Los nombres coincidentes "no deberían ser accesibles de ninguna forma", incluyendo "descarga, cambio a directorio, afectar algo dentro del directorio". vsf_access_check_file (access.c:16-43) llama a vsf_filename_passes_filter sobre la ruta del cliente tal como se proporciona, luego str_locate_str del tunable de llaves completo. El filtro coincide con el último componente / prefijo; el residuo /secret falla; la subcadena de {*.mp3,*.mov,.private} no encuentra nada.
CWD .private es denegado (el basename coincide). RETR .private/secret y RETR dir/.private tienen éxito. Ese "dentro del directorio" es lo que este residuo rompe.
CVE-2015-1419 es un parche público de distro para realpath/basename. El 3.0.5 oficial nunca lo adoptó. La rama de llaves de Debian todavía coincide con la ruta completa, por lo que este recorrido hijo permanece también en paquetes de distro parcheados. No volver a reportar 2015-1419.
Este residuo es un fallo de lista de denegación condicionado por configuración. No es RCE no autenticado, no es un escape de chroot, y no es una lectura del /etc del host.
| ID | sin CVE aún |
| Clase | Privilegio residual |
| Alcance | Remoto (FTP autenticado) |
| CWE | CWE-863, CWE-22 |
| CVSS | Alto: 7.5 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N |
| Producto | vsftpd (Chris Evans) |
| Afectado | vsftpd 3.0.5 tarball oficial SHA-256 26b602ae454b0ba6d99ef44a09b6b9e0dfa7f67228106736df1f278c70bc91d3 |
| Autenticación | FTP autenticado USER/PASS (anónimo en el laboratorio). deny_file establecido a una lista de llaves (por defecto sin establecer) |
| Licencia | GNU Affero GPL v3.0 |
| Laboratorio | solo 127.0.0.1. Anónimo de solo lectura. Archivo testigo. |
Quién: un usuario FTP autenticado (anónimo ftp en el laboratorio). UID de sesión tras la caída habitual de vsftpd. deny_file es la política extra que se suponía debía quitar capacidades a ese UID. Las recetas de usuario virtual que menciona la página de manual comparten guest_username, por lo que el DAC de Unix no las separa. El 3.0.5 de fábrica con tunable_deny_file NULL no es este residuo.
| Método + sink | Privilegio |
|---|---|
RETR .private/secret con la llave documentada deny_file={*.mp3,*.mov,.private} | handle_retr str_open de un archivo cuyo nombre de directorio está en la lista de denegación, como el uid de sesión. Laboratorio: 150/226, 37 bytes, cuerpo VSFTPD-DENY-FILE-BRACE-CHILD-WITNESS |
RETR dir/.private | mismo sink. Un archivo llamado .private bajo otro directorio (must_match_at_current_pos rechaza una coincidencia en índice > 0). Laboratorio: 150/226, cuerpo DIRDOT-VSFTPD-DENY-FILE-BRACE-CHILD-WITNESS |
vsf_access_check_file; el laboratorio era de solo lectura)| Método + sink | Privilegio |
|---|---|
STOR / APPE la misma ruta cuando write_enable=YES | handle_upload_common crea / sobrescribe como el uid de sesión |
DELE / RNTO la misma ruta | str_unlink / renombrado de un nombre que la lista de denegación debía detener |
| Método + sink | Privilegio |
|---|---|
LIST .private/x (o LIST dentro del árbol denegado) | handle_dir_common verifica el acceso del argumento completo (falla), luego str_opendir del prefijo .private tras la división por barra. Solo nombres. El DAC de Unix sigue aplicándose |
| Método + sink | Resultado |
|---|---|
CWD .private | 550 Permission denied. El basename .private coincide con la llave |
RETR song.mp3 | 550. *.mp3 es un patrón de último componente y sí coincide |
CWD .private en sí mismo (el basename coincide)hide_file (basename desde readdir; el RETR de un nombre oculto es intencional según la documentación)/etc del host desde un chroothttp_enable por defecto desactivado; GET en modo FTP mata la sesión)deny_file=.private sin llaves se salva por el fallback de subcadena (str_locate_str de .private en .private/secret)Fijé el vsftpd oficial 3.0.5 (SHA-256 26b602ae...c91d3) en lugar de un espejo de GitHub o un árbol de Debian/Fedora. Diecinueve búsquedas en el código fuente. El demonio compilado por defecto no tiene residuos Críticos/Altos no publicados. Este requiere que el operador copie el ejemplo de llaves de la página de manual.
vsf_filename_passes_filter (ls.c) coincide con * / ? / {,} no anidados y solo hace trabajo útil en un patrón de último componente. Para el nombre .private/secret contra {*.mp3,*.mov,.private}:
*.mp3, *.mov, .private se comparan al inicio de .private/secret. El residuo /secret falla a menos que el token fuera *.{*.mp3,*.mov,.private} y no encuentra nada.Camino equivocado ya público: CVE-2015-1419 es solo realpath de distro. El Changelog oficial no tiene tal corrección. Incluso la rama de llaves de Debian copia la ruta normalizada completa, por lo que los hijos todavía se recorren.
cd lab
./run.sh
Control en loopback 127.0.0.1:18710, PASV 18711-18718. Proyecto Compose vsftpd-deny-file-brace-child. Tarball oficial 3.0.5, anónimo de solo lectura, deny_file={*.mp3,*.mov,.private}. Testigo VSFTPD-DENY-FILE-BRACE-CHILD-WITNESS. Última línea:
SUCCESS vsftpd-deny-file-brace-child cwd-dotprivate=550 retr-child=yes retr-dir-dotprivate=yes mp3=550 VSFTPD-DENY-FILE-BRACE-CHILD-WITNESS
Comparar las alternativas de llaves contra cada componente de la ruta, o realpath / basename antes del filtro (con las advertencias habituales sobre enlaces simbólicos). El ejemplo documentado debería realmente denegar los hijos.
La recepción del proveedor es [email protected] (vsftpd). No abrir un issue público en GitHub en ningún espejo de vsftpd.
Abraxas Labs - abraxaslabs.tech - github.com/abraxas - @abraxas_null - [email protected]