vsftpd 3.0.5 — высокий — удалённый — остаточные привилегии — FTPS-данные принимают любой возобновлённый тикет
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · vsftpd-ssl-data-any-ticket
Класс: Остаточная привилегия Достижимость: Удалённая
vsftpd 3.0.5 (Chris Evans) — официальный tarball SHA-256 26b602ae454b0ba6d99ef44a09b6b9e0dfa7f67228106736df1f278c70bc91d3. Дистрибутивные сборки FTPS компилируют #define VSF_BUILD_SSL (в стоковом tarball #undef), а операторы устанавливают ssl_enable=YES. Standalone listen=YES — это скомпилированное и примерное значение по умолчанию.
ssl_accept (ssl.c:494-501) — это привязка канала данных. Он проверяет только SSL_session_reused(). Сравнения SSL_SESSION с управляющим SSL нет. У SSL данных нет FTP USER/PASS. Standalone ssl_init выполняется в слушателе до fork (main.c:147-150), поэтому дочерние процессы наследуют один SSL_CTX. SSL_CTX_set_timeout(INT_MAX) (ssl.c:182-186). Нет SSL_OP_NO_TICKET. SSL_session_reused()==1 означает, что этот ClientHello нёс ticket, который данный процесс может расшифровать, а не то, что это управляющая сессия Alice.
В man-странице для require_ssl_reuse сказано, что повторное использование доказывает тот же master secret, что и управляющий канал. В C-коде это не так. Отличается от kill-on-reuse-fail в 3.0.3 от Tim Kosse (ftpdataio.c:184-187), который всё равно отвечает 522 на свежий handshake и не мешает Mallory возобновить ticket, выпущенный для другого управляющего соединения.
Эта остаточная привилегия — одна FTPS-передача. Это не пропуск USER/PASS, не shell и не побег из chroot.
| ID | пока нет CVE |
| Класс | Остаточная привилегия (FTPS-данные принимают любой возобновлённый ticket) |
| Достижимость | Удалённая (PASV с того же IP; общий NAT — это AC:H) |
| CWE | CWE-345, CWE-287 |
| CVSS | High: 7.4 CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N |
| Продукт | vsftpd (Chris Evans) |
| Затронуто | 3.0.5 с VSF_BUILD_SSL и ssl_enable=YES. require_ssl_reuse=YES (по умолчанию). require_cert=NO (по умолчанию) |
| Аутентификация | Mallory: только AUTH TLS (без USER/PASS). Alice: анонимный PBSZ/PROT P |
| Лицензия | GNU Affero GPL v3.0 |
| Лаборатория | 127.0.0.1:18700 управляющий, PASV 18701-18708. Compose vsftpd-ssl-data-any-ticket |
Кто: другой FTPS-клиент (или on-path), разделяющий исходный IP Alice. PASV accept всё равно требует IP управляющего peer (privops.c:212-218), если не pasv_promiscuous. Общий NAT — обычный случай FTPS, для которого и был добавлен require_ssl_reuse. Mallory никогда не логинится.
| Метод + точка сброса | Привилегия |
|---|---|
Mallory только AUTH TLS, TLS 1.2 ALPN ftp, сохраняет session ticket из слушателя SSL_CTX. Alice анонимно PBSZ/PROT P, PASV, RETR secret.txt блокируется в get_remote_transfer_fd до accept. Mallory PASV с того же IP, возобновляет свой ticket (session_reused=True) | Расшифровывает этот один RETR. Лаборатория: сокет данных 35 байт VSFTPD-SSL-DATA-ANY-TICKET-WITNESS. У Alice 150, затем 226. Управляющий TLS и пароль остаются у Alice. Никакого обхода каталогов за пределами этой передачи. |
ssl_accept; в лаборатории был перехват RETR)| Метод + точка сброса | Привилегия |
|---|---|
Возобновление того же ticket, пока Alice выполняет STOR / APPE | Именованный файл Alice записывается из открытого текста TLS Mallory, от имени uid Alice / анонимного ftp uid, внутри существующего chroot |
| Метод + точка сброса | Привилегия |
|---|---|
Свежий handshake данных (reused=False) | 522 session reuse required (kill из 3.0.3). Эта передача умирает. Управляющее соединение остаётся у Alice |
privops.c:212-218)require_cert=YES плюс предъявленный сертификат привязывает SHA-256 дайджестыPROT C для локальных пользователей (force_local_data_ssl по умолчанию YES)ssl_init слушателя)ftp (vsftpd завершается FATAL)Я зафиксировал официальный tarball vsftpd 3.0.5, а не зеркало на GitHub или дерево Debian/Fedora. Девятнадцать поисков по исходникам. Поиск по SSL остановился на ssl_accept: повторное использование — это булево значение, а не привязка к этому управляющему SSL_SESSION. Слушатель SSL_CTX плюс время жизни ticket INT_MAX — это всё остальное.
Ошибочные пути: ALPN должен быть ftp, иначе vsftpd завершается FATAL (остаточная привилегия ALPACA из 3.0.4 уже закрыта). Для линковки на Arm64 потребовались явные -lpam -lcap; стоковый vsf_findlibs.sh проверяет только пути x86_64. Трактовать kill из 3.0.3 от Tim Kosse как этот баг: он отвечает 522 на свежий handshake на fd данных Alice и не останавливает ticket, выпущенный для другого управляющего соединения.
cd lab
./run.sh
Официальный 3.0.5 с #define VSF_BUILD_SSL. Конфигурация: 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. Два клиента с одного IP. TLS 1.2 tickets. ALPN ftp. Управляющий 127.0.0.1:18700, PASV 18701-18708. Compose vsftpd-ssl-data-any-ticket. Свидетель VSFTPD-SSL-DATA-ANY-TICKET-WITNESS. Последняя строка:
SUCCESS vsftpd-ssl-data-any-ticket reused-ticket=yes fresh-handshake=522 VSFTPD-SSL-DATA-ANY-TICKET-WITNESS
Привязать SSL_SESSION данных к управляющему SSL_SESSION (сравнивать session ids / master secret). Ограничить время жизни ticket временем управляющего соединения. Либо SSL_OP_NO_TICKET плюс кэш сессий с ключом по управляющему соединению. ProFTPD 1.3.9d сравнивает сессии.
Приём сообщений от вендора — [email protected] (security.appspot.com/vsftpd.html). Не открывайте публичный issue на GitHub в любом зеркале vsftpd.
Abraxas Labs - abraxaslabs.tech - github.com/abraxas - @abraxas_null - [email protected]