[CVE-2023-4300] 취약점 분석 보고서
Import XML and RSS Feeds 2.1.4 버전 이하에서 발생하는 file Upload 취약점
Import XML and RSS Feeds 플러그인 취약점 분석 (CVE-2023-4300)
취약점 개요
Import XML and RSS Feeds 플러그인의 2.1.4 버전 이전 버전에서 발견된 취약점은 파일 업로드 기능의 미흡한 확장자 검증으로 인해 발생한다. 공격자가 악성 PHP 파일을 업로드할 수 있어, 이를 통해 원격 코드 실행(RCE)이 가능하다.
- CVSS 점수: 7.2 (높음)
- 취약점 종류: File Upload
- 취약점 영향: Remote Code Execution (RCE)
- 취약 버전: 2.1.3 및 이전
- CVE ID: CVE-2023-4300
패치 분석 (버전 2.1.2 vs 2.1.3)
코드 Diff
다음은 moove_save_import_template
함수의 2.1.2와 2.1.3 버전 간의 변경사항이다.
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
function moove_save_import_template() {
$nonce = isset( $_POST['nonce'] ) ? sanitize_key( wp_unslash( $_POST['nonce'] ) ) : '';
if ( $nonce && wp_verify_nonce( $nonce, 'moove_xml_admin_nonce_field' ) && current_user_can(
- 'edit_posts' ) ) :
+ 'manage_options' ) ) :
if ( $_POST && is_array( $_POST ) ) :
$type = sanitize_text_field( $_POST['type'] );
$extension = sanitize_text_field( $_POST['extension'] );
+ $allowed_extensions = apply_filters('uat_allowed_extenstions', array( 'xml', 'rss' ) );
+ if ( $extension && in_array( strtolower( $extension ), $allowed_extensions ) ) :
$filename = uniqid( strtotime() );
+ $filename = function_exists('uniqid') ? uniqid( strtotime('now') ) : 'uat_tpl_' . strtotime('now');
if ( $type === 'upload' ) :
$xml = wp_unslash( $_POST['file'] );
$filename = $filename . "." . $extension;
$target_dir = dirname( __FILE__ ) . "/uploads/" . $filename;
file_put_contents( $target_dir, $xml, FILE_APPEND | LOCK_EX );
$filename = plugins_url( basename( dirname( __FILE__ ) ) ) . '/uploads/' . $filename;
else :
$filename = sanitize_text_field( $_POST['url'] );
endif;
if ( isset($_POST['form_data'] ) && is_array( $_POST['form_data'] ) ) :
// Create post object
$import_data = array(
'post_title' => wp_strip_all_tags( $_POST['name'] ),
'post_status' => 'publish',
'post_type' => 'moove_feed_importer'
);
// Insert the post into the database
$post_id = wp_insert_post( $import_data );
if ( $post_id ) :
foreach ( $_POST['form_data'] as $field_name => $field_value ) :
add_post_meta( $post_id, 'import_'. $field_name , $field_value );
endforeach;
add_post_meta( $post_id, 'import_xml_url', $filename );
add_post_meta( $post_id, 'import_type', wp_strip_all_tags( $_POST['type'] ) );
add_post_meta( $post_id, 'import_limit', wp_strip_all_tags( $_POST['limit'] ) );
add_post_meta( $post_id, 'import_selected_node', sanitize_text_field( $_POST['selected_node'] ) );
$slug = get_post_field( 'post_name', get_post( $post_id ) );
$update_post = array(
'ID' => $post_id,
'post_title' => $slug
);
wp_update_post( $update_post );
echo json_encode( array( 'success' => 'true', 'message' => 'Post created', 'slug' => $slug, 'template_id' => $post_id ) );
die();
else :
echo json_encode( array( 'success' => 'false', 'message' => 'Post not created!' ) );
die();
endif;
else :
echo json_encode( array( 'success' => 'false', 'message' => 'Form data empty!' ) );
die();
endif;
+ else :
+ echo json_encode( array( 'success' => 'false', 'message' => 'Unsupported extension, please check your feed!' ) );
+ die();
endif;
else :
echo json_encode( array( 'success' => 'false', 'message' => 'POST not set!' ) );
die();
endif;
else :
echo json_encode( array( 'success' => 'false', 'message' => 'Check nonce!' ) );
die();
endif;
}
주요 변경사항
- 사용자 권한 강화
- 사용자 권한이
'edit_posts'
에서'manage_options'
로 변경됐다. 이를 통해 관리자만 해당 기능을 사용할 수 있도록 제한했다.
- 사용자 권한이
- 허용된 확장자 목록 추가
$allowed_extensions
배열을 추가하여xml
과rss
확장자만 허용되도록 했다.apply_filters
함수를 사용해 확장자 목록을 동적으로 필터링할 수 있도록 설계했다.
- 확장자 검증
- 업로드된 파일의 확장자가 허용된 목록에 포함되어 있는지 확인하는 조건문이 추가됐다.
- 허용되지 않은 확장자가 입력될 경우 오류 메시지가 반환된다.
- 파일명 생성 방식 수정
uniqid
함수가 존재하지 않는 경우를 대비해 대체 파일명 생성 방식을 추가했다. 이로 인해 파일명이 항상 고유하도록 보장한다.
코드 분석
1
2
3
4
5
6
7
8
9
if ( $_POST && is_array( $_POST ) ) :
$type = sanitize_text_field( $_POST['type'] );
$extension = sanitize_text_field( $_POST['extension'] );
$filename = uniqid( strtotime() );
if ( $type === 'upload' ) :
$xml = wp_unslash( $_POST['file'] );
$filename = $filename . "." . $extension;
$target_dir = dirname( __FILE__ ) . "/uploads/" . $filename;
file_put_contents( $target_dir, $xml, FILE_APPEND | LOCK_EX );
moove_save_import_template
함수에서 사용자가 전송한 file 데이터를sanitize_text_field
로 확장자 검증을 하고 있다.sanitize_text_field
는 HTML 태그나 특수 문자를 제거하지만, 확장자 형식의 유효성 검증에는 적합하지 않아 php와 같은 실행 가능한 파일 확장자를 업로드 할 수 있다.
sanitize_text_filed란?
- 잘못된 UTF-8 확인
- 단일
<
문자를 엔터티로 변환 - 모든 태그 제거
- 줄 바꿈, 탭 및 추가 공백 제거
- 퍼센트로 인코딩된 문자 제거
사용자의 입력이나 데이터베이스에서 문자열을 정리하는 함수로 위 기능을 통해 특수 문자나 태그, Unicode 등을 방지하여 XSS를 방어할 수 있지만 별도의 확장자에 대한 제한이 없어 php 파일을 업로드 할 수 있다.
1
2
$filename = plugins_url( basename( dirname( __FILE__ ) ) ) . '/uploads/' . $filename;
add_post_meta( $post_id, 'import_xml_url', $filename );
moove_save_import_template
함수에서plugins_url
으로 url을 생성하고 있다.filename
변수는plugins_url
함수를 통해 디렉토리 내의 업로드된 파일에 접근할 수 있는 전체 url이 저장된다.filename
은 ‘import_xml_url’이라는 키 이름으로 특정 게시물의 메타 데이터로 저장된다.
1
'feedurl' => get_post_meta( $template_id, 'import_xml_url', true ),
- 특정 게시물의 메타 데이터는
template_view.php
파일에서 사용자에게 보여진다. - 해당 메타데이터에 저장된 url을 통해 업로드한 파일에 접근이 가능하고, 결과적으로 RCE가 가능하다.
POC
XML, RSS 파일 업로드
처음 파일을 업로드할 때 XML, RSS 파일 형식만 허용되기 때문에 정상적인 XML 파일을 업로드한다.
php 파일 업로드를 시도 할 때 *.xml , *.rss 형식만을 허용하는 것을 확인할 수 있다.
SAVE AS TEMPLATE
해당 CVE에서 취약점이 발생한 SAVE AS TEMPLATE 기능이다.
패킷을 확인해보면 앞서 살펴봤던 moove_save_import_template
함수로 file
, extension
등을 전송하는 것을 확인할 수 있고, moove_save_import_template
함수는 sanitize_text_field
함수를 통해 확장자를 검증하기 때문에 php 파일 업로드가 가능하다. 해당 실습에서는 간단한 웹쉘을 업로드했다.
WebShell 실행
SAVE AS TEMPLATE 기능으로 저장된 template을 선택 하면 해당 파일 경로를 얻을 수 있다.
wp-content/plugins/import-xml-feed/uploads/67399dca21640.php?cmd=pwd
앞서 확인한 경로로 접근 후 pwd 명령 실행 시 성공적으로 실행된 것을 확인할 수 있다.
추가 취약점: CVE-2023-4521
CVE-2023-4521은 이전 취약점(CVE-2023-4300)과 밀접하게 연관되어 있으며, 이전 취약점에 의해 업로드된 테스트 PHP 파일(Webshell)이 제거되지 않아 발생한다. 패치는 되었으나, 이미 시스템에 남아 있는 Webshell 파일로 인해 여전히 RCE가 가능한 상태가 유지된다.
취약점 개요
CVE-2023-4521의 핵심은 “웹쉘 파일의 잔존”이다. Import XML and RSS Feeds 플러그인의 이전 취약점(CVE-2023-4300)을 통해 업로드된 악성 PHP 파일이 플러그인 디렉토리에 그대로 남아 있는 경우, 패치 이후에도 공격자는 여전히 이를 통해 원격 코드 실행(RCE)을 수행할 수 있다.
- 취약점 종류: 잔존 파일(Remnant File)로 인한 취약점
- 취약점 영향: 원격 코드 실행 (RCE)
- 취약 버전: CVE-2023-4300 취약점 발생 후 웹쉘 제거되지 않은 상태의 2.1.4 이하
- CVE ID: CVE-2023-4521
취약점 원인
- 웹쉘 파일이 삭제되지 않음
- CVE-2023-4300의 패치를 통해 새로운 파일 업로드는 방지되었으나, 이전에 업로드된 악성 파일은 디렉토리에 그대로 남아 있었다.
- 공격자가 업로드한 웹쉘 파일은 URL을 통해 접근 가능하며, 이를 통해 원격 명령 실행(RCE)이 가능하다.
패치 로그 분석
wordpress plugin Repository에서 Moove Agency를 검색해보면 여러 패치 로그들이 나온다.
그 중 CVE-2023-4521으로 인한 패치 버전인 2.1.5버전을 확인해보면 2.1.4 버전에서 업로드 파일이 제거된 로그가 있는 것을 확인할 수 있다.
CVE-2023-4300으로 인한 패치 버전인 2.1.4버전을 찾아보면 총 3개의 파일들이 업로드 되어 있다. 해당 파일들은 당시 CVE-2023-4300의 테스트를 위한 웹쉘 파일으로 해당 파일을 이용해서 RCE가 가능하다는 것을 알 수 있다.