Merlin Lab — Incident Case Analysis
Originally published in Valix Consulting’s medium.
This report analyzes the incident of Merlin Lab exploited on Jun-29–2021 01:38:21 AM +UTC.
On Jun-28–2021 11:46:36 AM +UTC, Merlin deployed a new strategy for Alpaca-BNB, called MerlinStrategyAlpacaBNB. The attack then began after 14 hours of the strategy deployment.
Table of Contents
The attacker executed 25 significant transactions to profit from the newly deployed Merlin’s strategy for Alpaca-BNB.
Each attack transaction comprised of seven steps, as depicted in Figure 1, including:
Deposited 0.1 WBNB to Merlin’s Alpaca Vault
Transferred a certain amount of WBNB to Merlin Proxy Contract
Transferred 1 ALPACA to Merlin Proxy Contract
Harvested the ALPACA reward
Obtained incorrectly minted MERL reward
Withdrew the deposited 0.1 WBNB
Swapped the incorrectly minted MERL for a profit in WBNB
After executing the 25 attack transactions, the attacker took a profit of 1077.45 WBNB in total. You can find the details of the transactions in the Associated Transaction section below.
Attacker Address: 0x2bADa393e53D0373788d15fD98CB5Fb1441645BD
Attacker Contract #1: 0x1509e9df7fd7f567f6e52bcada0035932281cd0d
Attacker Contract #2: 0xeb69bad9662fbe068e851540778d845e41cd0f54
Attacker Contract #3: 0x25ed360a850838fcedd0a881b8962efecf5ffcbc
Attacker Contract #4: 0xcc369faf26c0457ceea7a24bbfe158e106214147
Merlin Proxy Contract: 0xFeFFa88E6e3C99937B73faa6f7A770f20b661CbE
MerlinStrategyAlpacaBNB Contract: 0x9059F2F644402DB18155B7917762823A58962397
Alpaca Vault Contract: 0xd7D069493685A581d27824Fc46EdA46B7EfC0063
Alpaca FairLaunch Contract: 0xA625AB01B08ce023B2a342Dbb12a16f2C8489A8F
Alpaca Contract: 0x8f0528ce5ef7b51152a59745befdd91d97091d2f
WBNB Contract: 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c
PancakeSwap Router: 0x5f66f543B20A4BD4195521C14E9ae77ED7111c98
In this section, we give you a detailed step-by-step analysis of the incident. One of the 25 attack transactions was picked to investigate, transaction id: 0x31fdf621f60684579de00f30b33fa051c19f1fd45d891c817e2a3888a9b75726. The attacker, in addition, used the same technique for the other transactions.
The attacker exploited the attack by deploying a custom smart contract as an attack automated tool called Attacker Contract.
As illustrated in Figure 2, the attacker invoked the deployed Attacker Contract. The Attacker Contract then atomically executed sequential calls to other related contracts to profit the attacker within a single transaction.
The attack transaction can be broken down into seven steps as follows.
Step 1: Deposited 0.1 WBNB to Merlin’s Alpaca Vault
The attack began by the attacker made a call to the Attacker Contract. Then, the Attacker Contract transferred 0.1 WBNB to the
_deposit function of Merlin Proxy as Step 1.
The function then deposited the received 0.1 WBNB to the Alpaca Vault to mint ibBNB tokens for the Attacker Contract (line no. 201). The function then calculated shares and principal variables (line no’s. 214–218) reflecting a deposit portion of the Attacker Contract in the MerlinStrategyAlpacaBNB’s pool.
Note, the ibBNB is the Alpaca’s interest-bearing token used to keep track of the users’ deposited funds and any interest earned.
Step 2: Transferred 546.712 WBNB to Merlin Proxy Contract
Then, the Attacker Contract called the
transfer function on WBNB Contract to transfer 546.712 WBNB to Merlin Proxy Contract as Step 2.
Step 3: Transferred 1 ALPACA to Merlin Proxy Contract
Next, the Attacker Contract called the
transfer function on Alpaca Contract to transfer 1 ALPACA to Merlin Proxy Contract as Step 3.
Step 4: Harvested the ALPACA reward
In Step 4, the Attacker Contract executed the
_harvest function to harvest the ALPACA reward. The
_harvest function has a staking check (line no. 192). However, since the Attacker Contract has previously deposited 0.1 WBNB in Step 1, the deposit bypassed the staking check logic here.
Since the deposit of 0.1 WBNB to the Alpaca Vault in Step 1 has just performed, within the same transaction as Step 4, there was no ALPACA reward earned (line no. 194). However, the attacker’s objective for executing this step was to invoke the
_tryReinvest function (line no. 196).
_tryReinvest function has the reward amount check logic in line no. 209, the control flow would pass this check if and only if the
rewardTokenAmount is more than or equal to the
Because the function considered the deposit of 1 ALPACA in Step 3 as a reward,
rewardTokenAmount, the condition check was bypassed. Next, the function swapped the deposited 1 ALPACA for 0.001424 WBNB via PancakeSwap Router (line no. 212).
Subsequently, once the function looked up for its balance, the function found 546.713 WBNB (i.e., 546.712 WBNB from the transfer in Step 2 + 0.001424 WBNB from the reward swap in line no. 212). Surprisingly, the function determined this balance as
stakingTokenAmount (line no. 215).
Eventually, the function deposited the
stakingTokenAmount, 546.713 WBNB, to the Alpaca Vault, and the addition ibBNB tokens were minted for the Attacker Contract (line no. 221).
Step 5: Obtained incorrectly minted MERL reward
Later, the Attacker Contract invoked the
_getRewards function as Step 5. Intriguingly, the function considered the deposited 546.713 WBNB in the previous step as the
earnedWantTokenAmount (line no. 276).
In line no. 299, the
earnedWantTokenAmount was passed as an argument into the function
_addProfitReward, and this is where the incorrect MERL tokens were minted as rewards by mistake. We will have a look into the details of this function later on.
After the execution in line no. 299 succeeded, the
earnedWantTokenAmount would hold some profit reward of 382.7 WBNB. This reward was then transferred to the Attacker Contract (line no. 303).
Let’s investigate how the incorrect MERL tokens were mistakenly minted. The reward of 546.713 WBNB was passed as the
amount argument into the
_addProfitReward function. The
amount was used to calculate a performance fee (line no. 236), which was 164014164406883244159 in this case. After that, the performance fee was passed into the
mintFor function (line no. 245).
mintFor function, the new
_performanceFee was calculated (line no’s. 211–212). The
contribution parameter was computed from the
_performanceFee (line no’s. 214–215). Consequently, the resulting
contribution was used by the
amountMerlinToMint function to calculate the amount of MERL tokens to mint as rewards (line no. 224). The incorrect 5,625.685 MERL were minted for the Attacker Contract (line no. 226).
amountMerlinToMint function was the root cause of the exploit. This function calculated the amount of MERL tokens that would be minted for the Attacker Contract as rewards.
The following equation was being used:
merlinPerProfitBNB had a fixed value of 350000000000000000, the
mintMerlin was 5,625.685, which considered an invalid value as the fixed value of the
merlinPerProfitBNB did not reflect the actual price of MERL comparing to the price of BNB at that moment.
Step 6: Withdrew the deposited 0.1 WBNB
After the incorrect 5,625.685 MERL was taken, the Attacker Contract invoked the
withdrawAll function as Step 6. This function withdrew the deposit of 0.1 WBNB (in Step 1) from Alpaca Vault (line no. 157). The deposit
amount deducted from the
withdrawalFee and the
performanceFee, 0.0995 WBNB, was transferred to the Attacker Contract (line no. 175).
Step 7: Swapped the incorrectly minted 5,625.685 MERL for 246.289 WBNB
Step 7 was performed as the final step. The Attacker Contract invoked a call to the
swapExactTokensForTokens function to swap the incorrectly minted 5,625.685 MERL for 246.289 WBNB through PancakeSwap Router.
Profit gained from this transaction
The attacker gained a profit from the considered transaction of 82.263 WBNB.
There were 25 significant transactions during the attack, generated by 4 Attacker Contracts as follows.
Transactions executed by Attacker Contract #1
— Profit Gained: 0.037 WBNB
Transactions executed by Attacker Contract #2
— Profit Gained: 8.595 WBNB
Transactions executed by Attacker Contract #3
— Profit Gained: 10.622 WBNB
Transactions executed by Attacker Contract #4
— Profit Gained: 12.684 WBNB
— Profit Gained: 15.706 WBNB
— Profit Gained: 19.354 WBNB
— Profit Gained: 23.711 WBNB
— Profit Gained: 28.838 WBNB
— Profit Gained: 34.767 WBNB
— Profit Gained: 41.464 WBNB
— Profit Gained: 48.816 WBNB
— Profit Gained: 56.584 WBNB
— Profit Gained: 64.394 WBNB
— Profit Gained: 71.723 WBNB
— Profit Gained: 77.917 WBNB
— Profit Gained: 82.263 WBNB
— Profit Gained: 84.078 WBNB
— Profit Gained: 82.837 WBNB
— Profit Gained: 78.3 WBNB
— Profit Gained: 70.517 WBNB
— Profit Gained: 59.924 WBNB
— Profit Gained: 47.215 WBNB
— Profit Gained: 33.246 WBNB
— Profit Gained: 18.899 WBNB
— Profit Gained: 4.959 WBNB
Total Profit Gained
The attacker gained a profit of 1077.45 WBNB.
The attacker used custom Smart Contracts as an automated tool to facilitate their attacks. The attacker executed 25 significant transactions during the raid that made the attacker gain 1077.45 WBNB.
After being attacked, Merlin Lab announced to cease its operations. Users who are farming via Merlin’s pools are asked to withdraw their funds as the website is shutting down, unless they may need to withdraw their funds through BscScan or other supported platforms instead.
About Valix Consulting
Valix Consulting is a blockchain and smart contract security firm offering a wide range of cybersecurity consulting services. Our specialists, combined with technical expertise with industry knowledge and support staff, strive to deliver consistently superior quality services.
For any business inquiries, please get in touch with us via Twitter, Facebook, or firstname.lastname@example.org.
Originally published in Valix Consulting’s medium.