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
TLDR
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.
Associated Addresses
-
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
Incident Analysis
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).
Since the _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 rewardTokenSwapThreshold
.
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).
In the 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).
And, the 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:
|
|
Where the 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.
Associated Transactions
There were 25 significant transactions during the attack, generated by 4 Attacker Contracts as follows.
Transactions executed by Attacker Contract #1
- 0x1ba1fd3ba2605314eee424abef0e42667a3bac739359b0a27d7c7f87b09f6798
— Profit Gained: 0.037 WBNB
Transactions executed by Attacker Contract #2
- 0x4dcf37a6d5ed7786fc7f103258a245ba8dcd813b57a5bc516eda68f56c26fec8
— Profit Gained: 8.595 WBNB
Transactions executed by Attacker Contract #3
- 0x0fed4bde0e389434925f6aa2072c5b862bc0878908c39e4c165c86872584a609
— Profit Gained: 10.622 WBNB
Transactions executed by Attacker Contract #4
-
0x3be6c0ab07d7fa03bca44b0d0aa4093e67dca2747278cf2951740f2cd8db5a0f
— Profit Gained: 12.684 WBNB -
0x163314b66bacee77c6e13a5063c37ad1201fa14f93a57fdb1099085b462b151a
— Profit Gained: 15.706 WBNB -
0xba2e4151b0a032075455b7c35f6f16afe8dc054fcc513abf5fadaf2c82eb8485
— Profit Gained: 19.354 WBNB -
0x09fdb6b036175c762809fc9e157f9ff27b203f24885b0516cb958ebfb6bf7295
— Profit Gained: 23.711 WBNB -
0xf527bb753045700435a2d233e5fa007e5c41c09b534f22e70f90d0fb1e0d89ba
— Profit Gained: 28.838 WBNB -
0xcf2fa2aad34b0368c903446821aae24626b0a5f15c74b23242083711f6034da2
— Profit Gained: 34.767 WBNB -
0xc9bc30c2e8f6dcd769e05ae0f0413436b652e3976f144eb662adfb5ebc68fced
— Profit Gained: 41.464 WBNB -
0xc653926bc4ee7a1e661aafe5aa677e444b82a3938908ba236952ab3eb78d9a06
— Profit Gained: 48.816 WBNB -
0x0451b4d0c777a82c5510505c7b849c301cc462dee8d39ae7576d6acc5bdcaf36
— Profit Gained: 56.584 WBNB -
0x27577113330c7a24bfbe3b1d12a7e39ed13ae652a639cebaaf187c140d3fc1bb
— Profit Gained: 64.394 WBNB -
0x4ccf7e11f1e542f4a9b58ef528d557aec9691f4c1ee86940a82c6be90fbd3d8f
— Profit Gained: 71.723 WBNB -
0x37ea9fb064ebcdcb191c3002ea067a3b9ad6d51d3dc9f423ceb3aec069542ff1
— Profit Gained: 77.917 WBNB -
0x31fdf621f60684579de00f30b33fa051c19f1fd45d891c817e2a3888a9b75726
— Profit Gained: 82.263 WBNB -
0xb0be09a44e4afd2e84c391dd3759e76462524cc323181f8d0b9563f16e0e5961
— Profit Gained: 84.078 WBNB -
0x3dd26b84dd8f302232aa85ecfbf6a95a30c707557ec2902fa8206e6f4fba64b3
— Profit Gained: 82.837 WBNB -
0xa93ed3833ea5c16655ea07a42a40c2866e26add45e27563a64e12c4dcab0b32f
— Profit Gained: 78.3 WBNB -
0x903ae34f48d4e00da8d7ca5dfad26f8f37e80cde2156907580cf551a63317f76
— Profit Gained: 70.517 WBNB -
0x8e70209ceef833e964c36d5c947b0adfbc60d8845f3ffc03588544408132ee15
— Profit Gained: 59.924 WBNB -
0x3f30562e347f31be2eb3ce27455606025ecc748b91b95bc5abe8291c6061efdc
— Profit Gained: 47.215 WBNB -
0x09cea6d81cadc548aa4727e295511b3b429bf1b439a6a1c603d9454461920242
— Profit Gained: 33.246 WBNB -
0xf48f28a24571ffded2a0e06cbf30acfb4db085a4a2c8dc659d2dbf16344ae1de
— Profit Gained: 18.899 WBNB -
0x9d0f75824f85f108063f6d138aabadc5146d2e5cada3e7928568d6b2519f5068
— Profit Gained: 4.959 WBNB
Total Profit Gained
The attacker gained a profit of 1077.45 WBNB.
Summary
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.
Author Details
Phuwanai Thummavet (serial-coder), Lead Blockchain Security Auditor and Consultant | Blockchain Architect and Developer.
See the author’s profile.
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 info@valix.io.
Originally published in Valix Consulting’s Medium.