vsftpd 3.0.5 - 높음 - 원격 - 잔여 권한 - 중괄호 deny_file이 .private/secret 누락
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · vsftpd-deny-file-brace-child
분류: 권한 잔여 도달 범위: 원격
vsftpd 3.0.5 - Chris Evans
매뉴얼 페이지(vsftpd.conf.5)의 예시는 deny_file={*.mp3,*.mov,.private}이다. 일치하는 이름은 "어떤 방식으로도 접근할 수 없어야" 하며, 여기에는 "다운로드, 디렉터리로 이동, 디렉터리 내부의 무언가에 영향 주기"가 포함된다. vsf_access_check_file(access.c:16-43)은 주어진 클라이언트 경로에 대해 vsf_filename_passes_filter를 호출한 다음, 전체 중괄호 튜너블에 대해 str_locate_str을 호출한다. 필터는 마지막 구성 요소 / 접두사를 일치시킨다. 남은 /secret은 실패하고, {*.mp3,*.mov,.private}의 부분 문자열은 일치하지 않는다.
CWD .private은 거부된다(베이스네임이 일치). RETR .private/secret과 RETR dir/.private은 성공한다. 바로 이 "디렉터리 내부"가 이 잔여 취약점이 깨뜨리는 부분이다.
CVE-2015-1419는 공개된 배포판의 realpath/basename 패치이다. 공식 3.0.5는 이를 받아들이지 않았다. Debian의 중괄호 분기는 여전히 전체 경로를 일치시키므로, 이 자식 경로 탐색은 패치된 배포판 패키지에서도 남아 있다. 2015-1419를 다시 신고하지 마라.
이 잔여 취약점은 설정으로 제한된 deny-list 누락이다. 인증 없는 RCE도, chroot 탈출도, 호스트 /etc 읽기도 아니다.
| ID | 아직 CVE 없음 |
| 분류 | 권한 잔여 |
| 도달 범위 | 원격 (인증된 FTP) |
| CWE | CWE-863, CWE-22 |
| CVSS | 높음: 7.5 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N |
| 제품 | vsftpd (Chris Evans) |
| 영향받음 | vsftpd 3.0.5 공식 tarball SHA-256 26b602ae454b0ba6d99ef44a09b6b9e0dfa7f67228106736df1f278c70bc91d3 |
| 인증 | 인증된 FTP USER/PASS (실습 환경에서는 anonymous). deny_file이 중괄호 목록으로 설정됨 (기본값은 미설정) |
| 라이선스 | GNU Affero GPL v3.0 |
| 실습 환경 | 127.0.0.1 전용. Anonymous 읽기 전용. Witness 파일. |
누가: 인증된 FTP 사용자 (실습 환경에서는 anonymous ftp). 일반적인 vsftpd 권한 강등 이후의 세션 UID. deny_file은 해당 UID로부터 권한을 빼앗기 위해 마련된 추가 정책이다. 매뉴얼 페이지가 언급하는 가상 사용자 레시피는 guest_username을 공유하므로, Unix DAC가 이들을 분리하지 않는다. tunable_deny_file이 NULL인 기본 3.0.5는 이 잔여 취약점에 해당하지 않는다.
| 메서드 + 싱크 | 권한 |
|---|---|
문서화된 중괄호 deny_file={*.mp3,*.mov,.private}와 함께 RETR .private/secret | 세션 uid로서, 디렉터리 이름이 deny 목록에 있는 파일을 handle_retr str_open으로 연다. 실습: 150/226, 37바이트, 본문 VSFTPD-DENY-FILE-BRACE-CHILD-WITNESS |
RETR dir/.private | 동일한 싱크. 다른 디렉터리 아래에 있는 .private이라는 이름의 파일 (must_match_at_current_pos가 인덱스 > 0에서의 일치를 거부함). 실습: 150/226, 본문 DIRDOT-VSFTPD-DENY-FILE-BRACE-CHILD-WITNESS |
vsf_access_check_file 게이트에서 추론됨; 실습은 읽기 전용이었음)| 메서드 + 싱크 | 권한 |
|---|---|
write_enable=YES일 때 동일한 경로에 STOR / APPE | 세션 uid로서 handle_upload_common 생성 / 덮어쓰기 |
동일한 경로에 DELE / RNTO | denylist가 막으려 했던 이름에 대한 str_unlink / 이름 변경 |
| 메서드 + 싱크 | 권한 |
|---|---|
LIST .private/x (또는 거부된 트리 내부에서 LIST) | handle_dir_common이 전체 인자를 접근 검사하고(누락), 슬래시 분할 후 .private 접두사에 대해 str_opendir을 수행한다. 이름만. Unix DAC는 여전히 적용됨 |
| 메서드 + 싱크 | 결과 |
|---|---|
CWD .private | 550 Permission denied. 베이스네임 .private이 중괄호에 일치 |
RETR song.mp3 | 550. *.mp3는 마지막 구성 요소 패턴이며 실제로 일치함 |
CWD .private 자체 (베이스네임이 일치)hide_file LIST 숨김 (readdir의 베이스네임; 숨겨진 이름의 RETR은 문서화된 의도된 동작)/etchttp_enable 기본값 꺼짐; FTP 모드 GET은 세션을 종료함)deny_file=.private은 부분 문자열 폴백으로 보호됨 (.private/secret 내 .private의 str_locate_str)GitHub 미러나 Debian/Fedora 트리가 아닌 공식 vsftpd 3.0.5 (SHA-256 26b602ae...c91d3)를 고정했다. 열아홉 번의 소스 탐색. 기본 컴파일된 데몬에는 공개되지 않은 Critical/High 잔여 취약점이 없다. 이 취약점은 운영자가 매뉴얼 페이지의 중괄호 예시를 복사해야 한다.
vsf_filename_passes_filter (ls.c)는 * / ? / 중첩되지 않은 {,}를 일치시키며, 마지막 구성 요소 패턴에서만 유용한 작업을 수행한다. 이름 .private/secret을 {*.mp3,*.mov,.private}에 대해 검사할 때:
*.mp3, *.mov, .private는 .private/secret의 시작 부분에서 일치한다. 남은 /secret은 토큰이 *가 아닌 한 실패한다.{*.mp3,*.mov,.private}를 검색하고 일치하지 않는다.이미 공개된 잘못된 방향: CVE-2015-1419는 배포판 전용 realpath이다. 공식 Changelog에는 그러한 수정이 없다. Debian의 중괄호 분기도 전체 정규화된 경로를 복사하므로, 자식은 여전히 탐색된다.
cd lab
./run.sh
루프백 제어 127.0.0.1:18710, PASV 18711-18718. Compose 프로젝트 vsftpd-deny-file-brace-child. 공식 3.0.5 tarball, anonymous 읽기 전용, deny_file={*.mp3,*.mov,.private}. Witness VSFTPD-DENY-FILE-BRACE-CHILD-WITNESS. 마지막 줄:
SUCCESS vsftpd-deny-file-brace-child cwd-dotprivate=550 retr-child=yes retr-dir-dotprivate=yes mp3=550 VSFTPD-DENY-FILE-BRACE-CHILD-WITNESS
중괄호 대안을 모든 경로 구성 요소에 대해 일치시키거나, 필터 전에 realpath / basename을 적용하라 (일반적인 심볼릭 링크 주의 사항과 함께). 문서화된 예시는 실제로 자식을 거부해야 한다.
벤더 접수는 [email protected] (vsftpd). 어떤 vsftpd 미러에도 공개 GitHub 이슈를 열지 마라.
Abraxas Labs - abraxaslabs.tech - github.com/abraxas - @abraxas_null - [email protected]