Reentering the Reentrancy Bug: Disclosing BurgerSwap’s Vulnerability

As part of our ongoing research at Zengo, we decided to explore BurgerSwap, an Automated Marker Maker service on Binance Smart Chain (BSC).

While doing so, we encountered a severe vulnerability that put BurgerSwap customers’ funds at risk. Upon making our discovery, we immediately disclosed the issue to BurgerSwap to have it fixed.

The vulnerability is a classical reentrancy attack, which enables an attacker to drain all WETH (Wrapped Ether) holdings on BurgerSwap’s Ethereum-BSC “bridge” contract on Ethereum (the attacker gets Ether) at a negligible cost and modest liquidity requirements. At the time of our disclosure of the vulnerability, there was ~$13K worth of Ether at immediate risk.

In this article, we’ll provide some background on Binance Smart Chain and BurgerSwap, give a detailed analysis of the reentrancy bug, and offer some insights into the security of such projects.

What is Binance Smart Chain?

Launched in April 2020, Binance Smart Chain (BSC) is an Ethereum Virtual Machine (EVM) compatible blockchain that supports smart contracts. It can host dapps or DeFi applications similar to Ethereum. However, due to a more “closed” consensus algorithm at its core, in the form of Proof-of-Staked-Authority, it trades-off decentralization in favor of a more rapid settlement.

BSC is run parallel to the less flexible Binance Chain, which was launched a year before, and the two blockchains integrate native methods for interoperability between them. BNB is the native coin on BSC and is used to pay gas fees, the equivalent to Ether on Ethereum. BEP-20 is BSC’s equivalent of Ethereum’s ERC-20 standard for tokens.

What is BurgerSwap?

BurgerSwap is an Automated Marker Maker (AMM) service operating on Binance Smart Chain, similar to Uniswap on Ethereum.

It offers users to list and trade specialized BEP-20 tokens among standard swapping options, representing the equivalent of existing ERC-20 tokens. To mint such tokens, users can use BurgerSwap’s “bridge” contract on Ethereum. On this contract, a user can lock a certain amount of an ERC-20 token, and in return, is credited with the same amount of a specialized Burger Swap token, a “bToken,” on BSC. 

One example could be locking Ether, which is converted via the contract to WETH (Wrapped Ether, an ERC-20 token pegged to Ether), and then the same wallet locking ETH can be credited with bWETH on BSC.

The same principle applies the other way around. A user can “payback” a BEP-20 bToken on the bridge contract on BSC and withdraw the same amount of the equivalent ERC-20 token via the bridge contract on Ethereum. Continuing with the example above, “paying back” bWETH on BSC would unlock WETH, and eventually credit the user with ETH. 

The vulnerability

The vulnerability was the Ethereum-BSC “bridge” contract on Ethereum. The issue had to do with calling the function withdrawFromBSC with WETH as the _token, and the _signature to conform with this token and an address of a structured smart contract as the sender.

This is the function’s implementation:


Notice the order of actions in this function:

  1. It checks executeMap[_paybackId] is false.
  2. It checks _signature is a valid signature on _paybackId, _token, msg.sender, and _amount.
  3. It calls TransferHelper.safeTransferETH(msg.sender, _amount).
  4. It sets executeMap[_paybackId] to true.

Just like other well-known reentrancy vulnerabilities in the past, such as the exploitation of The DAO to extract ~$150M, the issue here is the interaction with the sender’s address (step 3) happens before the internal effect (step 4). 

The interaction is the call to TransferHelper.safeTransferETH, which has the following implementation:


As you can see,{value:value}(new bytes(0)) is actually a call to the sender of the message, which can be an arbitrary smart contract.

This smart contract can be malicious where upon receiving an ETH transfer, a fallback function is called, and it calls again to the initial function, withdrawFromBSC.  Because executeMap[_paybackId] is still not set to true, the check will pass, and the attacker will be able to receive the requested amount of ETH again.

Repeating this process within the same transaction (for a sufficient amount of gas) will drain the vulnerable contract’s WETH holdings and credit the attacker with the respective amount of ETH.

Note: the reentrancy vulnerability could also be applied to transfers of other tokens, not only of ETH, as was the case with tens of millions of dollars worth of imBTC stolen from several DeFi projects. This is because imBTC is an ERC777 token, extending the ERC20 standard by triggering an arbitrary function in a receiving contract. Since no such tokens were listed on BurgerSwap at the disclosure time, we focus on ETH.

What is the required signature?

The signature required to withdraw funds from the bridge contract is validated against a signing address set by the contract deployer, i.e. under BurgerSwap’s control. Such a signature is obtained by facing BurgerSwap’s user interface: the application’s backend listens to deposit events and allows the depositing user to send the respective withdrawal transaction to the other blockchain, containing the required signature. The signature is given on the same address that deposited the funds. 

Typically, the use of the application is done by connecting MetaMask. The UI will usually allow a user to withdraw into the account active on MetaMask, an account controlled by a private key.

