├── README.md
├── docker-compose.yml
├── docker.mysql.yml
├── docker.php.yml
├── payhack.sql
├── php
├── poc.php
└── test.php
└── race.yaml
/README.md:
--------------------------------------------------------------------------------
1 | # Vulnerable PHP App (Race Condition)
2 |
3 |
4 | ## Environment setup:
5 |
6 | ```
7 | docker-compose up
8 | ```
9 |
10 | ## Environment verification:
11 |
12 | Connection Test:
13 | http://localhost/test.php
14 |
15 | Vulnerable endpoint:
16 | http://localhost/poc.php
17 |
18 | ## Race condition exploit:
19 |
20 | ```
21 | echo http://localhost | nuclei -t race.yml
22 | ```
23 |
24 | Observe that less than 1280$ were accounted for withdraw from the balance (10$ * 128 requests) with a variable net gain.
25 |
26 |
27 | #### Reference: https://defuse.ca/race-conditions-in-web-applications.htm
28 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 |
3 | services:
4 | mysql:
5 | build:
6 | context: .
7 | dockerfile: docker.mysql.yml
8 | container_name: php_mysql
9 | environment:
10 | MYSQL_ROOT_PASSWORD: payhack
11 | MYSQL_DATABASE: payhack
12 | MYSQL_USER: payhack
13 | MYSQL_PASSWORD: payhack
14 | ports:
15 | - "3306:3306"
16 | web:
17 | build:
18 | context: .
19 | dockerfile: docker.php.yml
20 | container_name: php_web
21 | depends_on:
22 | - mysql
23 | links:
24 | - mysql
25 | ports:
26 | - "80:80"
27 | stdin_open: true
28 | tty: true
--------------------------------------------------------------------------------
/docker.mysql.yml:
--------------------------------------------------------------------------------
1 | FROM mariadb:10.5.8
2 |
3 | ADD payhack.sql /docker-entrypoint-initdb.d
--------------------------------------------------------------------------------
/docker.php.yml:
--------------------------------------------------------------------------------
1 | FROM php:7.2-apache
2 | COPY php/ /var/www/html
3 | RUN docker-php-ext-install pdo pdo_mysql
4 | EXPOSE 80
--------------------------------------------------------------------------------
/payhack.sql:
--------------------------------------------------------------------------------
1 | USE payhack;
2 |
3 | CREATE TABLE IF NOT EXISTS moneyz (
4 | balance int(11) NOT NULL
5 | );
6 |
7 | INSERT INTO moneyz (balance) VALUES (10000);
8 |
--------------------------------------------------------------------------------
/php/poc.php:
--------------------------------------------------------------------------------
1 | true)
8 | );
9 |
10 | if(isset($_POST['restore']))
11 | setBalance(10000);
12 |
13 | if(isset($_GET['wd']))
14 | withdraw($_GET['wd']);
15 |
16 | echo "Current balance: " . getBalance();
17 |
18 | function withdraw($amount)
19 | {
20 | $balance = getBalance();
21 | if($amount <= $balance)
22 | {
23 | $balance = $balance - $amount;
24 | echo "You have withdrawn: $amount
";
25 | setBalance($balance);
26 | }
27 | else
28 | {
29 | echo "Insufficient funds.";
30 | }
31 | }
32 |
33 | /*
34 | * Use this to experiment with adding extra processing time to the withdraw
35 | * process.
36 | */
37 | function doWork()
38 | {
39 | for($i = 0; $i < 1000; $i++)
40 | $x *= $x;
41 | }
42 |
43 | function setBalance($x)
44 | {
45 | global $DB;
46 | $q = $DB->prepare("UPDATE `moneyz` SET balance=:x WHERE 1=1");
47 | $q->bindParam(':x', $x);
48 | $q->execute();
49 | }
50 |
51 | function getBalance()
52 | {
53 | global $DB;
54 | $q = $DB->prepare("SELECT balance FROM `moneyz` WHERE 1=1");
55 | $q->execute();
56 | return (int)$q->fetchColumn();
57 | }
58 | ?>
59 |
60 |