PHP Tricks
Last updated
Last updated
Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Отримайте перспективу хакера на ваші веб-додатки, мережу та хмару
Знайдіть та повідомте про критичні, експлуатовані вразливості з реальним бізнес-імпактом. Використовуйте наші 20+ спеціальних інструментів для картографування поверхні атаки, знаходження проблем безпеки, які дозволяють вам підвищити привілеї, та використовуйте автоматизовані експлойти для збору важливих доказів, перетворюючи вашу важку працю на переконливі звіти.
Це також стосується cookies phpMyAdmin.
Cookies:
Місця:
Якщо в PHP використовується ==
, то є несподівані випадки, коли порівняння не веде себе так, як очікується. Це пов'язано з тим, що "==" порівнює лише значення, перетворені в один і той же тип, якщо ви також хочете порівняти, що тип порівнюваних даних однаковий, вам потрібно використовувати ===
.
Таблиці порівняння PHP: https://www.php.net/manual/en/types.comparisons.php
"string" == 0 -> True
Рядок, який не починається з числа, дорівнює числу
"0xAAAA" == "43690" -> True
Рядки, що складаються з чисел у десятковому або шістнадцятковому форматі, можуть бути порівняні з іншими числами/рядками з результатом True, якщо числа були однаковими (числа в рядку інтерпретуються як числа)
"0e3264578" == 0 --> True
Рядок, що починається з "0e" і за яким слідує будь-що, буде дорівнювати 0
"0X3264578" == 0X --> True
Рядок, що починається з "0" і за яким слідує будь-яка літера (X може бути будь-якою літерою) і за яким слідує будь-що, буде дорівнювати 0
"0e12334" == "0" --> True
Це дуже цікаво, оскільки в деяких випадках ви можете контролювати вхідний рядок "0" і деякий вміст, який хешується і порівнюється з ним. Тому, якщо ви можете надати значення, яке створить хеш, що починається з "0e" і без будь-якої літери, ви могли б обійти порівняння. Ви можете знайти вже захешовані рядки з цим форматом тут: https://github.com/spaze/hashes
"X" == 0 --> True
Будь-яка літера в рядку дорівнює int 0
Більше інформації на https://medium.com/swlh/php-type-juggling-vulnerabilities-3e28c4ed5c09
Типове перетворення також впливає на функцію in_array()
за замовчуванням (вам потрібно встановити третій аргумент в true, щоб зробити строгий порівняння):
Якщо ця функція використовується для будь-якої перевірки автентифікації (наприклад, перевірка пароля) і користувач контролює одну сторону порівняння, він може надіслати порожній масив замість рядка як значення пароля (https://example.com/login.php/?username=admin&password[]=
) і обійти цю перевірку:
Та ж помилка виникає з strcasecmp()
Навіть якщо ===
використовується, можуть виникати помилки, які роблять порівняння вразливим до приведення типів. Наприклад, якщо порівняння перетворює дані в інший тип об'єкта перед порівнянням:
preg_match()
може бути використано для перевірки введення користувача (він перевіряє, чи є будь-яке слово/регулярний вираз з чорного списку присутнім у введенні користувача, і якщо його немає, код може продовжити своє виконання).
Однак, при обмеженні початку регулярного виразу preg_match()
перевіряє лише перший рядок введення користувача, тому, якщо якимось чином ви зможете надіслати введення в кількох рядках, ви зможете обійти цю перевірку. Приклад:
Щоб обійти цю перевірку, ви можете надіслати значення з новими рядками, закодованими в URL (%0A
), або якщо ви можете надіслати JSON-дані, надішліть їх у кількох рядках:
Знайдіть приклад тут: https://ramadistra.dev/fbctf-2019-rceservice
(Цей обхід, очевидно, був спробований на PHP 5.2.5, і мені не вдалося змусити його працювати на PHP 7.3.15)
Якщо ви зможете надіслати preg_match()
дійсний дуже великий вхід, він не зможе його обробити і ви зможете обійти перевірку. Наприклад, якщо він блокує JSON, ви можете надіслати:
From: https://medium.com/bugbountywriteup/solving-each-and-every-fb-ctf-challenge-part-1-4bce03e2ecb0
Трюк з: https://simones-organization-4.gitbook.io/hackbook-of-a-hacker/ctf-writeups/intigriti-challenges/1223 та https://mizu.re/post/pong
Коротко кажучи, проблема виникає через те, що функції preg_*
у PHP базуються на бібліотеці PCRE. У PCRE певні регулярні вирази співпадають, використовуючи багато рекурсивних викликів, що займає багато стекового простору. Можливо встановити обмеження на кількість дозволених рекурсій, але в PHP це обмеження за замовчуванням становить 100.000, що більше, ніж вміщується в стек.
Ця тема на Stackoverflow також була згадана в пості, де детальніше обговорюється ця проблема. Наше завдання стало зрозумілим:
Надіслати вхідні дані, які змусили б regex виконати 100_000+ рекурсій, викликавши SIGSEGV, змусивши функцію preg_match()
повернути false
, таким чином змусивши додаток вважати, що наш вхід не є шкідливим, підкидаючи сюрприз в кінці корисного навантаження щось на кшталт {system(<verybadcommand>)}
для отримання SSTI --> RCE --> flag :).
Отже, в термінах regex, ми насправді не виконуємо 100k "рекурсій", а замість цього ми рахуємо "кроки назад", які, як зазначає документація PHP, за замовчуванням становлять 1_000_000 (1M) у змінній pcre.backtrack_limit
.\ Щоб досягти цього, 'X'*500_001
призведе до 1 мільйона кроків назад (500k вперед і 500k назад):
Якщо PHP перенаправляє на іншу сторінку, але жодна функція die
або exit
не викликається після того, як заголовок Location
встановлено, PHP продовжує виконання та додає дані до тіла:
Перевірте:
File Inclusion/Path traversalregister_globals: У PHP < 4.1.1.1 або якщо неправильно налаштовано, register_globals може бути активним (або їх поведінка імітується). Це означає, що в глобальних змінних, таких як $_GET, якщо вони мають значення, наприклад, $_GET["param"]="1234", ви можете отримати доступ до них через $param. Таким чином, відправляючи HTTP параметри, ви можете перезаписати змінні, які використовуються в коді.
PHPSESSION куки одного домену зберігаються в одному місці, тому якщо в межах домену використовуються різні куки в різних шляхах, ви можете зробити так, щоб шлях отримував доступ до куки іншого шляху, встановивши значення куки іншого шляху. Таким чином, якщо обидва шляхи отримують доступ до змінної з однаковою назвою, ви можете зробити так, щоб значення цієї змінної в path1 застосовувалося до path2. І тоді path2 вважатиме змінні path1 дійсними (надаючи куки ім'я, яке відповідає йому в path2).
Коли у вас є імена користувачів користувачів машини. Перевірте адресу: /~<USERNAME>, щоб дізнатися, чи активовані php каталоги.
Ці функції зазвичай використовуються в PHP для генерації хешів з паролів та для перевірки, чи є пароль правильним у порівнянні з хешем.
Підтримувані алгоритми: PASSWORD_DEFAULT
та PASSWORD_BCRYPT
(починається з $2y$
). Зверніть увагу, що PASSWORD_DEFAULT часто є тим же, що і PASSWORD_BCRYPT. І наразі PASSWORD_BCRYPT має обмеження на розмір вхідних даних у 72 байти. Тому, коли ви намагаєтеся захешувати щось більше ніж 72 байти за допомогою цього алгоритму, будуть використані лише перші 72B:
З цього треду в твіттері ви можете побачити, що надсилаючи більше ніж 1000 GET параметрів або 1000 POST параметрів або 20 файлів, PHP не буде встановлювати заголовки у відповіді.
Це дозволяє обійти, наприклад, заголовки CSP, які встановлюються в кодах, таких як:
Якщо PHP-сторінка виводить помилки та відображає деякі дані, надані користувачем, користувач може змусити PHP-сервер вивести деякий контент достатньої довжини, так що коли він намагається додати заголовки до відповіді, сервер видасть помилку. У наступному сценарії зловмисник змусив сервер видати великі помилки, і, як ви можете бачити на екрані, коли PHP намагався змінити інформацію заголовка, він не зміг (тому, наприклад, заголовок CSP не був надісланий користувачу):
Перевірте сторінку:
PHP SSRFsystem("ls"); `ls`; shell_exec("ls");
Перевірте це для більш корисних функцій PHP
Щоб виконати код у аргументі "replace", потрібне принаймні одне співпадіння. Ця опція preg_replace була застаріла з PHP 5.5.0.
Ця функція в php дозволяє вам виконувати код, написаний у рядку, щоб повернути true або false (і в залежності від цього змінити виконання). Зазвичай змінна користувача буде вставлена в середину рядка. Наприклад:
assert("strpos($_GET['page']),'..') === false")
--> У цьому випадку, щоб отримати RCE, ви могли б зробити:
Вам потрібно зламати синтаксис коду, додати ваш payload, а потім виправити його знову. Ви можете використовувати логічні операції такі як "and" або "%26%26" або "|". Зверніть увагу, що "or", "||" не працює, оскільки якщо перша умова істинна, наш payload не буде виконано. Так само ";" не працює, оскільки наш payload не буде виконано.
Інша опція - додати до рядка виконання команди: '.highlight_file('.passwd').'
Інша опція (якщо у вас є внутрішній код) - змінити деяку змінну, щоб змінити виконання: $file = "hola"
Ця функція використовується для сортування масиву елементів за допомогою конкретної функції. Щоб зловживати цією функцією:
Ви також можете використовувати // для коментування решти коду.
Щоб виявити кількість дужок, які потрібно закрити:
?order=id;}//
: ми отримуємо повідомлення про помилку (Parse error: syntax error, unexpected ';'
). Можливо, нам не вистачає однієї або кількох дужок.
?order=id);}//
: ми отримуємо попередження. Це, здається, правильно.
?order=id));}//
: ми отримуємо повідомлення про помилку (Parse error: syntax error, unexpected ')' i
). Можливо, у нас занадто багато закриваючих дужок.
Якщо ви можете завантажити .htaccess, то ви можете налаштувати кілька речей і навіть виконати код (налаштувавши, щоб файли з розширенням .htaccess могли бути виконані).
Різні оболонки .htaccess можна знайти тут
Якщо ви знайдете вразливість, яка дозволяє вам модифікувати змінні середовища в PHP (і ще одну для завантаження файлів, хоча з більшою дослідженням це може бути обійдено), ви могли б зловживати цією поведінкою, щоб отримати RCE.
LD_PRELOAD
: Ця змінна середовища дозволяє вам завантажувати довільні бібліотеки під час виконання інших бінарних файлів (хоча в цьому випадку це може не спрацювати).
PHPRC
: Інструктує PHP, де знайти свій конфігураційний файл, зазвичай називається php.ini
. Якщо ви можете завантажити свій власний конфігураційний файл, то використовуйте PHPRC
, щоб вказати PHP на нього. Додайте запис auto_prepend_file
, що вказує на другий завантажений файл. Цей другий файл містить звичайний PHP код, який потім виконується PHP-інтерпретатором перед будь-яким іншим кодом.
Завантажте PHP файл, що містить наш shellcode
Завантажте другий файл, що містить директиву auto_prepend_file
, інструктуючи PHP-препроцесор виконати файл, який ми завантажили на етапі 1
Встановіть змінну PHPRC
на файл, який ми завантажили на етапі 2.
Отримайте більше інформації про те, як виконати цей ланцюг з оригінального звіту.
PHPRC - ще один варіант
Якщо ви не можете завантажити файли, ви можете використовувати в FreeBSD "файл" /dev/fd/0
, який містить stdin
, будучи тілом запиту, надісланого до stdin
:
curl "http://10.12.72.1/?PHPRC=/dev/fd/0" --data-binary 'auto_prepend_file="/etc/passwd"'
Або, щоб отримати RCE, увімкніть allow_url_include
і додайте файл з base64 PHP кодом:
curl "http://10.12.72.1/?PHPRC=/dev/fd/0" --data-binary $'allow_url_include=1\nauto_prepend_file="data://text/plain;base64,PD8KICAgcGhwaW5mbygpOwo/Pg=="'
Техніка з цього звіту.
Веб-сервер аналізує HTTP запити та передає їх PHP скрипту, виконуючи запит, такий як http://host/cgi.php?foo=bar
як php.exe cgi.php foo=bar
, що дозволяє ін'єкцію параметрів. Це дозволить ін'єктувати наступні параметри для завантаження PHP коду з тіла:
Крім того, можливо ввести параметр "-" за допомогою символу 0xAD через подальшу нормалізацію PHP. Перевірте приклад експлуатації з цього посту:
У цьому пості можна знайти чудові ідеї для генерації brain fuck PHP коду з дуже обмеженою кількістю дозволених символів. Більше того, також пропонується цікавий спосіб виконання функцій, які дозволили їм обійти кілька перевірок:
Подивіться, чи можете ви вставити код у виклики цих функцій (з тут):
Якщо ви налагоджуєте PHP-додаток, ви можете глобально увімкнути друк помилок у /etc/php5/apache2/php.ini
, додавши display_errors = On
, і перезапустити apache: sudo systemctl restart apache2
Ви можете використовувати web www.unphp.net для деобфускації php коду.
PHP обгортки та протоколи можуть дозволити вам обійти захисти на запис і читання в системі та скомпрометувати її. Для додаткової інформації перегляньте цю сторінку.
Якщо ви бачите, що Xdebug увімкнено в phpconfig()
виводі, вам слід спробувати отримати RCE через https://github.com/nqxcode/xdebug-exploit
Якщо на сторінці ви можете створити новий об'єкт довільного класу, ви можете отримати RCE, перевірте наступну сторінку, щоб дізнатися як:
PHP - RCE abusing object creation: new $_GET["a"]($_GET["b"])https://securityonline.info/bypass-waf-php-webshell-without-numbers-letters/
Згідно з цією статтею можливо згенерувати простий shellcode таким чином:
Отже, якщо ви можете виконувати довільний PHP без цифр і літер, ви можете надіслати запит, як наведено нижче, зловживаючи цим корисним навантаженням для виконання довільного PHP:
Для більш детального пояснення перегляньте https://ctf-wiki.org/web/php/php/#preg_match
Отримайте перспективу хакера щодо ваших веб-додатків, мережі та хмари
Знайдіть і повідомте про критичні, експлуатовані вразливості з реальним бізнес-імпактом. Використовуйте наші 20+ спеціальних інструментів для картографування поверхні атаки, знаходження проблем безпеки, які дозволяють вам підвищити привілеї, і використовуйте автоматизовані експлойти для збору важливих доказів, перетворюючи вашу важку працю на переконливі звіти.
Вчіться та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Вчіться та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)