Race Condition Exploit in Starbucks Gift Cards
A researcher was able to steal money from Starbucks by exploiting a race condition in its gift card value-transfer protocol. Basically, by initiating two identical web transfers at once, he was able to trick the system into recording them both. Normally, you could take a $5 gift card and move that money to another $5 gift card, leaving you with an empty gift card and a $10 gift card. He was able to duplicate the transfer, giving him an empty gift card and a $15 gift card.
Race-condition attacks are unreliable and it took him a bunch of tries to get it right, but there’s no reason to believe that he couldn’t have kept doing this forever.
Unfortunately, there was really no one at Starbucks he could tell this to:
The hardest part—responsible disclosure. Support guy honestly answered there’s absolutely no way to get in touch with technical department and he’s sorry I feel this way. Emailing InformationSecurityServices@starbucks.com on March 23 was futile (and it only was answered on Apr 29). After trying really hard to find anyone who cares, I managed to get this bug fixed in like 10 days.
The unpleasant part is a guy from Starbucks calling me with nothing like “thanks” but mentioning “fraud” and “malicious actions” instead. Sweet!
A little more from BBC News:
A spokeswoman for Starbucks told BBC News: “After this individual reported he was able to commit fraudulent activity against Starbucks, we put safeguards in place to prevent replication.”
The company did not answer questions about its response to Mr Homakov.
More info.
Anura • May 26, 2015 5:26 PM
Easily avoided through proper use of database transactions. My experience is that too many developers prefer to avoid multi-statement transactions because they lead to problems they don’t know how to deal with. For example, instead of figuring out how to deal with deadlocks, they execute each statement as a different transaction.
In Microsoft SQL Server, developers are so used to using NOLOCK on all of their selects, that they even do it inside those transactions. Oracle and PostgreSQL don’t block on reads and instead maintain snapshots, so if one transaction is in progress and updates a row in a table and you attempt to read the data from that row in another, it won’t block but instead give the old data. This is an optional feature in SQL Server, but it can be worse in some ways, and both can lead to the problem seen above.
When doing things like placing orders, when you are really concerned about things like making sure inventory is properly tracked or gift card balance is adhered to, you need to create the order in its entirely as a single transaction using either Repeatable Read (if transaction A reads a row, then transaction B cannot modify that row until transaction A completes) or Serializable (if transaction A reads a range of rows, transaction B cannot modify those rows or insert a row that would fall into that range until transaction A completes).