$1,800,000 was stolen from Binance Smart Chain PancakeSwap Lottery Pool
All information presented below can easily be verified by reviewing Binance Smart Chain logs on any blockchain tracker site like BscScan or directly from blockchain data.
Since April 12th, 2021 a person who had access to a Binance Smart Chain account 0x35f16a46d3cf19010d28578a8b02dfa3cb4095a1 (PancakeSwap admin account) has stolen from PancakeSwap lottery pool 59,765 Cakes (equivalent of about $1,800,000).
He used the exploit a few times. Shortly after the last theft the lottery game was suspended, and this account was banned by PancakeSwap. It gives a clear thought that PancakeSwap has been aware of the theft at that moment.
Author of this report decided to wait a few weeks, so PancakeSwap would have enough time to report the theft and cover players’ losses due to it. Unfortunately, none of this happened. So here are the results of the investigation.
The admin of PancakeSwap used his opportunity to manually call lottery contract methods such as:
function drawing(uint256 _externalRandomNumber) external onlyAdmin
function enterDrawingPhase() external onlyAdmin
He executed a few calls simultaneously (buy, enter drawing, draw) and put them all into the same block. That created for him an opportunity to predict jackpot numbers, since the random number generator, based on previous block hash, was no longer random.
In order to execute those calls in the correct sequence inside of each block, he set different (decreasing) gas prices on each transaction.
2021–04–12 13:59:59 Winner numbers: 1,11,13,6. Winner tickets: 1722181 Damage: 13058.50 cakes
BSC Block: 6502699 0xdc94f6e0842eed4c3455d5449ac456d42f74b174d3130c3c7e53be3143675473 30 buy
0x0087ea4805061b70dc38a84f9c40e868fdc3f023fc73fc01aa2534991e24471a 10 enter
0x323dcfbbd68d61b47173de8e3385bece39929ef75cdc9188d557165e9e132475 10 drawing
2021–04–17 01:59:47 Winner numbers: 9,5,6,10. Winner tickets: 1911081 Damage: 8670.33 cakes
BSC Block: 6632019 0x8cb393293a3f9742cac35435b39491f4de21b3b545746ad3d2b0b46607dda504 30 buy
0xc60fd45b315ee68658f3ca66d098b34bcb6cec46f3469b2d6399a7c59c0fc523 10 enter
0x0a730e930639e00094136a8ef5d5f4ba40e7a095d81751cfed40ec6b07837305 10 drawing
2021–04–19 01:59:35Winner numbers: 12,1,10,7. Winner tickets: 2007689 Damage: 9171.6 cakes
BSC Block: 6689562 0x2aa0866d3262760b964b6c7253500be41faf2a5ff7f56c6de92ede7c8bbdb054 30 buy
0xde918f0f76c503dd7f9447ccd4337da2c8a832c1438f2cc447718eeb0209e652 10 enter
0x8133b74c70355cfea13148dcd585e06c59a0a1c229bc849e8570589244584e87 10 drawing
2021–04–21 13:59:50 Winner numbers: 5,7,11,4. Winner tickets: 2049591, 2049592 Damage: 13818 cakes
BSC Block: 6760839 0x115fb6e4d115a9d1eced84acc20534b934d9ce12320177f23e13b4adbadd69e2 17 buy
0x952d51adb13d7bda46532d5390b3f994bc8c81885a5fd08ebf153cea85543aa1 15 enter
0x2043d8ef2238480c2ee9908f8f9c0f7b87648052b13e6ba1c9f6dea1e196cf5f 15 drawing
2021–04–23 13:59:45 Winner numbers: 1,9,9,11. Winner tickets: 2096497, 2096498, 2096500, 2096501 Damage: 15047 cakes
BSC Block: 6817022 0xed36154cc6256dee6b0ed07a779292ca49227d3b4e91ce9fa5d5e392fb03e148 17 buy
0xaf77da7a2e58c83b65cc89fcaaccdab72aa8ecd22e3d138d4fe6e3cc9144b8b4 17 buy
0xd55171ec9a66bb38471f28dee11f0c2bcd68aff9679d27b763794d66d8e7f69b 15 enter
0x8da3467244b3147fd6bfa625bd0ab2549a03afedef7b520656ef35fb838e462f 15 drawing
As we can see, he even has optimized his strategy since April 21st: he made closer values of gas prices to make transactions closer to each other inside of the block. It reduced the probability of being hit by a random buy call (a random buy call between his calls would have changed the jackpot number based on total ticket count).
One might assume that drawing and buy transactions are not connected, and it is just a random hacker front running, however, there are a few things that make this assumption highly unplausible:
1. Whoever bought the first-time exploit ticket (1722181), knew that two transactions were going to be in the same block BEFORE it actually happened. It was impossible to predict as it never happened before. Unless there is a person who made all three transactions.
2. Coordinated changes in gas prices (from April 19th to April 21st).
3. Admin actually created the possibility of exploit running two transactions together.
Suggested fix to remove further exploit possibilities
To make jackpot numbers genuinely random, drawing transaction should meet two criteria. First: it should be at least a few blocks away from enterDrawingPhase transaction in order to prevent exploit of known block hash. Block distance check should be performed inside of drawing method as a requirement. Second: drawing transaction should be performed in predefined time (e.g. 02:05:00 PM), or in a predefined time interval after enterDrawingPhase method (e.g. 05:00 minutes). If the second criterion is not met, there is another opportunity to exploit: admin might manipulate jackpot numbers waiting for the “right” block to inject drawing transaction.
Unfortunately, the nature of smart contracts doesn’t allow us to set a timer inside of enterDrawingPhase method to fire drawing event in exactly 5 minutes. All smart contract executions can be triggered only by a transaction from outside.
So, there are only two ways of implementing second criterion:
First method is an external trusted timer service. Someone everybody trusts sends a transaction in predefined time. This is a good method, but it’s basically the same as external trusted random number generator, and it breaks decentralized nature of the lottery.
Second method is decentralized and therefore should be preferred in this case:
Drawing method should be public, and literally anybody should be able to execute it as soon as target block distance (or target time interval) from enterDrawingPhase method is reached.
Owner of the lottery should assign a small bounty (e.g. 1 cake) to the first person who fires successful drawing method. It would motivate a lot of people, whether they participate in the lottery on not, to trigger that method when time comes. Anyone would be happy to get one cake and participate in generating jackpot numbers. In case if there are too many participants and blockchain is suffering, bounty amount might be reduced