Як врапперов php можуть бути використані для атаки на веб-додатки
Гнучкість мови програмування додає зручності розробникам, а й відкриває нові вектори для атаки. Розробники РНР часто використовують так звані wrapper'и і навіть не підозрюють, що це може призвести до уникнення вбудованих в додаток фільтрів безпеки і, наприклад, дозволити виконати на сервері довільний код. Про врапперов, їх особливості та загрози, з ними пов'язаних, і піде сьогодні мова.
Уразливості, пов'язані з реалізованим в PHP механізмом врапперов, обговорюються досить давно. Посилання на них присутні в OWASP TOP 10 та WASC TCv2. Однак ряд особливостей реалізації кодування даних призводить до того, що навіть додатки, розроблені з урахуванням вимог безпеки, можуть містити уразливості (включаючи критичні). У цій статті ми спочатку коротко розглянемо, що представляють собою PHP wrappers і як вони можуть бути корисні програмістам. Потім розберемо їх особливості, які дозволяють обходити вбудовані в додаток фільтри безпеки і реалізовувати атаки, пов'язані з несанкціонованим доступом до файлової системи і виконанням довільного коду.
Wrapper'и
де в якості $ filename може бути використаний шлях до локального файлу. Добре відомо, що отримати вміст локальних файлів можна так:
Але крім тривіального шляхи до файлу можуть бути використані так звані врапперов (wrapper). Кращий спосіб пояснити, що це таке, - навести кілька прикладів. Отже, з використанням врапперов через все ту ж функцію fopen стає можливим:
Врапперов (вони ж обробники протоколу або обгортки) вказують функцій, яким чином обробляти дані з потоку. Тому функції, що підтримують врапперов, можуть бути використані для отримання даних з різних джерел. Врапперов дозволяють гнучко і зручно обробляти дані, що надходять в програму через який-небудь потік, а також модифікувати їх при необхідності.

Аналогічним чином можна використовувати функцію file_put_contents і будь-яку іншу функцію, яка підтримує врапперов в режимі write:
У версії 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: // можна безпосередньо звертатися до файлів всередині архіву.
Можливість поміщати в архів файли, в іменах яких присутня слеш, дозволяє експлуатувати уразливості типу Remote File Include, при відсутності null-байта. Для прикладу розглянемо наступний простий скрипт:
Для початку припустимо, що є можливість створювати на атакується сервері файли. Тоді, створивши zip-архів, як показано в прикладі вище, можливо виконати PHP-код, використовуючи врапперов zip: //.
Якщо немає можливості створити потрібний файл за допомогою PHP-функції, то можна використовувати тимчасові файли, які створює PHP при завантаженні контенту через HTML-форму. Шлях до тимчасового файлу можна дізнатися з phpinfo (). Більш докладні відомості про те, як використовувати тимчасові файли при експлуатації вразливостей типу LFI / RFI, можна почерпнути на форумі rdot.org. Важливо відзначити, що директива allow_url_fopen не обмежує застосування обгортки zip: //.
Where is my data: //?
Врапперов data: // з моменту своєї появи привертав увагу фахівців з веб-безпеки. В офіційній документації цей врапперов пропонують використовувати в дуже обмеженій формі. Але згідно специфікації RFC 2379, ця обгортка допускає більш розгорнутий синтаксис:
При цьому mediatype може або повністю відсутні, або бути заповнений довільними значеннями:
Цю особливість врапперов можна використовувати для обходу перевірок і фільтрів. Наприклад, в популярному скрипті TimThumb v1.x є такий фільтр:
Обійти цю перевірку можна наступним чином:
У PHP існує така функція, як stream_get_meta_data (). Згідно офіційній документації, вона витягує метадані з потоків і файлових покажчиків:
При цьому в повернутому масиві містяться елементи з чітко заданими ключами, і завдання додавання в цей масив нових елементів виглядає на перший погляд досить проблематичною. Але за допомогою врапперов data: // можна досить просто маніпулювати цим масивом! Як? Наведу приклад:

