Patreon WordPress 플러그인에서 발견된 취약점
게시 됨: 2021-03-26WordPress용 Patreon 플러그인의 내부 감사 중에 Jetpack Scan 팀은 누군가가 웹사이트를 인수할 수 있는 몇 가지 약점을 발견했습니다.
이러한 취약점은 이러한 모든 문제를 수정하는 버전 1.7.2를 즉시 릴리스한 플러그인 작성자에게 공개되었습니다. 이전 버전의 플러그인을 실행 중인 경우 오늘 업데이트하세요!
모든 기술적 세부 사항을 읽으십시오. 이 문제가 해결되지 않으면 걱정하지 마십시오. 맬웨어 검사 및 자동 업그레이드 또는 제거를 처리하기 위해 Jetpack Scan을 제공합니다.
우리 팀은 로컬 파일 공개, CSRF(교차 사이트 요청 위조) 및 XSS(반사된 교차 사이트 스크립팅) 취약점을 비롯한 다양한 공격 벡터를 식별했습니다.
로컬 파일 공개 취약점은 악의적인 행위자가 웹사이트의 비밀 키 및 데이터베이스 자격 증명과 같은 중요한 정보에 액세스하는 데 사용할 수 있는 버그입니다. 반사된 사이트 간 스크립팅 및 사이트 간 요청 위조 취약성은 공격자가 조심스럽게 제작된 악성 링크를 클릭하도록 속여 의심하지 않는 사용자를 대신하여 특정 작업을 수행할 수 있게 하는 문제입니다.
악용될 경우 이들 중 일부는 악의적인 개인이 취약한 웹사이트를 장악할 수 있습니다.
로컬 파일 공개 취약점
영향을 받는 버전: < 1.7.0
CVE ID: CVE-2021-24227
CVSSv3: 7.5
CWSS: 83.6
public static function servePatronOnlyImage( $image=false ) {
if ( ( !isset( $image ) OR !$image ) AND isset( $_REQUEST['patron_only_image'] ) ) {
$image = $_REQUEST['patron_only_image'];
}
if ( !$image OR $image == '') {
// This is not a rewritten image request. Exit.
return;
}
if ( !( isset( $_REQUEST['patreon_action'] ) AND $_REQUEST['patreon_action'] == 'serve_patron_only_image' ) ) {
return;
}
$upload_locations = wp_upload_dir();
// We want the base upload location so we can account for any changes to date based subfolders in case there are
$upload_dir = substr( wp_make_link_relative( $upload_locations['baseurl'] ) , 1 );
$image = get_site_url() . '/' . $upload_dir . '/' . $image;
if ( current_user_can( 'manage_options' ) ) {
Patreon_Protect::readAndServeImage( $image );
}
// Below define can be defined in any plugin to bypass core locking function and use a custom one from plugin
// It is independent of the plugin load order since it checks if it is defined.
// It can be defined by any plugin until right before the_content filter is run.
if ( apply_filters( 'ptrn/bypass_image_filtering', defined( 'PATREON_BYPASS_IMAGE_FILTERING' ) ) ) {
Patreon_Protect::readAndServeImage( $image );
}
// Check if the image is protected:
$attachment_id = attachment_url_to_postid( $image );
// attachment_url_to_postid returns 0 if it cant find the attachment post id
if ( $attachment_id == 0 ) {
// Couldnt determine attachment post id. Try to get id from thumbnail
$attachment_id = Patreon_Protect::getAttachmentIDfromThumbnailURL( $image );
//No go. Have to get out and serve the image normally
if ( $attachment_id == 0 OR !$attachment_id ) {
Patreon_Protect::readAndServeImage( $image );
Patreon-Connect에는 사이트를 방문하는 모든 사람이 남용할 수 있는 로컬 파일 공개 취약점이 포함되어 있습니다. 이 공격 벡터를 사용하여 공격자는 nonce 및 쿠키 생성에 사용되는 데이터베이스 자격 증명 및 암호화 키가 포함된 wp-config.php와 같은 중요한 내부 파일을 유출할 수 있습니다.
악용에 성공하면 이 보안 결함으로 인해 악의적인 사용자가 사이트를 완전히 장악할 수 있습니다.
로그인 양식에 반영된 XSS
영향을 받는 버전: < 1.7.2
CVE ID: CVE-2021-24228
CVSSv3: 8.8
CWSS: 80.6
public static function processPatreonMessages() {
$patreon_error = '';
if ( isset( $_REQUEST['patreon_error'] ) ) {
// If any specific error message is sent from Patreon, prepare it
$patreon_error = ' - Patreon returned: ' . $_REQUEST['patreon_error'];
}
if ( isset( $_REQUEST['patreon_message'] ) ) {
return '<p class="patreon_message">' . apply_filters( 'ptrn/error_message', self::$messages_map[ $_REQUEST['patreon_message'] ] . $patreon_error ) . '</p>';
Patreon-Connect는 WordPress 로그인 양식(wp-login.php)을 연결하고 사용자가 Patreon 계정을 사용하여 사이트에서 인증할 수 있도록 합니다. 불행히도, 장면 뒤에 있는 오류 로깅 논리 중 일부는 사용자 제어 입력이 삭제되지 않은 상태로 로그인 페이지에 반영되도록 허용했습니다.
이 취약점을 성공적으로 악용하려면 공격자가 피해자를 속여 악성 자바스크립트 코드가 포함된 부비트랩 링크를 방문하도록 해야 합니다. Javascript는 피해자의 브라우저 컨텍스트에서 실행되기 때문에 공격자는 해당 링크에 숨겨진 코드를 조정하여 이 사용자의 권한이 허용하는 모든 작업을 수행할 수 있습니다.
이 공격이 관리자에 대해 성공하면 스크립트가 사이트를 완전히 장악할 수 있습니다.
AJAX 작업 'patreon_save_attachment_patreon_level'에 반영된 XSS
영향을 받는 버전: < 1.7.2
CVE ID: CVE-2021-24229
CVSSv3: 8.8
CWSS: 80.6
$args = array (
'attachment_id' => $attachment_id,
'patreon_level' => $_REQUEST['patreon_attachment_patreon_level'],
'message' => $message,
);
echo self::make_image_lock_interface( $args );
public function make_image_lock_interface( $args = array() ) {
$interface = '';
$interface .= '<div class="patreon_image_lock_modal_content">';
$interface .= '<span class="patreon_image_lock_modal_close">×</span>';
$interface .= ' <form id="patreon_attachment_patreon_level_form" action="/wp-admin/admin-ajax.php" method="post">';
$interface .= '<h1 class="patreon_image_locking_interface_heading">Lock Image</h1>';
$interface .= '<div class="patreon_image_locking_interface_level">';
$interface .= '<span class="patreon_image_locking_interface_input_prefix">$<input id="patreon_attachment_patreon_level" type="text" name="patreon_attachment_patreon_level" value="' . $args['patreon_level'] . '" / ></span>';
플러그인은 또한 AJAX 후크를 사용하여 Patreon 가입자가 지정된 첨부 파일에 액세스하는 데 필요한 서약 수준을 업데이트합니다. 이 작업은 'manage_options' 권한이 있는 사용자 계정(즉, 관리자만)에 액세스할 수 있습니다.

불행히도 이 AJAX 끝점에 사용된 매개변수 중 하나는 사용자에게 다시 인쇄되기 전에 삭제되지 않으므로 이것이 나타내는 위험은 우리가 설명한 이전 XSS 취약점과 동일합니다.
공격자가 사용자 메타를 덮어쓰거나 생성하도록 허용하는 CSRF
영향을 받는 버전: < 1.7.0
CVE ID: CVE-2021-24230
CVSSv3: 6.5
CWSS: 42
public function toggle_option() {
if( !( is_admin() && current_user_can( 'manage_options' ) ) ) {
return;
}
$current_user = wp_get_current_user();
$option_to_toggle = $_REQUEST['toggle_id'];
$current_value = get_user_meta( $current_user->ID, $option_to_toggle, true );
$new_value = 'off';
if( !$current_value OR $current_value == 'off' ) {
$new_value = 'on';
}
update_user_meta( $current_user->ID, $option_to_toggle, $new_value );
}
일부 끝점은 받은 요청이 사용자의 합법적인 작업에 따라 전송되었는지 확인하지 않았습니다. 이 작업은 nonce를 사용하여 수행할 수 있습니다. 이러한 보호되지 않는 끝점 중 하나를 통해 악의적인 개인은 부비 트랩된 링크를 만들 수 있어 한 번 방문한 피해자의 계정에 임의의 사용자 메타데이터를 덮어쓰거나 생성할 수 있습니다.
악용되는 경우 이 버그를 사용하여 영향을 받는 사용자 계정의 역할 및 권한이 포함된 "wp_capabilities" 메타를 덮어쓸 수 있습니다. 이렇게 하면 기본적으로 사이트에서 차단되어 유료 콘텐츠에 액세스할 수 없게 됩니다.
공격자가 Patreon에서 사이트 연결을 끊도록 허용하는 CSRF
영향을 받는 버전: < 1.7.0
CVE ID : CVE-2021-24231
CVSSv3: 6.5
CWSS: 26.1
if ( isset( $_REQUEST['patreon_wordpress_action'] ) AND $_REQUEST['patreon_wordpress_action'] == 'disconnect_site_from_patreon' AND is_admin() AND current_user_can( 'manage_options' ) ) {
// Admin side, user is admin level. Perform action:
// To disconnect the site from a particular creator account, we will delete all options related to creator account, but we will leave other plugin settings and post gating values untouched
$options_to_delete = array(
'patreon-custom-page-name',
'patreon-fetch-creator-id',
'patreon-creator-tiers',
'patreon-creator-last-name',
'patreon-creator-first-name',
'patreon-creator-full-name',
'patreon-creator-url',
'patreon-campaign-id',
'patreon-creators-refresh-token-expiration',
'patreon-creator-id',
'patreon-setup-wizard-last-call-result',
'patreon-creators-refresh-token',
'patreon-creators-access-token',
'patreon-client-secret',
'patreon-client-id',
'patreon-setup_is_being_done',
'patreon-setup-done',
'patreon-currency-sign',
);
// Ask the API to delete this client:
$creator_access_token = get_option( 'patreon-creators-access-token', false );
$client_id = get_option( 'patreon-client-id', false );
// Exceptions until v1 v2 transition is complete
$api_version = get_option( 'patreon-installation-api-version' );
if ( $api_version == '1' ) {
// Delete override - proceed with deleting local options
foreach ( $options_to_delete as $key => $value ) {
delete_option( $options_to_delete[$key] );
}
update_option( 'patreon-installation-api-version', '2' );
update_option( 'patreon-can-use-api-v2', true );
wp_redirect( admin_url( 'admin.php?page=patreon_wordpress_setup_wizard&setup_stage=reconnect_0') );
exit;
}
이는 CSRF(동일한 종류의 공격)이지만 관리자를 대상으로 한다는 점에서 마지막 취약점과 유사합니다. 이 특정 공격 벡터는 이전과 같이 작동합니다. 공격자는 로그인한 관리자가 특수 제작된 링크를 방문하도록 해야 합니다.
이 특정 끝점은 Patreon에서 사이트 연결을 끊을 수 있으므로 이 공격 벡터를 목표로 하는 공격자는 새 콘텐츠가 사이트에 동기화되는 것을 방지할 수 있습니다.
타임라인
- 최초 연락 시도(실패) – 12월 4일
- 2차 접촉 시도 - 12월 11일
- 저자는 보고서를 인정합니다 – 12월 15일
- 버전 1.7.0 릴리스 – 1월 5일
- 3월 9일에 2개의 추가 XSS 문제를 보고합니다.
- 저자는 두 번째 보고서를 인정합니다 – 3월 9일
- 버전 1.7.2 릴리스 – 3월 11일
결론
사이트에서 사용 중인 Patreon-Connect 플러그인의 현재 버전을 확인하고 1.7.2가 아닌 경우 가능한 한 빨리 업데이트하는 것이 좋습니다!
Jetpack에서는 이러한 유형의 취약점으로부터 웹사이트를 보호하기 위해 열심히 노력하고 있습니다. 새로운 위협에 한 발 앞서 나가려면 보안 검색 및 자동화된 맬웨어 제거가 포함된 Jetpack Scan을 확인하십시오.
크레딧
이 보안 공개는 George Stephanis, Fioravante Souza, Miguel Neto, Benedict Singer 및 Marc Montpas 덕분에 가능했습니다.