DeFi Risks 101 — 1: An Insecure Fork of MasterChef

Hello and welcome to the first episode of the DeFi Risks 101. In this series, the Inspex research team will utilize our experience from smart contract auditing service and incident investigations to provide our readers with an in-depth technical analysis of notable issues that introduce risks to DeFi platforms. We hope our efforts put into this series will help improve awareness of risks for both developers and platform users.

An Insecure Fork

In software development, developers can copy the software project’s source code and start independent development on the copy that results in a distinct and separate software. This action is called “fork”. With the same word, we could also use it to describe the result of forking, or software that has been copied for distinct and independent development.

Think about building a DeFi platform, developers could obtain a copy of either a frontend code or smart contracts from a public code repository of another DeFi platform to develop with their approach, and then present the finished platform to the DeFi ecosystem without needing to build from scratch.

By forking a piece of software, the copy inherits characteristics, functionality, and issues from the original. If some of these issues are identified and fixed on the original, the change will not be applied to the copy due to a separation caused by forking. The copy then continues to have fixed-in-original issues until the copy’s maintainer fixes them.

This article will focus on issues in one of the most forked contracts, the MasterChef. Some of these issues are mitigated but not fixed, leaving a possibility of incorrect implementation behind, and some are not an actual issue but a feature that can be abused for malicious intent.

The MasterChef

MasterChef is the name of the contract on the SushiSwap platform (later Sushi). MasterChef contract was originally designed to incentivize users to provide liquidity for the platform. Users can provide liquidity to liquidity pools to gain liquidity provider (LP) tokens, and stake those tokens to the MasterChef contract. MasterChef then rewards liquidity providers with a reward token, $SUSHI on SushiSwap, based on their principal and shares provided to the platform.

SushiSwap’s MasterChef Contract

With the popularity of DeFi, the idea implemented by SushiSwap’s MasterChef was copied and reimplemented on new platforms by forking the MasterChef contract. Some of the forks have been modified and were forked to start newer platforms repeatedly. This creates chains of forked MasterChefs which, if an issue is found on a fork, there is a high chance that the issue affects other forked copies under it as well.

SushiSwap’s Migrator

The first example we’re going to talk about is not far from what we’ve discussed in the last section. There is a functionality on SushiSwap’s MasterChef which plays an important role in the Vampire Attack, the liquidity stealing attack done to Uniswap by SushiSwap. This functionality is known as “migrator”, which consists of setMigrator() and migrate() functions on MasterChef.sol.

The migrate() function takes one argument, _pid for a pool index defined in MasterChef, and can be called by anyone. The function will proceed through these steps when executed:

  1. Verify that the migrator’s address is not zero. This condition cannot be true if the migrator’s address is not specified first by calling setMigrator() which is restricted to the owner.
  2. Find LP token address from pool referred by _pid
  3. Get LP token balance from the address
  4. safeApprove() is called to set the allowance amount for the spender
  5. migrate() under Migrator contract is called to transfer LP token to migrator’s address. The balance in the third step is used here to ensure the success of the migrating process.

It’s not difficult to understand how powerful these functions are. With the owner’s permission to execute setMigrator() to set a new migrator’s address, one can iterate through every pool and execute migrate() to transfer all LP tokens to the specified address, perfect ingredients for allowing a situation known as “rug pull”.

The presence of these functions has been proved as a critical risk by many rug pull incidents. The community responds with a solution to mitigate by using Timelock and completely prevent it by removing the migrator code. However, we still see the migrator code take part in many incidents.

PancakeSwap’s Syrup Pools

PancakeSwap is one the most widely used DeFi platform in Binance Smart Chain and one of the platforms that developed based on SushipSwap’s original MasterChef contract. By looking through the history of MasterChef contract file, we can see that the earliest version of PancakeSwap’s MasterChef is nearly identical to SushiSwap’s MasterChef.

