![]() |
Роковые ошибки Php. Часть вторая.
Статья является продолжением к первой части: _http://forum.antichat.ru/thread54355.html Выражается благодарность всем, что так или иначе обсуждал со мной аспекты атак, описанных в статье. ======================================= Роковые ошибки PHP. Часть вторая. Небезопасное Web-программирование. [ Вступление ] Поиск уязвимостей медленно но верно переходит на новую качественную ступень - исследование платформы/интерпретатора, изучение особеностей работы критичных для безопасности функций, пограничные состояния, переполнения буфера. Старые элементарные баги неумолимо изживают себя. www.hardened-PHP.net ярко показал современный уровень дыр и эксплоитов. И происходит своего рода естественный отбор - либо ты учишься чемуто-то новому, либо уходишь... Я покажу Вам, как остаться. В первой части тебя ознакомили с распространенными уязвимостями. Расскажу тебе об особенностях PHP, которые обязан знать каждый уважающий себя багоискатель. В купе с первой частью ты сможешь не только расковырять пачку двигов, но и осознать - век PHP багов отнюдь не 20й век. Все только начинается, тебе повезло быть здесь и сейчас. Учимся думать и все получится, ибо мозг - наш главный инструмент. [ index.php?GLOBALS[file]=hehe! ] Бага медленно уходит в историю. Существует немало эксплоитов под неё, например, от небезызвестного rgod'a. В достаточно устаревших версиях PHP < = 4.3.10 и PHP <= 5.0.5 есть возможность определить переменную через массив GLOBALS, используя запрос вида: php.ini: register_globals=ON /index.php?GLOBALS[foobar]=blaaa Смотрим на результат: print_r($GLOBALS); А поскольку суперглобальный массив GLOBALS проецируется на все переменные: print_r($foobar); [ import_request_variables('GPC') - EVIL OVERWRITE ] В первой части ты уже читал про неё, но опишу интересную особенность 'аццкой' перезаписи. 'GPC' (GET/POST/COOKIE) в аргументе функции означает порядок, следуя которому будут переписаны переменные. Тоесть в данном случае - GPC - сначала перепишутся значения из GET, потом из POST и только после из COOKIE. Как ты думаешь, что выдаст данный код? /index.php?a=111 POST: a=222 COOKIE: a=333; PHP код:
Используя данную особенность, можно виртуозно обходить фильтры защиты. Реальный пример уязвимого кода 'самой наисекурной' SLAED CMS 3.x ( мой привет автору +) ): PHP код:
/index.php?name=FAQ&file=index cookie: name=../../.././etc/passwd%00 [ parse_str() OVERWRITE ] Достаточно новый баг(скорее фича) - parse_str() без второго параметра позволяет аналогично extract() или import_request_variable() переопределить глобальный переменные, в том числе и служебные как _SERVER , _SESSION , _ENV, GLOBALS /index.php?_SERVER[REMOTE_ADDR]=antichat PHP код:
Для нас важно отсутствие второго аргумента, иначе перепишется только сам второй аргумент - parse_str($_SERVER['QUERY_STRING'],$query_str) [ $$, &$ - жесткие и символические ссылки ] Использование жестких и символических ссылок также открывает потенциальные возможности для проникновения. Ранее недоступные из вне переменные могут стать досягаемыми, они глобализируются и несут угрозу безопасности. Переменные становятся взаимозависимыми, и может возможность переопеределить критически важные переменные, например, идентификаторы сессии. /index.php?_SERVER[REMOTE_ADDR]=antichat.ru&_SESSION[auth]=bugaga! PHP код:
А теперь бонус - помимо сказанного, благодаря тому что уязвимость глобализирует переменные - открывается возможность использовать UNSET атаку даже при выключенном register_globals! Причем ни import_request_variables('GPC') , ни extract() такого же эффекта не дают. [ ini_set(), ini_get() ] Использование ini_set() позволяет изменять некоторые значения опций конфигурации. Во-первых, далеко не все значения могут быть изменены из текущего скрипта. Ознакомься с полным списком здесь http://php.su/functions/?ini_set и обрати внимание на "Определение констант PHP_INI_*' (внизу). Только опции с PHP_INI_ALL флагом могут быть изменены через ini_set(). А во-вторых, некоторые хостеры не жалуют ini_set(), ini_get() и запрещают их, тем самым делая свой хостинг менее привлекательным для размещения всякой гадости, вроде веб-ботов, парсеров\грабберов, анонимайзеров и прочей 'нежити', которым для комфортной работы требуется изменять стандартные настройки PHP, вроде "max_execution_time","default_socket_timeout" и тд. Но такой расклад получается далеко не на пользу легальным программам, особенно, если их безопасность строится, опираясь на ini_set(). Тогда, например, @ini_set( "register_globals", "0" ); @ini_set( "magic_quotes_gpc", "1" ); используемые в скрипте не изменят настроек и движок может стать легкой мишенью. Тоже касается и ini_get(), так как в случае его запрета может быть нарушена логика защитного механизма. Как пример из практики - последний громкий эксплоит под PunBB. [ ereg() poison NULL-byte ] Использование ereg() и её производных (ereg_replace(),mb_ereg_match()) опасно тем, что ereg() не является бинарно-совместимой функцией, тоесть воспринимает NULL-байт за конец строки и прекращает обработку, что дает возможность обойти фильтр. /index.php?page=%00../../../../../../../etc/passwd%00blaa PHP код:
PHP код:
Поскольку ereg() юзают где только возможно, здесь поле непаханное для всех типов атак. [ $_SERVER[HTTP_X] ] Дыры в http-заголовах занимают в моем скромном рейтинге почетное первое место. Это настоящий клад для багоискателей. Сколько двигов тут полегло - уже и не сосчитать. Уважаемые девелоперы! Продолжайте и далее доверять всем входящим http-данным! =)) И без работы ни вы ни мы не останемся... Достаточно вспомнить последний публичный эксплоит под IPB_2.16 Если хорошо порыться на милворме, можно найти от XSS и SQL-inj до выполнения кода(!). Наиболее часто используемые заголовки - user-agent, referer, accept-language, client-ip, x-forwarded-for, x-real-ip Вообще сейчас очень модно отсылать данные, помещая их в HTTP-заголовки. Такой способ часто используется при выполнении команд в эксплоитах(так называемый интерактивный шелл). Как пример - последний публичный эксплоит под punBB - $_SERVER['HTTP_SHELL'] Но модно не потому, что круто. А потому что очень удобно. ~ простота посылки и приема средствами php,perl,etc.. ~ отсутствие логирования, в отличии от _POST (mod_security) ~ пока не существует IDS, фиксирующей передачу левых данных в произвольных http-заголовках [ magic_quotes_gpc - ? ] Практически каждый из нас слышал о 'магических' ковычках и их роли в SQL-injection. Но мало кто задумывался, что означает 'gpc'. Если мы обратимся к определению, то там ясно сказано - 'magic_quotes_gpc=ON' автоматически слеширует _GET/_POST/_COOKIE - (gpc) + _REQUEST. А остальное - никто не слеширует, как то $_FILES, $_ENV, $_SERVER, $_SESSION... Теперь вспомните предыдущий пункт. Да, юзер-агент, реферер лежат в _SERVER и magic_quotes'ом не обрабатываются. А значит, если программер не позаботился о фильтре, то репутация продукта висит на волоске. Кроме того, многие защиты основываясь на 'if(get_magic_quotes_gpc())..' используют add~ или stripslashes. Поскольку такой подход применяется ко всем переменным подряд, а является корректным лишь для GET/POST/COOKIE/REQUEST, то при magic_quotes=ON приложение становится подверженным SQL-inj в не GPC-массивах. PHP код:
[ intval(), (int) ] У intval() есть интересная особенность - она возвращает TRUE если первой в аргументе содержится хотя бы одна цифра. И у разработчиков тоже есть интересная особенность =)) -- они периодически используют intval()/(int) в логичесих условиях, допуская непростительные ошибки. Ведь наличие цифр в строке вовсе не гарантирует отсутствие других символов. Пример: /index.php?id=1'"qwerty PHP код:
Для безопасного сравнения используйте is_numeric() [ a == 'a' and a === 'a' ] PHP не проверяет равенство типов при двойном равно (==), автоматически приводя их к строковому. Для верного сравнения данных разных типов применяется тройное равно (===). Неправильное использование двойного равно, например, в авторизации, == может обернуться уязвимостью. PHP код:
no. Как пример - этой уязвимости был подвержен phpBB 2.0.8 Однако, такая бага далеко не уникальна и встречается в других продуктах по сей день ;) [ string or integer ? ] Более того, если мы попробуем сравнить строку с числом, то... сравнение как ни странно состоится! Важно только то, чтобы первым символом в строке было число - именно с ним произойдет сравнение. Пример: PHP код:
[ %2527 => %27 => ' , %2522 => %22 => " ] Двойное урл-кодирование параметра в купе с urldecode() дает возможность обойти magic_quotes/фильтры и выполнить произвольные SQL-команды. Здесь %25 - урл-символ знака процента '%'. Таким образом после преобразования мы получаем незаэкранированную ковычку. Пример уязвимого кода: /index.php?login=hack%2527+or+1=1+limit+1/* PHP код:
Подобный баг был в ранних версиях phpBB и давал совместно с preg_replace(//e) выполнение произвольного кода. [ base64_encode/base64_decode ] Кодирование данных в base64 виде - также излюбленный приём вебмастеров. Ну и как следствие - в закодированном виде слеширования конечно же не происходит и может возникнуть условие для SQL-inj. Пример уязвимого кода: PHP код:
[ index.php?a[]=antichat ] Не спешите пропускать абзац - вас ждет не только раскрытие пути... Зачастую данные извлекаются из глобальных массивов без проверки параметра, массив он или строка(число). И здесь можно получить от раскрытия пути до обхода проверок. А теперь вспомните, функцию, которая чаще всего работает с глобальными массивами - addslashes() ! ага? пахнет жаренным. Это уже действительно серьезно. Многие проверки в движках давятся при неожиданной встрече с массивом. Пример безопасной проверки, используя рекурсивный самовызов: PHP код:
Например, array_key_exists() выдаст "Warning: array_key_exists()..." Полный список уязвимых функций еще предстоит составить... [ preg_replace() with /e ] Достаточно известный способ выполнить команды там, где нельзя, но очень хочется =) При использовании /e(~e) модификатора в регулярке - пхп код, содержащийся во втором аргументе, выполнится... /index.php?match=123456&search=phpinfo(); PHP код:
Баг достаточно известный - эксплоит под phpbb=2.0.17 как раз на нем и был основан. Пример: /index.php?match=123456&search=phpinfo();&modifi=34 5/e%00 PHP код:
[ Динамическое определение переменных ] Иногда перед кодером встает необходимость динамически определить переменную, когда задается произвольно не только значение, но и её имя. Например, данные получают из БД, конфига, темплэйта или напрямую от пользователей, а после проходят эту операцию. Опасность заключается в том, что при отсутствии должной фильтрации атакующий получает веб-шелл на сервере. Пример уязвимого кода: /index.php?value=;phpinfo(); PHP код:
Но и без эвала, это несет угрозу неконтролируемой глобализации произвольных переменных, как например здесь: /index.php?var=auth&val=OK; PHP код:
[ create_function() ] Создание функций как частный случай обратного вызова функций - один из самых красивых способов выполнения произвольного кода. Рассмотрим пример на create_function(): /index.php?a=phpinfo(); PHP код:
Практическое нахождение такой уязвимости обусловлено высоким уровнем удачи вашего юнита +) , а так же редкой криворукостью кодера. Как пример - нашумевший эксплоит Шанкара для выполнения произвольных php-команд в TikiWiki. [ header("Location: ... die(); ] Поставив перенаправление, программист порой забывает добавить после него exit() или die(). Таким образом, код продолжает выполняться. Используя любую http-тулзу(AccessDiver,intruder,inetcrack), отключив java-script в браузере(если редирект выполнен на яве) или к примеру эксплоит на PHP - атакующий увидит результат. Подобная ситация - серьезная брешь в безопасности,ведь редирект часто используют при неверной авторизации или в механизмах обработки ошибок. Пример безопасного кода: PHP код:
[ EOF ] С точки зрения кодера, многих из описанных уязвимостей вообще не существует, поскольку копаться в тонкостях PHP - неблагодарная а самое главное - не оплачиваемая работа. Потому у Нас всегда будет преимущество и лишний козырь. Эти баги были. И будут. Вне зависимости от того, сколько раз про это напишут и скажут. Описанным уязвимостям подвержены многие и многие продукты. И именно Нам еще и еще раз выпадает честь и удовольствие это подтвердить =) Чем мы вскоре и займемся, но это уже совсем другая история... |
Вообшем прочитал всё 3 раза, был в шоке, после пива очнулся. Но малекий шок остался, после того как прочитал про inval() я думал что кроме числовых значей он больше нечего не берёт , но окозалось не так жаль. Вот иди после всего этого и пиши скрипт.
P.S Elekt отлично просто супер, статья очень понравилась, давай продолжение таких статей. |
Так сразу страшно ложиться спать стало. Пересмотрел последний скрипт. Практически все вышеописанное используется, но к счастью по умному (хотя сам этого не подозревал)
Весьма полезная статья оказалась для меня, да думаю не только для меня :) |
многие пункты типа "intval" и "urldecode после addslashes" ,addslashes_deep - вообще бред. В документации ясно все про это написано. А последняя функция - вообще непонятно что.
|
Соглашусь с nerezus что там можна делать с headr() ? чёта тожа непонял.
|
про Header не понял.
Цитата:
|
про HEADER имеется ввиду- такой к примеру скрипт edit.php в админке, в начале проверяется залогинился ли ты под админом , если нет идет редирект, но после функции header не стоит exit, поэтому можно использовать этот скрипт дальше невзираю на права.
|
Хорошая статья, Элек на высоте, но вот только мое ИМХО, журнал Х не стоит таких мыслей и такой Pr ачату не нужен, все разработки, подобные этой, должны оставаться внутри закрытых комунити.
|
В принцыпе боян, но первый раз это вижу изложенненным с точки зрения php, и с точки зрения програмиста.
|
Цитата:
Ну а Элект молодец. |
| Время: 13:55 |