Most of these issues come from not using Transactions.
Let's check this piece of code from mmcfe from Greedi's git.
if($isValidAddress){
//Subtract TX feee
$currentBalance = $currentBalance - 0.1;
//Send money//
if($bitcoinController->sendtoaddress($paymentAddress, $currentBalance)) {
$paid = 0;
$result = mysql_query("SELECT IFNULL(paid,'0') as paid FROM accountBalance WHERE userId=".$userId);
if ($resultrow = mysql_fetch_object($result)) $paid = $resultrow->paid + $currentBalance;
//Reduce balance amount to zero & make a ledger entry
mysql_query("UPDATE `accountBalance` SET balance = '0', paid = '".$paid."' WHERE `userId` = '".$userId."'");
mysql_query("INSERT INTO ledger (userId, transType, amount, sendAddress) ".
" VALUES ".
"('$userId', 'Debit_MP', '$currentBalance', '$paymentAddress')");
$goodMessage = "You have successfully sent ".$currentBalance." to the following address:".$paymentAddress;
//Set new variables so it appears on the page flawlessly
$currentBalance = 0;
}else{
$returnError = "Commodity failed to send. Contact site support immediately.";
}
}else{
$returnError = "Invalid or missing Litecoin payment address.";
Let's look at this bit right here
if($bitcoinController->sendtoaddress($paymentAddress, $currentBalance)) {
$paid = 0;
$result = mysql_query("SELECT IFNULL(paid,'0') as paid FROM accountBalance WHERE userId=".$userId);
if ($resultrow = mysql_fetch_object($result)) $paid = $resultrow->paid + $currentBalance;
//Reduce balance amount to zero & make a ledger entry
mysql_query("UPDATE `accountBalance` SET balance = '0', paid = '".$paid."' WHERE `userId` = '".$userId."'");
mysql_query("INSERT INTO ledger (userId, transType, amount, sendAddress) ".
" VALUES ".
"('$userId', 'Debit_MP', '$currentBalance', '$paymentAddress')");
So we see that our money gets sent before the balance is deducted. Depending on how PHP works, an attacker could execute a timely(albeit possibly very hard) attack and prevent
this code from executing
//Reduce balance amount to zero & make a ledger entry
mysql_query("UPDATE `accountBalance` SET balance = '0', paid = '".$paid."' WHERE `userId` = '".$userId."'");
mysql_query("INSERT INTO ledger (userId, transType, amount, sendAddress) ".
" VALUES ".
"('$userId', 'Debit_MP', '$currentBalance', '$paymentAddress')");
Balance never gets deducted, attacker still has coins, depending on how often this attack can be done, a user can simply milk the pool till it's dry.
A simple solution to fix this would be to use Transactions, and instead of instantly querying bitcoin to send the coins, create a payment_queue in MySQL that a cronjob checks every few seconds and executes a few sendmany commands.
Funny note, prior to changing my nick to the current one, it was
mcfe which I coined back in 2007ish waay before all this happened.