Данная статья рассматривает способы подключения файлов в PHP скриптах, способы проверки данных, передаваемых в функции "include" и "require", возможные случаи передачи данных в эти функции. Так же рассматриваются возможности подключения файлов "сторонних" производителей. В конце рассматривается возможность подключения файла загруженного как аватар. Статья преследует цель показать методику поиска и анализа мест, потенциально уязвимых для PHP-инъекций. Она будет интересна людям, делающим первые шаги на пути поиска и эксплуатирования ошибок в PHP приложениях, давая возможность проследить за ходом поиска уязвимых мест в скриптах, также будет интересна и программистам, показывая на примерах
способы проверки данных и выявление неточности в логике построения проверок. Для тестиования выбран форум phpBB 2.0.21, и перед тем как читать дальше я рекомендую установить этот продукт.

Поискав require и include во всех исходниках форума я нашел подключения различных файлов. Много подключений однотипных, например подключается файл так "$phpbb_root_path . ‘includes/functions_validate.’.$phpEx", т.е. указание на директорию, которая в зависимости от расположения подключающего файла меняется, затем точное указание на файл, добавление к нему расширения.  Скорее всего $phpbb_root_path создана для удобной возможности перемещения файла из одного каталога в другой, и изменение этой переменной позволит легко переопределить пути подключения файла. Изменение этой переменной привело бы к возможности удаленного инклюда. $phpEx определяет расширение PHP скриптов, связано это с тем,
что WWW сервис определяет PHP скрипт по его расширению. На некоторых серверах возможны такие варианты как "php3","php5","phtml".

Итак, что же сделано для того чтобы никто не смог переписать эти переменные? В файлах, где переменная "$phpbb_root_path"и "$phpEx" не задается явно, стоит проверка определена ли константа IN_PHPBB, если нет, то скрипт умирает сообщая "Hacking attempt". В файлах,  где определяется эта константа, в самом начале определяются и эти две переменные $phpbb_root_path и $phpEx. Изменить эти переменные можно только если register_globals включен (по умолчанию он выключен), и программист забыл поставить проверку константы. Архитектурно неверно использовать подобные приемы, лучше всего жестко определять пути для подключаемых файлов, либо для определения пути использовать константы. Так же лучше
всего определить фиксированное расширение для подключаемых файлов и выделить для них отдельный каталог, закрытый, например, с помощью .htaccess, от передачи оттуда файлов . Хотя такие решения с переменными дают возможность использовать данное приложение на различных WWW сервисах.

Из друг на друга похожих икнлюдов выделяются и интересуют нас следующие:

в файле admin/admin_styles.php

$phpbb_root_path. "templates/" . basename($install_to) . "/theme_info.cfg"
$phpbb_root_path. "templates/" . $sub_dir . "/theme_info.cfg"
в файле index.php
'./' . $file
в файле faq.php
$phpbb_root_path . 'language/lang_' . $board_config['default_lang'] . '/'
. $lang_file . '.' . $phpEx

в файле includes/functions.php

function init_userprefs - $phpbb_root_path . 'language/lang_'
. $board_config['default_lang'] . '/lang_main.' . $phpEx
function init_userprefs - $phpbb_root_path . 'language/lang_'
. $board_config['default_lang'] . '/lang_admin.' . $phpEx
function setup_style - $phpbb_root_path . $template_path . $template_name
. '/'. $template_name . '.cfg'
function message_die - $phpbb_root_path . 'language/lang_'
. $board_config['default_lang'] . '/lang_main.'.$phpEx

Файл admin/admin_styles.php

include($phpbb_root_path. "templates/" . basename($install_to)
. "/theme_info.cfg");

Переменная install_to берется из запроса GET или POST в чистом виде, что может привести к передаче в эту переменную любой информации. Переменная обрабатывается функцией "basenamе()" и поэтому данные вида "/../../myScriptName" обрежутся до "myScriptName". Но можно передать строку "myScript\x0" и тогда из папки templates подключится файл "myScript" , либо передать ".." и тогда если в корне форума существует theme_info.cfg, он подключится. Если его нет, то будет ошибка.

Стоит добавить проверки, которые поставлены в рассматриваемом далее инклюде, он так же находится в этом файле

. include($phpbb_root_path. "templates/" . $sub_dir
. "/theme_info.cfg");

Расположенный вне проверок такой вызов функции позволил бы, передав в $sub_dir данные, запустить скрипт загруженный как аватар. В итоге полученная строка, переданная в include, выглядела бы так

./../templates/../images/avatars/shell.jpg\x0/theme_info.cfg

Но $sub_dir переменная, в которую поочередно передаются имена файлов и папок из каталога "./../templates/", значит возможность передать спецсимволы исключена.Дальше идет проверка того, что бы переменная не являлась файлом или ссылкой, не была бы равна ".", ".." и, как нестранно, "CVS". После такой проверки, $sub_dir однозначно будет каталогом. Последняя проверка устанавливает факт существования "theme_info.cfg". В итоге проникнуть можно либо создав свой каталог и поместив "theme_info.cfg", либо изменить существующий "theme_info.cfg". 

