
CVE-2026-9811 is a Stored Cross-Site Scripting (Stored XSS) vulnerability affecting Mautic 7 (versions 7.0.0 through 7.1.1).
CVE-2026-9811은 Mautic 7(버전 7.0.0~7.1.1)에 영향을 미치는 저장형 크로스 사이트 스크립팅(Stored XSS) 취약점입니다. 이 취약점은 프로젝트 선택기(Project Selector) 구성 요소에 존재하며, AJAX 요청을 통해 반환된 프로젝트 이름이 적절한 출력 인코딩이나 무해화 과정 없이 페이지에 삽입되는 데서 발생합니다. 프로젝트 생성 권한이 있는 인증된 사용자는 프로젝트 이름에 악성 JavaScript 페이로드를 저장할 수 있습니다. 이후 다른 관리자가 프로젝트 선택기가 포함된 엔티티 편집기를 열면 악성 스크립트가 해당 관리자의 브라우저에서 실행됩니다.
속성 값 제품 Mautic 영향받는 버전 7.0.0 – 7.1.1 수정 버전 7.1.2 구성 요소 Project Selector 취약점 유형 Stored Cross-Site Scripting (XSS)
애플리케이션은 인증된 사용자가 입력한 프로젝트 이름을 저장한 후, 나중에 AJAX 엔드포인트를 통해 이를 조회합니다. 프로젝트 선택기를 렌더링할 때 애플리케이션은 이 값을 이스케이프하거나 무해화하지 않은 채 HTML option 요소로 DOM에 직접 삽입합니다.
개념적으로:
User creates project
│
▼
Project name stored in database
│
▼
AJAX returns project name
│
▼
Inserted into DOM without sanitization
│
▼
Browser executes embedded JavaScript
애플리케이션이 저장된 데이터를 신뢰하기 때문에, 다른 권한 있는 사용자가 취약한 페이지를 로드할 때마다 악성 JavaScript가 실행됩니다.
Attacker
│
▼
Creates a project with a malicious JavaScript payload as its name
│
▼
Payload is stored in the database
│
▼
Administrator opens an entity editor
│
▼
AJAX request retrieves project names
│
▼
Application inserts project name into the DOM without encoding
│
▼
Browser executes the malicious script
│
▼
Attacker gains actions within the administrator's session
프로젝트 생성 권한이 있는 공격자는 이름에 악성 JavaScript 페이로드가 포함된 프로젝트를 생성합니다.
이후 관리자가 프로젝트 선택기(Project Selector)가 포함된 다른 엔티티를 편집합니다. 선택기는 AJAX를 통해 프로젝트 이름을 로드하여 페이지에 직접 삽입합니다. 프로젝트 이름이 무해화되지 않았기 때문에 브라우저는 관리자의 권한으로 삽입된 스크립트를 실행합니다.
Mautic에 다음과 같은 양식이 있다고 가정해 보겠습니다:
POST /projects/create HTTP/1.1
Content-Type: application/x-www-form-urlencoded
projectName=Marketing Campaign
description=Email Campaign
프로젝트는 다음과 같이 저장됩니다:
Marketing Campaign
2단계: 공격자가 프로젝트 생성
공격자는 일반적인 이름 대신 무해한 JavaScript 이벤트가 포함된 HTML을 입력합니다:
POST /projects/create HTTP/1.1
Content-Type: application/x-www-form-urlencoded
projectName=<img src="https://raw.githubusercontent.com/aj2108/cve-2026-9811/HEAD/x" onerror="console.log('XSS Demo')">
description=Test
데이터베이스에는 이제 정확히 해당 문자열이 저장됩니다.
3단계: AJAX가 저장된 값 반환
이후 관리자가 프로젝트를 로드하는 페이지를 엽니다.
AJAX 응답:
{
"id": 7,
"projectName": ""
}
4단계: 취약한 JavaScript
애플리케이션이 다음과 같이 동작한다고 가정해 보겠습니다:
const project = response.projectName;
document.getElementById("projects").innerHTML +=
`<option>${project}</option>`;
브라우저는 다음과 같이 해석합니다:
<option>
<img src="https://raw.githubusercontent.com/aj2108/cve-2026-9811/HEAD/x" onerror="console.log('XSS Demo')">
</option>
애플리케이션이 문자열을 텍스트가 아닌 HTML로 삽입했기 때문에 브라우저는 이를 실제 요소로 파싱합니다. 이미지가 로드에 실패하면(src="x") onerror 핸들러가 실행됩니다.
취약점을 성공적으로 악용하면 공격자는 다음을 수행할 수 있습니다:
| 지표 | 점수 |
|---|---|
| CVSS v3.1 (CNA) | 5.4 (중간) |
NVD는 아직 이 CVE에 대한 자체 CVSS 평가를 게시하지 않았습니다.
참고: 벤더는 정확한 취약 소스 코드를 공개하지 않았습니다. 다음 예시는 취약점 패턴만을 보여줍니다.
// Data returned from AJAX
const project = response.projectName;
// Unsafe: inserted directly into the DOM
document.getElementById("projectSelector").innerHTML +=
`<option>${project}</option>`;
프로젝트 이름은 사용자 입력에서 비롯되지만 HTML 인코딩 없이 페이지에 삽입됩니다. 저장된 값에 HTML 또는 JavaScript가 포함되어 있으면 브라우저는 이를 일반 텍스트가 아닌 실행 가능한 콘텐츠로 해석합니다.
const option = document.createElement("option");
// Safe: assign as text, not HTML
option.textContent = response.projectName;
document.getElementById("projectSelector").appendChild(option);
또는 HTML 출력을 피할 수 없다면 삽입 전에 적절한 출력 인코딩을 적용해야 합니다.
textContent(또는 이에 상응하는 안전한 API)를 사용하면 특수 문자가 실행 가능한 HTML 또는 JavaScript가 아닌 일반 텍스트로 처리됩니다. 악성 프로젝트 이름이 데이터베이스에 저장되어 있더라도 실행되지 않고 문자 그대로 표시됩니다.