As the popularity of PancakeSwap grew and new features were added, the success of the platform attracts developers to walk on the same path, resulting in another chain of forks with the PancakeSwap’s ideas, design, and contract as the original. And, one of PancakeSwap’s ideas that have been adopted widely by other platforms is the Syrup Pools and $SYRUP.

PancakeSwap’s users can stake $CAKE, the PancakeSwap’s reward token, to obtain $SYRUP, then stake $SYRUP in Syrup Pools to earn rewards as other tokens. If they want their $CAKE back, the users must return the amount of $SYRUP gained on staking.

On November 3, 2020, not long after the opening of Syrup Pools and $SYRUP, PancakeSwap announced the discontinuation of $SYRUP after they discovered an already exploited vulnerability in the MasterChef contract resulted in an excessive amount of $SYRUP minted. The attack was done with the help of a well-known function, emergencyWithdraw().

The emergencyWithdraw() function can be used to withdraw LP tokens staked in the pool. This function allows users to withdraw directly from the MasterChef contract in a situation when normal withdrawal can’t be done.

Looking at the code responsible for issuing $SYRUP and returning $CAKE, $SYRUP is created by calling syrup.mint() in enterStaking() and will be burned by syrup.burn() inside leaveStaking() if users need their $CAKE back with the condition that the same amount of $SYRUP must be returned.

However, if you look closely at the emergencyWithdraw() function which you can use to withdraw LP token like what leaveStaking() does, there’s no need to return $SYRUP to obtain $CAKE back. We can execute enterStaking() to obtain $SYRUP with $CAKE first, then use emergencyWithdraw() to obtain the staked $CAKE back, and redo this process again. This can be done to mint a massive amount of $SYRUP.

Example suspicious transactions aligned with the attack

The decision at that time was to focus on the mitigation rather than updating the MasterChef contract, leaving the code responsible for the attack to be unfixed on the MasterChef contract. By leaving the known issue unfixed, the risk of forking and using the same vulnerable code is higher and can also be transferred to any platforms that accept $SYRUP as well.

The “Less Reward” Bug

The final issue we would like to show on the MasterChef contract lies under the reward calculation process when the staking token is the same as the reward token. This kind of liquidity pool is popular among DeFi platforms because it directly helps increase the usability of that token. However, by relying on some popular MasterChef contracts, this process goes wrong by an easy-to-find issue.

MasterChef contract takes many factors such as the user’s shares and the total amount of tokens staked to help calculate the reward for each user. The example code below shows the updatePool() from SushiSwap which is responsible for this process

In updatePool(), the total amount of token staked for a specific pool is identified using the balance of that token in the MasterChef contract and is stored in the lpSupply variable. If the pool allows the use of the reward token for staking, the amount stored in lpSupply will be incorrectly inflated due to the reward minted to the MasterChef contract, leading to the reward miscalculation.

This issue has a direct impact on the users because the miscalculation will lower the benefits that the users should receive, which will be continually reduced based on the rewards that have not been claimed from the MasterChef by other users.

The issue can be mitigated in different ways. Developers could fix this by separating the contract responsible for collecting the reward from the reward calculation contract, or preventing the pool with the same staking token as the reward token from being added.

What Can We Do?

Due to the issues and risks of forking and using an insecure MasterChef contract, reviewing the MasterChef contract before deciding to use it is a good strategy that allows us to assess the risks of the platform on our own. We can review the MasterChef contracts with the help of methods and guidelines compiled by RugDoc as follows:

  1. Identify the origin of the MasterChef contract from signatures added by MasterChef contract developers
  2. Obtain the original contract and compare it with the forked version
  3. Review changes on the forked MasterChef. Use known issues and bugs collected by RugDoc to help in this step

About Inspex

Inspex is formed by a team of cybersecurity experts highly experienced in various fields of cybersecurity. We provide blockchain and smart contract professional services at the highest quality to enhance the security of our clients and the overall blockchain ecosystem.

For any business inquiries, please contact us via Twitter, Telegram, contact@inspex.co

Cybersecurity professional service, specialized in blockchain and smart contract auditing https://twitter.com/InspexCo