The Receive button (red circle) for withdrawing Wrapped Ether (WETH) on Ethereum into the connected MetaMask address.

It’s important to remember that this address must be of a smart contract to take advantage of the reentrancy attack. The application should also have some network call to a backend producing this signature on a given address for the UI to construct such a transaction. Finding and modifying this network call is an option, but we found it simpler to use our own modified version of MetaMask that sends an account of our choice to connected dapps. We hard-code this account to be the address of our malicious smart contract.

The exploit – putting it all together

An attacker needs to have an address with modest amounts of ETH and BNB on Ethereum and BSC, respectively (addresses are derived in BSC the same as in Ethereum). Furthermore, on BSC, the attacker needs bWETH (possible either by using BurgerSwap’s Bridge with depositing ETH or a direct swap).

These were the steps for exploitation: 

  1. Deploy a malicious contract* to participate in the reentrancy exploit.
    This contract needs:
    a. delegate – a payable function which delegates contract interactions from the attacker’s address.
    b. setParams – a setter function to set the parameters for the vulnerable withdrawFromBSC function, which can not be known in advance, i.e. the signature, the payback ID, and optionally the amount.
    c. A fallback payable function which accepts calls from the vulnerable contract when withdrawing ETH, and executes the reentrancy attack itself.
    * The contract deployed to both networks must have the same address. Because of this and the fact that contract addresses are computed deterministically with their deployer’s address and nonce as inputs, we need to make sure we use the same address with the same nonce to deploy the contract on both chains. This can be done by transferring ETH (and BNB) to a fresh address, which will deploy the contracts (with nonce = 0).
The attacker’s contract
  1. Send bWETH to the malicious contract.
  1. Call the malicious contract’s delegate function to delegate a call to paybackTransit on BSC’s BurgerSwap bridge contract, with bWETH as the token and the desired amount.
  1. Fork MetaMask’s open-source code to create a modified MetaMask extension, such that the account returned to connected Dapps is the address of the malicious contract (and not a private-key owned account).
  1. Go to
    a. Connect to the forked MetaMask on Ethereum.
    b. Click a “Receive” button available on the “Transit Record” section in the application. MetaMask will pop-up for broadcasting a transaction containing the necessary data with the desired signature, permitting the malicious contract to withdraw the ETH on Ethereum.
    c. Copy the data-field on MetaMask, and decode it by some external tool according to the contract’s Abstract Binary Interface.
    d. Call the malicious contract’s setParams function with the given signature, payback ID and amount.
    e. Call the malicious contract’s delegate function, so it delegates a call to withdrawFromBSC, with the same data produced on step b.
    f. Profit!

Handling the vulnerability

Raising concerns about the vulnerability was challenging. With the BurgerSwap development team anonymous, and no security section on their website or blogs with instructions for dealing with such a vulnerability report, we had to look at other ways to make contact.

Eventually, we approached the @Burger???? King???????? Telegram handle [sic] directly, which appeared to run BurgerSwap’s Telegram channel linked from their Dapp, and were referred to the relevant engineers. 

After giving our detailed report, BurgerSwap’s team took action within several hours, following our recommendations. Since they control the signing key for authorizing withdrawal calls on the vulnerable contract, they didn’t need to perform an actual reentrancy hack. Instead, all they were required to do was sign themselves on the entire amount of available WETH to withdraw the funds to their own wallet.

Afterward, they moved all other assets to their own wallet as well. They deployed a new contract with a reentrancy prevention and modified their web application so it will direct transactions to this fixed contract instead of the vulnerable one.

Withdrawal function in BurgerSwap’s fixed Ethereum-BSC bridge contract (source)

The fix consists of two parts:
(1) The internal effect _executedMap[_paybackId] = true occurs before the actual interaction in TransferHelper.safeTransferETH.
(2) Using the lock modifier:

Using the modifier above makes sure that there would be only one entrance to the withdrawal function within a single transaction.
The former would be enough as it prevents both reentrancy and replay attacks (re-using the same parameters for double-withdrawing the same token), whereas the latter fixes only reentrancy. As a recognition of our effort, the team rewarded us with a bounty.

Concluding thoughts 

The bridge contracts use a signature of a specific signing key (a single private key, not a MultiSig contract) under the platform maintainers’ control. This is the simplest way to connect between two distinct blockchains – a third party signs to trigger a certain outcome on one blockchain, ideally because a specific event occurred on the other blockchain (in this case, an asset was locked).

While this method turned out to be helpful for a quick transfer of the funds at risk into a safe wallet, it places enormous pressure on the owner of the single key to keep funds safe from hackers. It also requires trust in the project that they will not maliciously withdraw funds for themselves, which is precisely what played out in a recent “DeFi” scam incident.

To conclude, we’d like to encourage every project to create a security page on their platforms (website, social media, etc.) with clear instructions for how to responsibly disclose a vulnerability. By making it easier for security researchers and white-hat hackers to report vulnerabilities, projects can respond quicker and have a better chance of protecting users’ funds.