
Joomla 확장 프로그램 PhocaCommander는 getSource 함수에서 Path Traversal 취약점이 있습니다.
PhocaCommander ≤ 6.1.3 — 인증된 관리자 경로 탐색, CSRF 불필요
PhocaCommander의 파일 편집기 엔드포인트에 임의 파일 읽기 취약점이 존재합니다. getSource() 함수는 base64로 인코딩된 filename 매개변수를 디코딩하여 경로 포함 검사 없이 file_get_contents()에 직접 전달합니다. 인증된 관리자는 GET 요청 하나만으로 웹 서버 프로세스가 접근할 수 있는 모든 파일(웹 루트 외부의 파일 포함)을 읽을 수 있습니다. CSRF 토큰은 필요하지 않습니다.
이 취약점을 악용하면 configuration.php에서 Joomla의 데이터베이스 자격 증명과 비밀 키가 유출되며, 경로 탐색 시퀀스(예: ../../../../etc/passwd)를 통해 /etc/passwd와 같은 시스템 파일도 읽을 수 있습니다.
| 구성 요소 | 취약 버전 | 테스트 환경 | 수정 버전 |
|---|---|---|---|
| PhocaCommander | 1.0.0 – 6.1.3 | Joomla 5.4.7 + PhocaCommander 6.1.3 (PHP 8.2 / Apache) | 6.1.4 |
필요한 인증: 관리자 세션 (CSRF 토큰 불필요 - GET 요청)
파일: administrator/components/com_phocacommander/models/phocacommanderedit.php
getSource()는 filename을 base64에서 디코딩하여 JPATH_ROOT에 직접 연결한 다음, 포함 검사 없이 파일을 읽습니다:
PHOCACOMMANDEREDIT.PHP — 취약 코드
public function getSource($fileName) {
$fileName = base64_decode($fileName); // ← attacker-controlled
$item = new stdClass;
if (PhocaCommanderHelper::fileExists(JPATH_ROOT . '/' . $fileName)) {
$item->source = file_get_contents(JPATH_ROOT . '/' . $fileName); // ← no containment check
}
return $item;
}
fileExists()는 is_file(Path::clean($file))을 사용합니다. Path::clean()은 슬래시를 정규화하지만 ../ 시퀀스를 제거하지 않습니다. 따라서 경로 탐색이 검사 없이 웹 루트를 벗어날 수 있습니다.
코드베이스에는 helpers/phocacommander.php에 realpath() 기반의 적절한 포함 검사를 수행하는 getContainedRealPath() 함수가 있습니다. 이 함수는 save()와 download()에서 호출되지만 getSource()에는 추가된 적이 없습니다. 또한 컨트롤러의 edit() 메서드는 GET을 통해 디스패치되므로 표준 CSRF 토큰 검사가 적용되지 않습니다. 활성 관리자 세션 쿠키만으로도 임의 파일 읽기를 트리거할 수 있습니다.
관리자 로그인 페이지를 통해 인증하여 유효한 세션 쿠키를 획득합니다. 표준 관리자 세션 외에 추가 권한은 필요하지 않습니다.
base64로 인코딩된 탐색 경로와 함께 다음 GET 요청을 전송합니다:
GET /administrator/index.php?option=com_phocacommander&task=phocacommanderedit.edit&filename=Li4vLi4vLi4vLi4vZXRjL3Bhc3N3ZA==
Cookie: [admin session]
filename 디코딩 결과: Li4vLi4vLi4vLi4vZXRjL3Bhc3N3ZA== → ../../../../etc/passwd
최종 경로: /var/www/html/../../../../etc/passwd = /etc/passwd
편집기가 /etc/passwd의 전체 내용을 렌더링하므로 웹 루트 외부에 대한 읽기 접근이 가능함을 확인할 수 있습니다.
요청 / 응답
