Contents

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


Figure 1. Each attack transaction comprised of seven steps

Figure 1. Each attack transaction comprised of seven steps


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:

  1. Deposited 0.1 WBNB to Merlin’s Alpaca Vault

  2. Transferred a certain amount of WBNB to Merlin Proxy Contract

  3. Transferred 1 ALPACA to Merlin Proxy Contract

  4. Harvested the ALPACA reward

  5. Obtained incorrectly minted MERL reward

  6. Withdrew the deposited 0.1 WBNB

  7. 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



.    .    .

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.

Figure 2. Overview of Smart Contracts interaction

Figure 2. Overview of Smart Contracts interaction


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


Figure 3. The _deposit function of Merlin Contract

Figure 3. The _deposit function of Merlin Contract


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


Figure 4. The transfer function of WBNB Contract

Figure 4. The transfer function of WBNB 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


Figure 5. The transfer function of Alpaca Contract

Figure 5. The transfer function of Alpaca 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


Figure 6. The _harvest and _tryReinvest functions of Merlin Contract

Figure 6. The _harvest and _tryReinvest functions of Merlin Contract


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


Figure 7. The _getRewards function of Merlin Contract

Figure 7. The _getRewards function of Merlin Contract


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).

Figure 8. The _addProfitReward function of Merlin Contract

Figure 8. The _addProfitReward function of Merlin Contract


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).

Figure 9. The mintFor function of Merlin Contract

Figure 9. The mintFor function of Merlin Contract


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).

Figure 10. The amountMerlinToMint function of Merlin Contract

Figure 10. The amountMerlinToMint function of Merlin Contract


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:

1
mintMerlin = bnbProfit * merlinPerProfitBNB / 10^18


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


Figure 11. The withdrawAll function of Merlin Contract

Figure 11. The withdrawAll function of Merlin Contract


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


Figure 12. The swapExactTokensForTokens function of PancakeSwap Contract

Figure 12. The swapExactTokensForTokens function of PancakeSwap Contract


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


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
amountIn = 382.7 WBNB (Step 5) + 
           0.099 WBNB (Step 6) +
           246.289 WBNB (Step 7)
         = 629.088 WBNB

amountOut = 0.1 WBNB (Step 1) + 
            546.712 WBNB (Step 2) + 
            0.001 WBNB (Step 3)
          = 546.813 WBNB

txFee = 0.012 WBNB

profitGained = amountIn - amountOut - txFee
             = 629.088 WBNB - 546.813 WBNB - 0.012 WBNB
             = 82.263 WBNB


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

  1. 0x1ba1fd3ba2605314eee424abef0e42667a3bac739359b0a27d7c7f87b09f6798
    Profit Gained: 0.037 WBNB


Transactions executed by Attacker Contract #2

  1. 0x4dcf37a6d5ed7786fc7f103258a245ba8dcd813b57a5bc516eda68f56c26fec8
    Profit Gained: 8.595 WBNB


Transactions executed by Attacker Contract #3

  1. 0x0fed4bde0e389434925f6aa2072c5b862bc0878908c39e4c165c86872584a609
    Profit Gained: 10.622 WBNB


Transactions executed by Attacker Contract #4

  1. 0x3be6c0ab07d7fa03bca44b0d0aa4093e67dca2747278cf2951740f2cd8db5a0f
    Profit Gained: 12.684 WBNB

  2. 0x163314b66bacee77c6e13a5063c37ad1201fa14f93a57fdb1099085b462b151a
    Profit Gained: 15.706 WBNB

  3. 0xba2e4151b0a032075455b7c35f6f16afe8dc054fcc513abf5fadaf2c82eb8485
    Profit Gained: 19.354 WBNB

  4. 0x09fdb6b036175c762809fc9e157f9ff27b203f24885b0516cb958ebfb6bf7295
    Profit Gained: 23.711 WBNB

  5. 0xf527bb753045700435a2d233e5fa007e5c41c09b534f22e70f90d0fb1e0d89ba
    Profit Gained: 28.838 WBNB

  6. 0xcf2fa2aad34b0368c903446821aae24626b0a5f15c74b23242083711f6034da2
    Profit Gained: 34.767 WBNB

  7. 0xc9bc30c2e8f6dcd769e05ae0f0413436b652e3976f144eb662adfb5ebc68fced
    Profit Gained: 41.464 WBNB

  8. 0xc653926bc4ee7a1e661aafe5aa677e444b82a3938908ba236952ab3eb78d9a06
    Profit Gained: 48.816 WBNB

  9. 0x0451b4d0c777a82c5510505c7b849c301cc462dee8d39ae7576d6acc5bdcaf36
    Profit Gained: 56.584 WBNB

  10. 0x27577113330c7a24bfbe3b1d12a7e39ed13ae652a639cebaaf187c140d3fc1bb
    Profit Gained: 64.394 WBNB

  11. 0x4ccf7e11f1e542f4a9b58ef528d557aec9691f4c1ee86940a82c6be90fbd3d8f
    Profit Gained: 71.723 WBNB

  12. 0x37ea9fb064ebcdcb191c3002ea067a3b9ad6d51d3dc9f423ceb3aec069542ff1
    Profit Gained: 77.917 WBNB

  13. 0x31fdf621f60684579de00f30b33fa051c19f1fd45d891c817e2a3888a9b75726
    Profit Gained: 82.263 WBNB

  14. 0xb0be09a44e4afd2e84c391dd3759e76462524cc323181f8d0b9563f16e0e5961
    Profit Gained: 84.078 WBNB

  15. 0x3dd26b84dd8f302232aa85ecfbf6a95a30c707557ec2902fa8206e6f4fba64b3
    Profit Gained: 82.837 WBNB

  16. 0xa93ed3833ea5c16655ea07a42a40c2866e26add45e27563a64e12c4dcab0b32f
    Profit Gained: 78.3 WBNB

  17. 0x903ae34f48d4e00da8d7ca5dfad26f8f37e80cde2156907580cf551a63317f76
    Profit Gained: 70.517 WBNB

  18. 0x8e70209ceef833e964c36d5c947b0adfbc60d8845f3ffc03588544408132ee15
    Profit Gained: 59.924 WBNB

  19. 0x3f30562e347f31be2eb3ce27455606025ecc748b91b95bc5abe8291c6061efdc
    Profit Gained: 47.215 WBNB

  20. 0x09cea6d81cadc548aa4727e295511b3b429bf1b439a6a1c603d9454461920242
    Profit Gained: 33.246 WBNB

  21. 0xf48f28a24571ffded2a0e06cbf30acfb4db085a4a2c8dc659d2dbf16344ae1de
    Profit Gained: 18.899 WBNB

  22. 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.

Figure 13. Merlin Lab announced to cease its operations

Figure 13. Merlin Lab announced to cease its operations


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.