## https://sploitus.com/exploit?id=975D545B-C809-541F-8CDD-3A25A1C7E8DD
# NOISY_ECHIDNA β Race Condition CTF
This repository implements a small, intentional-toy web challenge that demonstrates a Time-of-CheckβTime-of-Use (TOCTOU) race condition in a funds transfer API. The goal: build, run, and exploit the race window to achieve a negative balance and recover the flag.
---
## Quick facts
* **Category:** Web / API (TOCTOU race condition)
* **Primary language:** Python (Flask)
* **Flag:** `FLAG{echidnas_dig_fast_and_race_conditions_execute_faster}`
* **Ports:** 5000 (default)
---
## Contents
* `app.py` β Flask application (vulnerable transfer endpoint)
* `exploit.py` β simple threaded exploit that demonstrates the issue
* `Dockerfile`, `docker-compose.yml` β container configuration for easy testing
* `requirements.txt` β Python dependencies
* `README.md`, `BUILD.md`, `WALKTHROUGH.md` β documentation (this file consolidates and cleans up)
* `templates/index.html` β small landing page used by the app
---
## Quick start (Docker β recommended)
1. From the project root:
```bash
docker-compose up --build
```
2. Open the app in your browser: `http://localhost:5000`
3. Run the exploit (from another shell):
```bash
python exploit.py
```
4. When finished:
```bash
docker-compose down
```
---
## Quick start (Local Python)
1. Create and activate a virtualenv (recommended):
```bash
python -m venv venv
source venv/bin/activate # Linux/macOS
venv\Scripts\activate # Windows (PowerShell)
```
2. Install dependencies:
```bash
pip install -r requirements.txt
```
3. Run the app:
```bash
python app.py
```
4. Run the exploit in a second terminal:
```bash
python exploit.py
```
---
## Expected behaviour and outputs
* `GET /api/account/` returns a small JSON object for that account (username, name, balance, account number).
* `POST /api/transfer` performs a balance check then deducts funds. The app intentionally inserts a short sleep to create a race window.
* When the race is exploited the server will return the flag in the transfer response and `GET /api/stats` will report an account with a negative balance.
Example (successful exploit):
```json
{
"status": "success",
"message": "π― EXPLOIT SUCCESSFUL! Negative balance achieved! FLAG{...}",
"transaction_id": 5,
"from_balance": -20.0,
"to_balance": 170.0,
"flag": "FLAG{echidnas_dig_fast_and_race_conditions_execute_faster}"
}
```
---
## How the vulnerability works (short)
1. Server checks account balance (Time-of-Check).
2. The code then waits for a short time (`time.sleep(0.01)` by default).
3. The balance is deducted (Time-of-Use) without re-checking or using a lock.
Multiple concurrent requests can all pass the balance check before any deduction occurs, allowing the sum of successful transfers to exceed the original balance.
---
## Tuning the challenge
* To make exploitation easier or harder, modify the artificial delay in `app.py`:
```python
# default: 0.01 (10 ms)
time.sleep(0.01)
```
* Adjust starting balances in `app.py` under the `accounts` dictionary.
* Adjust the exploit parameters in `exploit.py`: `TRANSFER_AMOUNT` and `NUM_THREADS`.
---
## Commands & debugging
Check the service:
```bash
curl http://localhost:5000/api/account/dr_tachyglossus
```
Reset accounts:
```bash
curl -X POST http://localhost:5000/api/reset
```
View transactions:
```bash
curl http://localhost:5000/api/transactions
```
Check stats:
```bash
curl http://localhost:5000/api/stats
```
Docker troubleshooting:
```bash
# container status
docker ps
# logs
docker-compose logs --tail=200
# port conflicts
lsof -i :5000 # Linux/macOS
netstat -ano | findstr :5000 # Windows
```
---
## Short developer notes (for reviewers)
* The app uses an in-memory datastore (resets on restart) β this keeps testing deterministic for a CTF.
* The vulnerability is intentional for training purposes; do not deploy this code in production.
* A simple fix is to perform the balance check and deduction under a lock or use database transactions with row-level locking.
Suggested minimal fix:
```python
with balance_lock:
if accounts[from_user]["balance"] < amount:
return jsonify({"error": "Insufficient funds"}), 400
accounts[from_user]["balance"] -= amount
accounts[to_user]["balance"] += amount
```
---
## Submission checklist
* `app.py`
* `exploit.py`
* `Dockerfile` and `docker-compose.yml`
* `requirements.txt`
* `templates/index.html`
* `README.md` (short), `BUILD.md` (build steps), `WALKTHROUGH.md` (detailed solve steps)
---
## Contact / Author
Challenge author: Kirk
If anything is unclear or a command fails, mention the failing command and include logs β that makes debugging quick.