В исходнике admin/index.php был найден такой инклюд "include(‘./’ . $file);". Выглядит инклюд очень заманчиво, но передать в него любые данные не получится, поскольку в $file, передаются имена файлов и каталогов из "admin/". А затем происходит проверка preg_match("/^admin_.*?\." . $phpEx . "$/", $file). Рассмотри алгоритм проверки: первым проверяется наличие в начале строки admin_, дальше может идти любое значение, заканчиваться строка должна .php. Действительно, "^" означает начало строки в которой осуществляется поиск и за началом следует "admin_", отсутсвие утверждения о начале, приведет к тому, что "admin_" сможет находится везде, что приведет к подключению файла с именем "mySTRING_admin_.php".
"$" утверждает об окончании строки (или множества строк). На конце строки должен находится php. Без данного утверждения preg_match возвращал бы true даже в том случае, если имя файла было бы "admin_.php.mySTRING" . В принципе, существование директории "admin_.php" вызовет ошибку в работе скрипта. Можно было бы, добавить проверку на не директорию и не ссылку. Так как единственная возможность выполнить include с вредоносным кодом это записать этот файл в директорию "phpBB2/admin", то я думаю следует ограничить эту директорию правами только чтение и запуск.

Так же в "faq.php" замечен интересный инклюд

include($phpbb_root_path . 'language/lang_' . $board_config['default_lang']
. '/' . $lang_file . '.' . $phpEx);

$lang_file определятся однозначно либо как lang_bbcode, либо lang_faq . Остается переменная $board_config[‘default_lang’]. Нужно проверить существует ли возможность ее перезаписи.

В common.php так определяется $board_config:

$sql = "SELECT *
FROM " . CONFIG_TABLE;
if( !($result = $db->sql_query($sql)) )
{
message_die(CRITICAL_ERROR, "Could not query config information"
, "", __LINE__, __FILE__, $sql);
}

while ( $row = $db->sql_fetchrow($result) )
{
$board_config[$row['config_name']] = $row['config_value'];
}

То есть, если существует возможность SQL инъекции, то нападающий сможет изменить значение в таблице, тем самым установив значение $board_config[‘default_lang’], например, на файл аватара. Скрипт faq.php обламывает такие попытки функцией init_userprefs (includes/functions.php). В этой фунции есть проверка basename’ом. В связи с тем, что basename отсечет все попытки прорваться в другие каталоги с помощью конструкций вида /../../, то можно указать на файл находящийся в language и начинающийся lang_ и заканчивающийся "\x0".

Как говорилось выше в функции init_userprefs происходит вызов basename($board_config[‘default_lang’]), затем полученный результат передается в $default_lang, проверяется наличие файла для инклюда, если файл не существует, то $default_lang становится равным "english". А перед вызовом include, $board_config[‘default_lang’] приравнивается $default_lang. В функции setup_style есть вот такое подключение файла

($phpbb_root_path . $template_path
. $template_name . '/' . $template_name . '.cfg')

"$template_path" определяется как ‘templates/’ и с этим ничего нельзя сделать, но остается "$template_name". В "$template_name" передаются данные из SQL запроса $row[‘template_name’]. Значит при осуществлении SQL инъекции можно будет передать сюда все, что угодно. После инициализации переменной "$template_name" происходит вызов функции из класса Template, параметры, которые в неё передаются — ($phpbb_root_path . $template_path . $template_name). Затем проверяется значение, которое возвратила функция, а она возвращает ID объекта. И если посмотреть на функцию, то видно, что ей безразлично какие параметры передаются. А потому проверка расположенная перед инклюдом проходится спокойно.
Создайте файл hack.php в папке includes и поместите туда такой код:

<?
$phpbb_root_path = "./../";
$template_path = 'templates/';
$template_name = "../images/avatars/shell.jpg\x0";
include ("./template.php");
$template = new Template($phpbb_root_path . $template_path . $template_name);
if ( $template )
{
@include($phpbb_root_path . $template_path
. $template_name . '/' . $template_name . '.cfg');
}
?>

Видим, что оболочка, загруженная под видом аватара, запускается. Теперь найдем вызов функции "setup_style" в функции "init_userprefs", так как я использовал установленный без различных модификаций и стилей форум, то я перед вызовом setup_style($board_config[‘default_style’]) ставлю echo $board_config[‘default_style’]; и получаю 1. Просмотрев SQL запрос на получение данных составляем запрос, который должен быть осуществлен за счет SQL инъекции "UPDATE phpbb_themes SET template_name=/’../images/avatars/shell.jpg\x0′ WHERE themes_id = 1;

Инзменив таким образом $template_name мы получаем возможность загрузить вместо стиля по умолчанию наш скрипт, загруженный как аватар. Данная уязвимость работает (скорее всего) на всех версиях phpBB2 (я проверил на последней 2.0.21 и 2.0.10). Внеся такие изменения попробуйте теперь загрузить главную страницу и вы получите запуск "shell.jpg".

Осталась функция message_die

function message_die - $phpbb_root_path . 'language/lang_'
. $board_config['default_lang'] . '/lang_main.'.$phpEx

Известно, что $board_config[‘default_lang’] в функции не изменяется, что можно переписать эту переменную SQL инъекцией. Задача найти вызов функции, где перед ее вызовом не объявляется константа HEADER_INC и первым параметром не передается CRITICAL_ERROR, а так же не вызывается перед message_die функция init_userprefs.

И это уже задача, твоя, читатель

 

Оставить мнение

Check Also

Пространство для эксплуатации. Как работает новая RCE-уязвимость в Apache Struts 2

Во фреймворке Apache Struts 2, виновном в утечке данных у Equifax, нашли очередную дыру. О…