vsftpd 3.0.5 - Élevé - À distance - Reliquat de privilèges - Les données FTPS acceptent tout ticket repris
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · vsftpd-ssl-data-any-ticket
Classe : Reliquat de privilège Portée : Distante
vsftpd 3.0.5 (Chris Evans) - archive officielle SHA-256 26b602ae454b0ba6d99ef44a09b6b9e0dfa7f67228106736df1f278c70bc91d3. Les compilations FTPS des distributions définissent #define VSF_BUILD_SSL (l'archive standard #undef) et les opérateurs positionnent ssl_enable=YES. Le mode autonome listen=YES est la valeur par défaut compilée et celle de l'exemple.
ssl_accept (ssl.c:494-501) est le point de liaison du canal de données. Il vérifie uniquement SSL_session_reused(). Il n'y a aucune comparaison de SSL_SESSION avec le SSL de contrôle. Le SSL de données n'a pas de USER/PASS FTP. En mode autonome, ssl_init s'exécute dans l'écouteur avant le fork (main.c:147-150), donc les processus enfants héritent d'un seul SSL_CTX. SSL_CTX_set_timeout(INT_MAX) (ssl.c:182-186). Pas de SSL_OP_NO_TICKET. SSL_session_reused()==1 signifie que ce ClientHello transportait un ticket que ce processus peut déchiffrer, et non qu'il s'agit de la session de contrôle d'Alice.
La page de manuel de require_ssl_reuse indique que la réutilisation prouve le même secret maître que le canal de contrôle. Le code C ne le fait pas. À distinguer du correctif Tim Kosse de la 3.0.3 qui tue en cas d'échec de réutilisation (ftpdataio.c:184-187), lequel renvoie toujours un 522 sur une nouvelle poignée de main et n'empêche pas Mallory de reprendre un ticket émis sur un autre contrôle.
Ce reliquat correspond à un seul transfert FTPS. Ce n'est pas un contournement de USER/PASS, ni un shell, ni une évasion de chroot.
| ID | pas encore de CVE |
| Classe | Reliquat de privilège (les données FTPS acceptent tout ticket repris) |
| Portée | Distante (PASV même IP ; NAT partagé est AC:H) |
| CWE | CWE-345, CWE-287 |
| CVSS | Élevé : 7.4 CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N |
| Produit | vsftpd (Chris Evans) |
| Affecté | 3.0.5 avec VSF_BUILD_SSL et ssl_enable=YES. require_ssl_reuse=YES (par défaut). require_cert=NO (par défaut) |
| Auth | Mallory : AUTH TLS uniquement (pas de USER/PASS). Alice : PBSZ/PROT P anonyme |
| Licence | GNU Affero GPL v3.0 |
| Lab | 127.0.0.1:18700 contrôle, PASV 18701-18708. Compose vsftpd-ssl-data-any-ticket |
Qui : un autre client FTPS (ou un attaquant sur le chemin) qui partage l'IP source d'Alice. L'accept PASV exige toujours l'IP du pair de contrôle (privops.c:212-218) sauf si pasv_promiscuous. Le NAT partagé est le cas FTPS habituel que require_ssl_reuse a été ajouté pour bloquer. Mallory ne s'authentifie jamais.
| Méthode + cible | Privilège |
|---|---|
Mallory AUTH TLS uniquement, TLS 1.2 ALPN ftp, conserve le ticket de session du SSL_CTX de l'écouteur. Alice anon PBSZ/PROT P, PASV, RETR secret.txt bloqué dans get_remote_transfer_fd jusqu'à l'accept. Mallory PASV même IP, reprend son ticket (session_reused=True) | Déchiffre ce seul RETR. Labo : socket de données 35 octets VSFTPD-SSL-DATA-ANY-TICKET-WITNESS. Alice 150 puis 226. Le TLS de contrôle et le mot de passe restent ceux d'Alice. Aucun parcours de répertoire au-delà de ce transfert. |
ssl_accept ; le labo était un vol de RETR)| Méthode + cible | Privilège |
|---|---|
Même reprise de ticket pendant un STOR / APPE d'Alice | Le fichier nommé d'Alice est écrit depuis le texte en clair TLS de Mallory, avec l'uid d'Alice / l'uid ftp anonyme, à l'intérieur du chroot existant |
| Méthode + cible | Privilège |
|---|---|
Nouvelle poignée de main de données (reused=False) | 522 session reuse required (kill 3.0.3). Ce transfert meurt. Le contrôle reste celui d'Alice |
privops.c:212-218)require_cert=YES plus un certificat présenté lie les empreintes SHA-256PROT C en clair pour les utilisateurs locaux (force_local_data_ssl par défaut YES)ssl_init de l'écouteur)ftp (vsftpd FATAL)J'ai épinglé l'archive officielle vsftpd 3.0.5 plutôt qu'un miroir GitHub ou une arborescence Debian/Fedora. Dix-neuf recherches dans le code source. La recherche SSL s'est arrêtée sur ssl_accept : la réutilisation est un booléen, pas une liaison à ce SSL_SESSION de contrôle. Le SSL_CTX de l'écouteur plus la durée de vie de ticket INT_MAX constituent le reste.
Fausses pistes : l'ALPN doit être ftp sinon vsftpd FATAL (le reliquat ALPACA de la 3.0.4 est déjà fermé). L'édition de liens Arm64 nécessitait explicitement -lpam -lcap ; le vsf_findlibs.sh standard ne sonde que les chemins x86_64. Considérer le kill Tim Kosse de la 3.0.3 comme ce bug : il renvoie 522 sur une nouvelle poignée de main sur le fd de données d'Alice et n'empêche pas un ticket émis sur un autre contrôle.
cd lab
./run.sh
3.0.5 officiel avec #define VSF_BUILD_SSL. Conf : ssl_enable=YES, allow_anon_ssl=YES, force_anon_logins_ssl=YES, force_anon_data_ssl=YES, require_ssl_reuse=YES, require_cert=NO. Docker loopback. Deux clients de même IP. Tickets TLS 1.2. ALPN ftp. Contrôle 127.0.0.1:18700, PASV 18701-18708. Compose vsftpd-ssl-data-any-ticket. Témoin VSFTPD-SSL-DATA-ANY-TICKET-WITNESS. Dernière ligne :
SUCCESS vsftpd-ssl-data-any-ticket reused-ticket=yes fresh-handshake=522 VSFTPD-SSL-DATA-ANY-TICKET-WITNESS
Lier le SSL_SESSION de données au SSL_SESSION de contrôle (comparer les identifiants de session / le secret maître). Limiter la durée de vie du ticket à celle de la connexion de contrôle. Ou SSL_OP_NO_TICKET plus un cache de sessions indexé par le contrôle. ProFTPD 1.3.9d compare les sessions.
Le canal de réception du fournisseur est [email protected] (security.appspot.com/vsftpd.html). N'ouvrez pas d'issue GitHub publique sur un miroir vsftpd.
Abraxas Labs - abraxaslabs.tech - github.com/abraxas - @abraxas_null - [email protected]