A series of attacks compromised several Binance Smart Chain (BSC) projects in May. Following PancakeBunny, three project forks — AutoShark, Merlin Labs, and PancakeHunny were also attacked using similar techniques. PancakeBunny suffered the most costly attack of the four, which saw nearly $45M in total damages. In this article, we dig into the details behind the attacks on the three copycats.
AutoShark was attacked five days after PancakeBunny, followed by Merlin Labs and PancakeHunny, respectively. The following is an analysis of the problems and possible attack techniques for these three forked projects.
In the SharkMinter.mintFor() function, the amount of rewarding SHARK tokens to be minted (i.e., mintShark) is derived from sharkBNBAmount computed by tokenToSharkBNB() in line 1494. However, tokenToSharkBNB() references the current balance of flip, which makes it a vulnerable point. One could assume that the amount of tokens received in line 1492 is equal to the amount of the flip balance. Still, a bad actor could manipulate the flip balance simply by sending in some flip tokens right before the getReward() call and indirectly breaking the logic of tokenToSharkBNB().
In the underlying implementation of tokenToSharkBNB(), there’s another attack surface. As shown in the above code snippet, _flipToSharkBNBFlip() removes liquidity from ApeSwap (line 1243) or PantherSwap (line 1262) and converts the LP tokens into SHARK+WBNB. Later on, the generateFlipToken() is invoked to convert SHARK+WBNB into SHARK-BNB LP tokens.
Inside generateFlipToken(), the current SHARK and WBNB balances of SharkMinter (amountADesired, amountBDesired) are used to generated LP tokens and the amount of LP tokens are returned to mintFor() as sharkBNBAmount. Based on that, the bad actor could transfer SHARK+WBNB into SharkMinter to manipulate the amount of SHARK tokens to be minted as well.
The loophole in PancakeHunny is identical to that found in AutoShark, in that the bad actor can manipulate HUNNY reward minting with HUNNY and WBNB tokens.
Compared to AutoShark and PancakeHunny, Merlin Labs’ _getReward() has a more obvious vulnerability.
The code snippet above shows that the performanceFee could be manipulated by the balance of CAKE, which indirectly affects the MERL rewards minting. However, the nonContract modifier gets rid of flash loans.
Even without an exploit contract, the bad actor could still profit through multiple calls.
Reproducing the AutoShark Attack
To reproduce the AutoShark hack, we need to first get some SHARK-BNB-LP tokens from PantherSwap. Specifically, we swap 0.5 WBNB into SHARK (line 58) and transfer the rest WBNB with those SHARK tokens into PantherSwap for minting SHARK-BNB-LP tokens (line 64). Later on, we deposit those LP tokens into AutoShark’s StrategyCompoundFLIP contract (line 69) to qualify for rewards. Note that we purposely only deposit half of the LP tokens in line 69.
The second step is to make getReward() go into the SharkMinter contract. In the above code snippet, we know that the reward can be retrieved by the earned() function (line 1658). Besides, 30% of the reward (i.e., performanceFee) should be greater than 1,000 (i.e., DUST) to trigger the SharkMinter.mintFor() in line 1668.
Therefore, in our exploit code, we transfer some LP tokens to the StrategyCompoundFLIP contract in line 76 to bypass the performanceFee > DUST check and trigger the mintFor() call. Since we need a lot of WBNB+SHARK to manipulate SharkMinter, we leverage PantherSwap’s 100k WBNB via a flash-swap call in line 81.
In the flash-swap callback, pancakeCall(), we exchange half of the WBNB into SHARK and send the SHARK with the remaining 50,000 WBNB to the SharkMinter contract to manipulate the reward minting.
The next step is to trigger getReward() when the SharkMinter receives the WBNB+SHARK tokens to mint a large amount of SHARK to the caller.
The last step is to convert SHARK to WBNB, pay the flash loan, and walk away with the remaining WBNB tokens.
In our experiment, the bad actor starts with 1 WBNB. With the help of flash loans, he profits from more than 1,000 WBNB being returned in one transaction.
Reproducing PancakeHunny Attack
The theory behind the PancakeHunny attack is similar to the AutoShark attack. In brief, we need to send a lot of HUNNY+WBNB to HunnyMinter before triggering getReward(). However, the HUNNY token contract has a protection mechanism called antiWhale to prevent large amount transfers. Therefore, flash loans do not work here.
To bypass antiWhale, we create multiple child contracts and initiate multiple CakeFlipVault.deposit() calls via said contracts.
In the above exploit code snippet, the LP tokens gathered in line 116 are divided into 10 parts and transferred to 10 Lib contracts in line 122 followed by Lib.prepare() calls for each of them.
Inside Lib.prepare(), we approve() the CakeFlipVault to spend the LP tokens and invoke CakeFlipVault.deposit() to enable the later getReward() calls for minting rewarding HUNNY tokens.
After preparing 10 Lib contracts, the main contract iterates each of them to: 1) swap WBNB to the maximum allowable amount of HUNNY; 2) transfer WBNB+HUNNY to HunnyMinter; 3) trigger getReward() via lib.trigger(); and 4) swap HUNNY back to WBNB.
In the end, the bad actor with 10 WBNB earns around 200 WBNB from 10 runs of 10 Lib contracts operations.
Reproducing the Merlin Labs Attack
As mentioned above, Merlin Labs has the noContract modifier to get rid of flash loan attacks. However, we could use a script to trigger the attack with multiple transactions initiated from an EOA (Externally Owned Account) address. The only difference is that someone may front-run the bad actor’s transaction to steal the profits.
Similar to the AutoShark attack, we need to prepare enough LINK and WBNB (line 23), use them to mint WBNB-LINK-LP tokens (line 34), and deposit LP tokens into VaultFlipCake contract (line 38).
The remaining actions are:
Swapping WBNB to CAKE (line 42).Manipulating MERL minting by sending CAKE to VaultFlipToCake contract (line 50).Triggering getReward() in line 55 (a large amount of MERL tokens are minted).Swapping MERL back to WBNB and repeating the above steps multiple times.
As mentioned earlier, if someone front runs step 3 right after step 2, that person could remove a large amount of MERL.
In our experiment, the bad actor starts with 10 WBNB and walks away with around 165 WBNB by repeating the four steps 10 times.
The information contained in this post (the “Information”) has been prepared solely for informational purposes, is in summary form, and does not purport to be complete. The Information is not, and is not intended to be, an offer to sell, or a solicitation of an offer to purchase, any securities.
The Information does not provide and should not be treated as giving investment advice. The Information does not take into account specific investment objectives, financial situation or the particular needs of any prospective investor. No representation or warranty is made, expressed or implied, with respect to the fairness, correctness, accuracy, reasonableness or completeness of the Information. We do not undertake to update the Information. It should not be regarded by prospective investors as a substitute for the exercise of their own judgment or research. Prospective investors should consult with their own legal, regulatory, tax, business, investment, financial and accounting advisers to the extent that they deem it necessary, and make any investment decisions based upon their own judgment and advice from such advisers as they deem necessary and not upon any view expressed herein.