Якщо в змінній $ file замість імені локального файлу використовувати врапперов data,
холодний компрес
Згідно з документацією, обгортка compress.zlib: // дозволяє розпаковувати gz-архіви. Якщо за допомогою цього врапперов обробляти дані, які не є zlib-архівом, то дані повертаються без змін.
Наприклад, прочитати файл / etc / hosts можна таким чином:
«Дуже корисно!» - подумаєш ти :). Зараз буде крутіше. Якщо ти хоч трохи програмував на PHP для вебу, то напевно знайомий з функцією prase_url (). Нагадаю, ця функція здійснює парсинг URL. І тут є один цікавий момент: на вхід функції можна надати не тільки URL, але і рядок досить загального типу:
З огляду на цю особливість, можна обходити різні перевірки і фільтри на основі функції parse_url, використовуючи багатофункціональні врапперов. Для прикладу розглянемо наступний скрипт, який, за задумом розробників, може завантажувати файли тільки з довіреної хоста img.youtube.com.
У штатному режимі превью з img.youtube.com завантажуються в такий спосіб:
У цьому випадку фільтр можна обійти і з допомогою врапперов compress.zlib: //.
Крім цього, досить просто обійти фільтр на ім'я хоста і завантажити на сервер файл з довільним ім'ям і вмістом за допомогою раніше розглянутого нами врапперов data: //:
В цьому випадку локальні файли будуть копіюватися в папку з превью: якщо ця папка доступна для прямого звернення з браузера, то з'являється можливість переглядати системні файли. З цього прикладу видно, що використання врапперов data: // і compress.zlib: // може бути корисним в скриптах, які скачують файли з віддалених хостів. Одним з таких скриптів є TimThumb.

Експлуатація вразливостей в TimThumb v1.x
Суть уразливості полягала в тому, що скрипт некоректно проводив перевірку URL за списком довірених хостів, з яких можливо було завантажити зображення. Для обходу фільтрів, наприклад по довіреній хосту blogger.com, пропонувалося зареєструвати домен четвертого рівня, що містить в собі URL довіреної хоста, наприклад blogger.com.attacker.com, і завантажувати файли з цього домену.
Цим способом можна було проексплуатувати вразливість до версії 1.32 (revision 142). Але більш нові версії виявилися також уразливі. Розглянемо, яким чином відбувається завантаження зображень у версії 1.34 (revision 145):
Нескладно помітити, що при проектуванні функції check_external було допущено кілька логічних помилок:
- Після виконання більшості перевірок в функцію parse_str потрапляють нефільтровані призначені для користувача дані. Таким чином, можна перевизначити змінні, які до цього перевірялися: $ url_info [ 'host'], $ src, $ local_filepath. Тому можливо завантажувати файли з будь-яких серверів.
- Після завантаження файлу на сервер на основі getimagesize перевіряється, чи є файл зображенням. Якщо перевірка не пройдена, то файл видаляється. Але так як є можливість впливати на змінну $ local_filepath, то до локального файлу можна звертатися, використовуючи врапперов php: // filter, compress.zlib: //. А в цьому випадку функція unlink не зможе видалити файл.
Трохи покопавшись, я написав експлойт для завантаження файлів. З довільним ім'ям і з довільним вмістом, в довільне місце системи.
Гілка 1.х закінчується 149-й ревізією, в якій теж є уразливості. У цій ревізії вже прибрана функція parse_str і тому немає можливості провести перезапис змінних. Але фільтри, перевіряючі валідність URL, перевіряють тільки входження відповідних подстрок в рядку $ src. При цьому якщо функція curl_init недоступна на атакується сервері, то завантаження файлів здійснюється за допомогою file_get_contents / file_put_contents. Важливо відзначити, що ці функції, на відміну від curl_init, підтримують всі доступні в PHP врапперов.
Таким чином, за допомогою врапперов data: // можна обійти всі фільтри і створити файл в директорії кеша з довільним вмістом:
Або за допомогою врапперов compress.zlib: // скопіювати в кеш локальний файл:
Профіт в тому, що до файлів з кеша можна звертатися безпосередньо, в результаті чого домогтися RCE через запис шелла за допомогою врапперов data, а також отримати вміст локальних файлів, використовуючи compress.zlib.
замість висновку
Очевидно, що вбудовані в PHP врапперов дають великі можливості при експлуатації вразливостей типу File Manipulation. Але при цьому варто відзначити, що навіть найпростіші перевірки на основі функцій file_exists, is_file, filesize не дадуть скористатися врапперов. Також при встановленому патчі Suhosin за замовчуванням неможливо використовувати врапперов в інклуд, навіть якщо директива allow_url_include має значення On. На цьому я не закриваю тему використання врапперов і в наступній статті розповім про можливості врапперов php: // filter на прикладах експлуатації вразливостей в популярних веб-двигунах. Stay tuned!
Покажи цю статтю друзям: