Перезапись произвольных переменных через import_request_variables() в PHP 1) Уязвимость: Используя import_request_variables() можно перезаписать $_* и $* (любые переменные PHP). 2) Дата: 09.03.2007 3) Под угрозой: PHP >=4.0.7 <=5.2.1 4) Опасность: Высокая 5) Производитель: http://www.php.net/ 6) Инструкции/Источник: http://www.wisec.it/vulns.php?id=10 http://www.wisec.it/vuln_10.txt 7) Автор: Stefano `wisec` di Paola ([email protected]) Francesco `ascii` Ongaro ([email protected]) 8) Перевод: NeMiNeM I. Вступление PHP это скриптовый язык. Поскольку в прошлом PHP включал по умолчанию GLOBALS программисты писали приложения используя этот метод ввода, теперь, когда конфигурации с GLOBALS ON отошли (хотя всё ещё используются многими вэб-хостингами), программисты вместо переписывания кода, сами добавляют патчи для реализации superglobals. Такие коды приносили разработчикам больше проблем чем выгод и они решили добавить функцию для безопасного импорта части всего _REQUEST. Эта функция называется import_request_variables() и существует начиная с PHP 4.0.7. 2. Описание. Цитата из справочника по PHP: Значит import_request_variables() имитирует register globals on но немножко отличаеться от extract(). (Мини-разведку import_request_variables() против extract() читаем в конце статьи). Они предупреждают нас о префиксе и это правильно: во-первых, без prefix у нас будут такие же проблемы globals on. Во-вторых это то, о чём идёт речь в этой инструкции по безопасности: использование фунцкии import_request_variables даёт возможность перезаписать массивы $_GET $_POST $_COOKIE $_FILES $_SERVER $_SESSION и все другие, которые мы не назвали. Мы проводим дальнейшие исследование над _FILES и похоже возможно перезаписать массив но мы не уверены что таким образом можно обмануть скрипты загрузки файла. Если дать взломщику определённые точки входа (первый аргумент функции это нечувствительная к регистру строка методов ввода которые импортируют. G значит GET, P - Post и C - Cookie), он сможет перезаписать любой внутренний и защищённый массив. Как результат, если у вас REGISTER GLOBALS ON вы НАМНОГО больше в безопасности. Есть небольшой бонус: как выделено в фрагментах кода в следующем разделе Анализ, символ P будет включать точку входа как POST так и FILES, таким образом import_request_variables('GPC') будет давать глобальную область видимости всего, что указано в GET POST COOKIE и в FILES. 3. Анализ import_request_variables это не новость в уязвимостях: рассмотрим этот журнал изменений за 24 Ноября 2005, PHP 5.1. Используя нижеследующий тестовый комплект, запустите скрипт в перезаписываемый каталог внутри корневого каталога, потом через браузер перейдите к файлу test.php и попробуйте. testsuite.sh Рекомендуемые тесты: - test.php?_SERVER=string (перезаписать массив $_SERVER и сделать его строкой) - test.php?_SERVER[REMOTE_ADDR]=обход проверки IP клиента - test.php?_SERVER[HTTP_REFERER]=обход проверки реферала. и т.д. Уязвимость в этой строке: ./ext/standard/basic_functions.cHP_FUNCTION(import_request_variables) ./Zend/zend_hash.c:ZEND_API void zend_hash_apply_with_arguments(HashTable *ht, apply_func_args_t apply_func, int num_args, ...) Фрагмент уязвимого кода: PHP: PHP_FUNCTION(import_request_variables) { [..] if (prefix_len == 0) { php_error_docref(NULL TSRMLS_CC, E_NOTICE, "No prefix specified - possible security hazard"); } [..] for (p = types; p && *p; p++) { switch (*p) { case 'g': case 'G': zend_hash_apply_with_arguments(Z_ARRVAL_P(PG(http_globals)[TRACK_ VARS_GET]), (apply_func_args_t) copy_request_variable, 2, prefix, prefix_len);break; case 'p': case 'P': zend_hash_apply_with_arguments(Z_ARRVAL_P(PG(http_globals)[TRACK_ VARS_POST]), (apply_func_args_t) copy_request_variable, 2, prefix, prefix_len); zend_hash_apply_with_arguments(Z_ARRVAL_P(PG(http_globals)[TRACK_ VARS_FILES]), (apply_func_args_t) copy_request_variable, 2, prefix, prefix_len); break; case 'c': case 'C': zend_hash_apply_with_arguments(Z_ARRVAL_P(PG(http_globals)[TRACK_ VARS_COOKIE]), (apply_func_args_t) copy_request_variable, 2, prefix, prefix_len);break; } } [..] } Как видим есть разные точки входа, но "вывод" это глобальная область видимости. example.php PHP: <?php echo 'GLOBALS '.(int)ini_get("register_globals")."n"; import_request_variables('GPC'); if ($_SERVER['REMOTE_ADDR'] != '10.1.1.1') die('Go away!'); echo 'Hello admin!'; ?> curl http://URL/example.php?_SERVER[REMOTE_ADDR]=10.1.1.1 Выдаст вам: Hello admin! Дополнение (автор:Francesco 'ascii' Ongaro) import_request_variables() против extract() Пожалуйста, обратите внимание, что extract() также заменит любую переменную кроме $GLOBALS, но главное отличие в том, что http://it2.php.net/extract не рекомендуют нам использовать extract() против ненадежных данных, например $_GET, .... Фактически у extract() есть флажок EXTR_SKIP и если есть конфликт, существующая переменная не перезаписывается. Использование extract() с EXTR_SKIP даст нам что-то типа GLOBALS ON но безопаснее в сравнении с тем, что случится если использовать extract($_GET); или import_request_variables('G'); test1.php PHP: <?php extract($_GET); print_r($_SERVER); ?> Демо: test1.php?SERVER=abc Ожидаемый результат: массив _SERVER станет строкой. Мораль в том, что небезопасное использование extract() разработчиком будет его ошибкой, но нет надежного использования import_request_variables и это на 100% промах PHP. ------------------- Источники: http://security.nnov.ru/news/PHP/import_request_variables.html http://security.nnov.ru/Qdocument280.html http://security.nnov.ru/Qdocument289.html (c) NeMiNeM Специально для antichat.ru ps: В статье/переводе возможны ошибки. Просьба не кричать, а спокойно указать и исправить Спасибо.
Ребят пробовал я короче говоря эту тему но вот наткнулся на один косяк... PHP: <?php echo 'GLOBALS '.(int)ini_get("register_globals")."n"; import_request_variables('GPC'); if ($_SERVER['REMOTE_ADDR'] != '10.1.1.1') die('Go away!'); echo 'Hello admin!'; ?> вот это из примера... Я попробовал изменить вот так. PHP: <?php echo 'GLOBALS '.(int)ini_get("register_globals")."n "; import_request_variables('GPC','r_'); $tmp=$_SERVER['REMOTE_ADDR'] ; if ($tmp != '10.1.1.1') die('Go away!'); echo 'Hello admin!'; ?> я добавил в функцию import_request_variables('GPC','r_'); еще и префикс r_ и теперь у меня эта штука не работает при том что я обрашаюсь к скрипту test.php?r_tmp=10.1.1.1
2k1b0rg, пробовал вот так: PHP: <?php echo 'GLOBALS '.(int)ini_get("register_globals")."n "; import_request_variables('GPC','r_'); $tmp=$_SERVER['REMOTE_ADDR'] ; if ($tmp != '10.1.1.1') die('Go away!'); echo 'Hello admin!'; ?> обращался test.php?r_SERVER[REMOTE_ADDR]=10.1.1.1 и так пробовал: PHP: <?php echo 'GLOBALS '.(int)ini_get("register_globals")."n "; import_request_variables('GPC','r_'); if ($_SERVER['REMOTE_ADDR'] != '10.1.1.1') die('Go away!'); echo 'Hello admin!'; ?> обращался так же: test.php?r_SERVER[REMOTE_ADDR]=10.1.1.1 Ни у кого больше мыслей нет по сабжу?
Это одна из самых серьезных багов в php. Если уметь ими пользоваться то можно очень много уязвимостей найти... Это посерьезнее многих баг описанных на этом форуме...