
JoomGallery의 깨진 객체 수준 권한 부여를 통한 IDOR + Stored XSS
JoomGallery ≤ 4.3.0 — 편집자(Editor) 역할 사용자가 모든 갤러리 이미지를 탈취하고 XSS 페이로드를 저장하여 관리자 세션 탈취 가능
JoomGallery의 UserimageController::save()는 checkACL('edit.own', ...) 대신 checkACL('edit', ...)를 확인합니다. 편집자(Editor) 역할 사용자는 소유권과 관계없이 모든 이미지에 대해 task=userimage.save&id=N으로 POST할 수 있습니다(IDOR — CWE-639). 편집자 역할은 전역적으로 core.edit 권한을 보유하므로, 관리자가 소유한 이미지를 포함한 사이트의 모든 이미지 ID에 대해 권한 부여 검사가 통과됩니다.
프론트엔드 이미지 템플릿에서 $this->escape() 호출이 누락된 것과 결합되어, 편집자는 관리자 소유 이미지를 포함한 모든 이미지 제목에 XSS 페이로드를 저장할 수 있으며, 이로 인해 모든 방문자의 브라우저에서 JavaScript가 실행됩니다. 이를 통해 완전한 관리자 세션 탈취와 사이트 전체 장악이 가능합니다.
| 구성 요소 | 취약 버전 | 테스트 환경 | 수정 버전 |
|---|---|---|---|
| JoomGallery (com_joomgallery) | 4.0.0 – 4.3.0 | Joomla 5.4.7 + JoomGallery 4.3.0-stable (PHP 8.2 / Apache) | 4.4.0 |
유형: 저장형 크로스 사이트 스크립팅(CWE-79)과 연계된 객체 수준 권한 부여 결함 / IDOR (CWE-639) 필요한 인증: 낮은 권한의 편집자(Editor) 계정
파일: components/com_joomgallery/src/Controller/UserimageController.php
save() 작업은 edit.own 대신 edit 권한을 사용하여 ACL 검사를 수행합니다. edit 권한은 모든 편집자 역할 사용자에게 전역적으로 부여되므로, 작성자가 누구인지와 관계없이 모든 이미지 ID에 대해 검사가 통과됩니다.
USERIMAGECONTROLLER.PHP — 취약 코드 (145행)
// Vulnerable
if (!$this->checkACL('edit', 'image', $recordId, $parent_id, true)) { ... }
core.edit 권한은 편집자 그룹이 전역적으로 보유하므로 조건은 모든 이미지 ID에 대해 false로 평가되어 제한 없는 쓰기 액세스를 부여합니다. 저장에 성공하면 모델은 추가로 created_by를 공격자의 사용자 ID로 업데이트하여 이미지의 소유권을 공격자에게 조용히 이전합니다.
파일: components/com_joomgallery/tmpl/image/default.php
프론트엔드 이미지 템플릿은 $this->item->title을 alt 속성 컨텍스트로 HTML 인코딩하지 않고 출력합니다. Joomla의 JInput STRING 필터는 큰따옴표 문자를 제거하지 않으므로 "를 포함한 페이로드가 속성을 빠져나와 임의의 이벤트 핸들러를 주입할 수 있습니다.
DEFAULT.PHP — 취약 코드 (64행, 78행)
// Vulnerable
item->title; ?>" ...>
페이로드 abc" onmouseover="alert(document.domain);" x="는 jos_joomgallery.title에 저장되며, 페이지가 렌더링될 때마다 HTML 속성에 원시 상태로 주입됩니다. 저장 계층이나 표시 계층에서 어떠한 삭제(sanitization)도 수행되지 않습니다.
jform[title]에 XSS 페이로드를 담아 task=userimage.save&id=3으로 POST합니다. ACL 검사가 통과됩니다(edit.own이 아닌 core.edit). 서버는 HTTP 403이 아닌 303을 반환합니다.created_by가 공격자의 사용자 ID로 이전됩니다.jos_joomgallery.title에 저장됩니다.alt="abc" onmouseover="alert(document.domain);"를 렌더링합니다. XSS가 실행됩니다. 관리자 세션이 탈취되어 → 사이트 전체가 장악됩니다.관리자가 JoomGallery 백엔드(Joomla 5.4.7)를 통해 admin_image를 생성합니다. 이미지는 게시 및 승인 상태이며 관리자가 소유합니다.

GET /index.php/component/users/login — 응답 JSON에 "csrf.token":"a68c2b3a..."가 포함되어 있습니다. 후속 로그인 POST를 위해 토큰을 캡처합니다.

joomla_user_state=logged_inCSRF 토큰과 편집자 자격 증명으로 POST /index.php/component/users/login을 수행합니다. 응답: HTTP 303 및 Set-Cookie: joomla_user_state=logged_in. 세션 쿠키를 캡처합니다.

세션 쿠키로 GET /index.php?option=com_joomgallery를 수행합니다. 응답에는 저장 요청에 사용할 새 "csrf.token":"2d96934b..."가 포함되어 있습니다.

편집자가 jform[title]을 다음과 같이 설정하여 option=com_joomgallery&task=userimage.save&id=3으로 POST합니다:
abc" onmouseover="alert(document.domain);" x="
서버가 HTTP 403이 아닌 303을 반환하여 IDOR을 확인합니다. Location 헤더의 리디렉션 URL에 XSS 페이로드가 표시되어 제목이 수락 및 저장되었음을 확인합니다.

JoomGallery 백엔드에 이미지 ID=3의 소유자가 이제 Editor User로 표시됩니다. 승인되지 않은 저장 중에 데이터베이스의 created_by 필드가 조용히 업데이트되었습니다.

jos_joomgallery에 대한 SQL 쿼리는 XSS 페이로드가 저장되었음을 확인합니다 — "가 "가 아닌 원시 큰따옴표로 저장됩니다. 저장 계층에서 어떠한 삭제(sanitization)도 수행되지 않았습니다.

/index.php/component/joomgallery/gallery를 방문하는 모든 사용자가 페이로드를 트리거합니다. 브라우저의 alert() 대화상자가 피해자 오리진(document.domain)에서 JavaScript 실행을 확인합니다.

created_by를 공격자에게 조용히 재할당하여 감사 추적(audit trail)을 영구적으로 변경합니다.alt 속성의 악성 스크립트가 관리자의 세션 쿠키를 유출하여 공격자에게 전체 백엔드 액세스와 Joomla 설치 전체에 대한 제어 권한을 부여할 수 있습니다.S:C)에서 실행되며, 공격자의 낮은 권한 세션과 피해자의 높은 권한 세션 사이의 신뢰 경계를 넘어섭니다.