SQL INJECTION (на примере MySQL) Часть 1
Сразу хочу оговориться, что данная статья не призывает ни к каким деструктивным действиям, а представлена в качестве ознакомления с такой проблемой как атака sql injection и призвана указать начинающим веб программистам на возможные ошибки в их коде. В статье я попытаюсь подробно, шаг за шагом рассмотреть стандартные ситуации которые наиболее часто встречаются разобрав саму атаку по частям.
Начну пожалуй с определения, что же такое атака SQL injection?
Атака sql injection - это попытка атакующего внедрить произвольные sql запросы к базе данных с целью извлечения из базы нужной информации. Возможность проведения данного рода атак происходит из за недостаточной фильтрации принимаемых от пользователя данных учавствующих в запросе.
Наибольший интерес для атакующего представляет запрос типа SELECT, который выбирает нужную пользователю информацию из базы данных и передает ее запрашиваемому скрипту, который в свою очередь обрабатывет должным образом и передает запросившему пользователю( или не передает ) Так вот к этому запросу, по сути дела, и сводится цель нападающего, чтобы составить такой запрос, который скрипт без проблем пропустит к базе данных, а база в свою очередь вернет нужную
взломщику информацию.
Примеры и коментарии буду приводить на тестовом новостном скрипте news.php, который принимает от пользователя численное значение, с помощью которого составляет запрос к базе данных, возвращая нужную новость.
Тестовый линк принимает вид
http://site.ru/news.php?id=1.
Что мы с этого можем поиметь? Для начала нам нужно обнаружить сам факт иньекции,для этого передадим параметру id всяческие значения, которые должны вызвать ошибку в sql запросе:
1.news.php?id=-1
2.news.php?id=9999
3.news.php?id=aa1
4.news.php?id=1'
5.news.php?id=1"
6.news.php?id=2-1
Наш запрос к базе данных приблизительно примет вид SELECT * FROM news WHERE id=наше_переданное_значение.
Прокоментирую, что мы только что сделали. В первом и втором запросе мы по сути дела отправили "верные" значения - числовые, те которые от нас ждет скрипт, но значения, которых скорее всего в базе данных не будет, во всяком случае значения id=-1 уж точно

Смотрим ответ сервера на наши запросы, если в качестве ответа скрипт вернул нам
в том месте где должны быть данные пустую страницу, то это может свидетельствовать о том, запрос прошел корректно, но база данных вернула пустой результат, на который скрипт никак не отреагировал и выдал результат запроса как есть, тоесть пустой результат, это несомнено на руку нападающему(скрипт переданные ему данные не обработал) ситуация в данном случае не предсказуемая программистом.Другой вариант исхода данной ситуации - скрипт вернул сообщение наподобие "запрашиваемые данные не были найдены", программист скорее всего предвидел такую ситуацию и обработал в скрипте ответ сервера базы данных. Разберем следующие три запроса, при явной ошибке, (в которой обычно указывается сервер на котором крутится база данных, то место в котором произошла ошибка, скрипт, полный путь к скрипту), все это скорее всего будет свидетельствовать об уязвимости. Но что может предпринять программист? Чтобы запутать взломщика програмист может выключить отображение ошибок в конфиге(так в принципе и следует поступить, все ошибки полезны в процессе отладки,ошибки в готовом проекте играют только на руку взломщику),пустая страница так же может свидетельствовать об ошибке. Программист может фильтровать либо приводить запрос к безопасному виду с помощью регулярных выражений в самом скрипте. Для "борьбы" с кавычками в конфигурации PHP может быть так же включена опция magic_quotes_gpc, которая экранирует обратным слешем кавычки, тем самым приводя запрос к базе данных к неправельному виду.Поясню последний запрос, этот запрос при отсуствии фильтрации должен возвратить нам тот же результат, что и news.php?id=1, но и тут взломщику надо быть осторожным потому как программист, просто может резать лишнюю часть запроса, приводя его к безопасному виду.
И так с горем пополам взломщик всетаки обнаружил наличие иньекции в параметре id. Дальше, как было сказано выше ему нужно так выстроить запрос, чтобы он был верным и выдавал нужные данные.Но прежде чем выстраивать запрос нужно определиться обрамляются ли передаваемые данные в запросе кавычками, напомню что числовые данные как могут
обрамляться кавычками так и нет, строковые же обязательно обрамляются кавычками.
Узнать этот факт можно в результате возврата сервером ошибки( за отсуствием реального примера ошибки, попробую обьяснить на
пальцах) Если при передачи серверу такого параметра news.php?id=1' , в возврате ошибки сервер выдал что то типо ''' это свидетельствует о том что параметр не обрамлен кавычками и наша кавычка пришлась не к стати.Если ''''' , то параметр обрамлен одинарными кавычками. Если же '/'' , то кавычка экранируется обратным слешем. Если в сообщении
ошибки ничего подобного не обнаружено, то ситуацию поможет разрулить логическое И(AND), напомню, что выражение, содержащее AND возвращает true(истина) если обе части этого выражения возвращают true, и falce если хотя бы одно из них ложь.Выстраиваем запрос примерно таким образом - news.php?id=1+AND+1/* , поясняю по ходу, знак + в запросе заменяет пробел, пробел можно так же заменить URL кодом %20, AND(логическое И), единица это просто единица

,
/* это комментарии, которыми мы "вырезаем" оставшуюся часть запроса, т.к. мы не указали закрывающий коментарий */.
Единица справа от AND всегда вернет true, потму что true любое число отличное от 0. Таким образом если запрос не обрамлен кавычками и присуствует факт наличия иньекции, то результат будет аналогичным запросу news.php?id=1.
Соответственно возвращение другого результата свидетельствует об ошибке и мы переформируем запрос следующим образом news.php?id=1'+AND+'1'='1 В реальном обращении к базе данных этот запрос примет вид WHERE id='1' AND '1'='1'(недостающие кавычки добавились автоматом. 1=1 тоже возвращает истину, так же как и просто 1. Разумеется кавычки сработают, если они никак не фильтруютя, с фильтрацией кавычек их подставлять не имеет смысла, параметр кавычками не обрамлен.
Далее можно попытаться определить есть ли в запросе не закрытые скобки таким образом news.php?id=1)/* Подставлять скобки столько пока не будет выведен результат отличный от правельного.
Итак мы определились с кавычками и скобками, перейдем к обьединению запросов.Обьединить запросы в версиях MySQL 4 и выше будет возможным с помощью спомощью конструкции UNION, позволяющей совместитьв два запроса типаа SELECT(как в нашем случае) в один, данная конструкция не доступна в версиях ниже 4.Существуют некоторые ограничения при обьединениии запросов, количиство полей и тип возвращаемых данных должны соответствовать друг другу в обоих запросах.
Следующим логическим шагом взломщика скорее всего будет проверка своих прав на доступ к таблице user базы mysql. Напоминаю, что данная таблица создается в базе данных MySQL по умолчанию и содержит имена пользователей,их пароли и права на доступ к базе данных. Запрос приобретает вид :
news.php?id=1+UNION+SELECT+*+FROM+mysql.user/* или
news.php?id=1'+UNION+SELECT+*+FROM+mysql.user/* если параметр обрамлен кавычками. При анализе ответа взломщик смотрит, что вернул ему сервер, здесь скорее всего может быть два варианта, один вариант когда сервер выдал запрет на доступ к таблице пользователей mysql либо о несоответствии полей во втором запросе SELECT первому, что свидетельствует о том ,что доступ к таблице взломщик имеет и остается только подобрать поля(столбцы) во втором запросе, которые бы
соответствовали первому.
Дальнейшие действия взломщика предугадать не сложно, подбираем поля во втором запросе, это может быть осуществимо как через значение NULL, которое может быть совмещено с любым типом данных, так и,что более удобно через цифры.
Запросы принемают такой вид:
news.php?id=1+UNION+SELECT+NULL/*
news.php?id=1+UNION+SELECT+NULL,NULL/*
news.php?id=1+UNION+SELECT+NULL,NULL,NULL/*
И так до результата, когда запрос будет верным и аналогичен результату запроса news.php?id=1, но здесь есть есть один недостаток - с помощью NULL мы не сможем определить в каком из полей данные выводятся в браузер пользователя, ведь NULL не содержит никаких данных, здесь возможно два пути один это подстановка в каждый столбец произвольных данных до результата отображения этих данных в окне браузера, либо использование цифр вместо NULL. Рассмотрим оба варианта.
Запросы с данными могут принять такой вид:
news.php?id=1+UNION+SELECT+'hack',NULL,NULL/*
news.php?id=1+UNION+SELECT+NULL,'hack',NULL/*
news.php?id=1+UNION+SELECT+NULL,NULL,'hack'/*
При помещении данных в поле которое выводит данные в окно браузера, взломщик получит результат в виде слова hack. Опять обращаю внимание, что подстановка слова hack обязательно обрамляется кавычками, т.к. строковый тип данных. И если кавычки на сервере фильтруются, то такой результат ничего не даст(далее я опишу как можно обойти фильтрацию кавычек)И придеться прибегнуть к запросу с помощью цифр, запрос выстраивается примерно так:
news.php?id=1+UNION+SELECT+1,2,3/* , соответственно результат при выводе данных в первом поле будет 1, втором-2, третьем 3. Но и здесь тоже может быть одно но, результаты последнего запроса как могут вывестись в окне, так и нет, причиной может быть следующее - данные могут выводиться порциями, точнее, к примеру, одна строка, а все что более строки в окно не выводится, здесь у взломщика опять же два пути. Первый путь - добавить конструкцию LIMIT и с помощью нее регулировать "блоки" информации выводящиеся в браузер, например так:
news.php?id=1+UNION+SELECT+1,2,3+LIMIT+0,1/*
news.php?id=1+UNION+SELECT+1,2,3+LIMIT+1,1/*
news.php?id=1+UNION+SELECT+1,2,3+LIMIT+2,1/*
Напомню, что конструкция LIMIT осуществляет выборку результатов запроса выводимых пользователю. И второй вариант это использовать в параметре id значение которого не может быть в базе данных, но тип значения, в свою очередь должен быть верным. В самом начале я уже говорил, что значение id равное отрицательному целому числу или положительному, но большому, скорее всего вернет пустой результат, так вот этот пустой результат как раз и займет наше значение в поле.
Пример запросов может выглядеть примерно так:
news.php?id=-1+UNION+SELECT+1,2,3/*
news.php?id=9999+UNION+SELECT+1,2,3/*
Вот примерно таким способом в большинстве случаев подбираются поля. Программист может конечно усложнить действия взломщика, запретив использование в запросах конструкции UNION, что по большому случаю усложнит задачу, но не сделает ее невозможной.При запрете UNION база данных 4 ветки и выше будет аналогична версии 3, где UNION не было, так же программист может запретить в скрипте LIMIT,AND,OR. Что может поставить в тупик некоторых взломщиков. От Того каким способом он это сделает зависит возможность проведения атаки.
И так что взломщик имеет на данный момент? Он знает колличество полей, учавствующих в запросе и в каком из полей данные выводятся в браузер. Что дальше? Дальше с помощью стандартных функций можно получить некоторую информацию о системе,предположим, что взломщик вышеперечисленными действиями определил, что данные выводятся во втром столбце запроса, его действия:
news.php?id=9999+UNION+SELECT+1,VERSION(),3/* узнает версию базы данных, при наличии смволов n или nt в версии,
можно сделать вывод, что сервер крутится на Windows системе, тоже самое, разве что кроме точной версии взломщик
мог определить анализировав вывод ошибки, где обычно выводится сервер и полный путь к скрипту вызвавшем ошибку.
news.php?id=9999+UNION+SELECT+1,DATABASE(),3/*выводит название базы данных.
news.php?id=9999+UNION+SELECT+1,USER(),3/* выводит имя пользователя под которым запущен скрипт и имя хоста.
Анализировав последний запрос вломщик может узнать под какими правами он осуществляет запросы из скрипта, если пользователь root
(не нужно путать с root администраторской учетной записью в nix подобных системах,root в MySQL никак не относится к root в UNIX)то взломщик может сделать вывод, что права доступа к базе данных у него неограничены и с базой данных, при возможности он может делать все, что пожелает. Задача программиста в данном случае состоит в следующем, чтобы ограничить пользователей под которыми выполняются запросы до прав необходимых для нормальльного выполнения скрипта
не более. И сменить имя учетной записи root на что то менее яркое , что позволит ввести неопытного взломщика в некоторое заблуждение.
Следующее, что может предпринять взломщик в данном контексте это чтение произвольного файла на сервере с помощью функции LOAD_FILE('полный_путь')полный путь к скриптам или файлам на сервере взломщик может знать как исходя из стандартной конфигурации системы(например /etc/passwd в Unix системах, доступ к которому имеют все пользователи и если сервер, к примеру, не крутится в "песочнице" взломщик скорее всего получит тоже) или анализируя сами ошибки сервера, если были таковые, где как я уже отмечал выше, скорее всего будет выведен полный путь к скрипту.Ответим на такой
вопрос, Что может подчеркнуть для себя взломщик анализируя произвольные файлы на сервере? Да все что угодно, начиная от паролей и логинов к админским учетным записям и ошибки в скриптах, которые взломщик возможно пропустил и до дня рождения бабушки администратора сервера, который он старательно закоментировал в одном из скриптов, чтобы не забыть

Но что будет делать взломщик в данном случае если кавычки жеско режутся злостным админом? Взломщик прибегнет к функции char которая позволяет перевести символы в ASCll код(числа от 0 до 255). Запрос примет вид:
news.php?id=9999+UNION+SELECT+1,LOAD_FILE(char(ч сла через запятую)),3/* программист же в свою очередь должен запретить выполнение вышеперечисленных функций.
Теперь взломщику предстоит выбрать данные из нужных таблиц, но как название этих таблиц вот в чем вопрос?
Конечно имея дело с публичным проектом взломщик просто напросто может скачать данный проек к себе на машину и исследовать, но в данном случае взломщику нужно обладать неплохими знаниями как в WeB кодинге, так и в проведении атак на веб приложения, потому как публичный продукт наверняка перелопатил не один хакер и возможности найти баги в таком продукте не высоки, но зато при обнаружении уязвимости, в арсенале взломщика будет приватный сплоит к публичному
приложению, что само по себе предоставляет реальные шансы завладеть не одной системой, но не будем об этом, а рассмотрм обычный пример, где хакеру предстоит столкнуться кодером написавшим сам свой проект и скачать его исходники нет возможности.
В этом случае взломщику нужно быть просто чуточку или не чутучку(как повезет) сообразительным вот и все. Из анализа контента сайта он сделает примерные выводы о содержании таблиц в базе данных, что за данные в них хранятся, какие скорее всего названия таблиц и названия полей, ведь программисты в большинстве случаев не стремяться изобретать велосипед и оставляют названия по умолчанию,если это уже готовый продукт либо стремясь к солидарности с другими
программистами дают названия своим таблицам стандартные и легкие для восприятия, например userpassword

понимаю, что понятное и легкое название воспринимается легко и дает неоспоримое приемущество при "вьезжании с ходу" что за данные хранятся в этой таблице, но одновременно таит в себе некоторую опасность, взломщик так же может предугадать название таблиц и полей исходя из своих догадок, методом подбора, обратив внимание на название переменных учавствующих в скриптах, проанализировав Cookie файлы. Вот почему в этих случаях нужно быть несколько хитрее взломщика.
При доступе к базе mysql таблице user взломщик может сформировать такие запросы при получении данных:
news.php?id=9999+UNION+SELECT+1,user,3+ FROM+mysql.user/*
news.php?id=9999+UNION+SELECT+1,password,3+ FROM+mysql.user/*
соответственно получив или не получив(если программист предвидел такую попытку) доступ к базе данных.
Одним из вариантов для подбора не изветных взломщику таблиц,полей может может пригодиться конструкция LIKE применяющаяся для выборки данных по маске.
Например отсуствие ошибки в таком запросе
news.php?id=9999+UNION+SELECT+1,2,3+ FROM+LIKE+'p%'/*
ьудет свидетельствовать о наличии в текущей базе данных таблицы, которая начинается на p, и далее подставляя другие символы взломщик
может основываясь на предположения подобрать название таблицы, а затем и полей к ней.
В арсенале хакера так-же может быть такая опасная функция как INSERT OUTFILE, с помощью которой хакер может записать произвольный файл на сервере в папку доступную для записи (обычно это tmp)web шел и затем подцепить этот шел, например через баг PHP Include, выше я уже писал, что с помощью LOAD_FILE хакер может просматривать содержимое произвольных файлов как раз таким способом и можно обнаружить уязвимость в каком нибудь скрипте, чтобы воспользоваться web шелом.
При использовании INSERT OUTFILE есть отличия от LOAD_FILE и они заключаются в следующем: параметры передаваемые этой функции так же обрамляются кавычками, но не помещаются в круглые скобки,нет возможности применить char и обязательное указание таблицы, присуствующей на сервере( при фильтрации кавычек соответсвенно возможности использовать INSERT OUTFILE нет). Сам запрос примерно выглядит так:
UNION SELECT веб_шел INTO OUTFILE 'путь_к_папке_доступной_на_з апись' FROM таблица_существующая_в_баз
Ну вот впринципе все что я хотел поведать в первой части материала о SQL injection, люди знакомые с данным видом атак наерное ничего нового для себя из прочтения данной статьи не подчеркнули, потму как в статье был описан стандартный метод проведения атаки, рассматривая не стандартные методы и более интересные нужно приводить конкретные примеры уязвимостей на практике из конкретно взятой системы, думаю по понятным причинам в планах у меня этого нет.
Людям же начинающим программировать WEB приложения, я надеюсь, данная статья приоткрыла глаза на сколько эта атака может быть серьезной и как может повлиять на их будущее в качестве программистов.
И на последок хотелось бы заметить, что при написании приложения нужно отдавать себе отчет, что помимо допропорядочных пользователей ваше детище навеняка будут ковырять руки не с добрыми намерениями и безопасности нужно уделить должное внимание, тут наверное нужно посветовать встать на место взломщика, что мы попытались сделать в данной статье, предугадать его действия и возможные запросы, в скрипте жестко контролировать принимаемые от пользователя данные и если параметр принимает численное значение, то имеет смысл запретить все кроме чисел и исходить НЕ из принципа разрешено все кроме запрещенного, а наоборот ЗАПРЕЩЕНО все кроме разрешенного.
Вот теперь наверно все .
Автор: Э_L_A_Y