Started at 12:58:19 AM +UTC, Apr-18-2020, the DeFi platform, Uniswap, was attacked by a hacker using a reentry vulnerability. Around 24 hours later, at Apr-19-2020 12:58:43 AM +UTC, a similar hack occurred on Lendf.Me. Technically, the main logic behind these two incidents is the incompatibility between ERC777 and those DeFi smart contracts, which might be misused by the attacker to utterly hijack a normal transaction and perform additional illicit operations.

Specifically, in the Uniswap hack, the attacker exploits the vulnerability to drain the Uniswap liquidity pool of ETH-imBTC (with about 1,278 ETH)while in the Lendf.Me hack, the attacker makes use of it to (arbitrarily) increase the internal record of the attacker’s imBTC collateral amount so that she can borrow (and indeed borrow) a variety of 10+ assets from all available Lendf.Me liquidity pools (with total asset value of $25,236,849.44).

Meanwhile, it is also important to notice that ERC777 itself is a community-established token standard with its advanced features for various scenarios. However, these advanced features might not be compatible with certain DeFi scenarios. Worse, such incompatibility might further lead to undesirable consequences (e.g., reentrancy). We also notice that other token standards (e.g., ERC1155) have been similarly designed to have a callback function. Having said that, our manual review of the imBTC ERC777 implementation indicates that it indeed follows the ERC777 standard.


Figure 1: ERC777-Compatible transferFrom()

Root Cause Analysis

If we delve into the source code, the vulnerability lies in the internal logic to call the tokensToSend() function when the from address of the transferFrom() operation has registered itself as the implementer (through the standard ERC1820 interface). For illustration, we show in the following code snippets the context when the tokensToSend() function is called. In particular, as shown in line 1054, the getInterfaceImplementer() of ERC1820 is used to retrieve the registered implementer, if any. This particular function takes two parameters, from and TOKENS_SENDER_INTERFACE_HASH: the first argument is the essentially the attacker (e.g., the address supplying imBTC into Lendf.Me) and the second is a constant, i..e, keccak256("ERC777TokensSender"). Later on, in line 1056, the tokensToSend() function defined in the implementer is called, which allows the attacker to hijack the transaction by essentially injecting additional malicious code for execution.


Figure 2: ERC777-Compatible tokensToSend() Hijacking

As described in OpenZeppelin’s post in April 2019 and the proof-of-concept exploit published in last July, the attacker can setInterfaceImplementer() to setup the hook with a smart contract that defines the above-mentioned tokensToSend().


Figure 3: OpenZeppelin's Exploit Demo (Hook Setup)

Within the tokensToSend() function, the attacker can basically perform additional logic, e.g., selling the same batch of tokens multiple times.


Figure 4: OpenZeppelin's Exploit Demo (Hook Function)

Uniswap Hack

Since the theory behind the Uniswap hack has been described earlier in this post, we’re not going to elaborate further in this blog. Instead, we examine a specific malicious transaction (hash: 0x9cb1d93d6859883361e8c2f9941f13d6156a1e8daa0ebe801b5d0b5a612723c1). Evidently, there is an additional tokenToEthSwapInput() call embedded inside. It means the attacker can trade another batch of imBTC tokens for ETH when the conversion rate has been manipulated to the attacker’s advantage.


Figure 5: Uniswap Hack

Lendf.Me Hack

The Lendf.Me hack works slightly differently, but still in the same nature. If we examine a particular malicious transaction (hash: 0xae7d664bdfcc54220df4f18d339005c6faf6e62c9ca79c56387bc0389274363b), the deposit function, i.e., supply() in Lendf.Me is hooked by embedding an additional withdraw() operation, leading to the effect of increasing the internal record of the attacker’s imBTC collateral amount without actually depositing the amount.


Figure 6: Lendf.Me Hack

The logic behind is that the attacker did supply certain amount of imBTC into Lendf.Me in the first place (e.g., 289.99999999 imBTC). However, in the second supply(), the attacker simply supplied 0.00000001 imBTC but additionally withdraw()'ed 290 imBTC inside the hook (by hijacking the IMBTC::transferFrom() call inside doTransferIn() - line 1583). As a result, 290 imBTC was subtracted from the attacker’s balance within the embedded withdraw(). However, when the execution went back to supply(), the balance was reset to 290 imBTC (line 1599). That’s how the attacker manipulates the internal record of the attacker’s imBTC collateral amount in Lendf.Me. With the sufficiently large of collateral amount, the attack can therefore borrow all available 10+ assets from various liquidity pools (with total asset value of $25,236,849.44).


Figure 7: Lendf.Me Hack Details

Mitigation

As a common mitigation mechanism to block such reentrancy attacks, the so-called Checks-Effects-Interactions design pattern always helps. For example, if the Lendf.Me’s supply() calls doTransferIn() after saving user updates of token balance, there will be no chance that the attacker could reset the balance updates due to the withdraw() call.

On the other hand, the nature of ERC777 inevitably enables the hooking mechanism such that we need to prevent reentrancy in all trading functions. For example, if both supply() and withdraw() try to hold a mutex lock in the beginning of the function, bad actors cannot perform a withdraw() inside the body of a supply(). Last but not least, we might need to revisit the choice between ERC20 and ERC777 while noticing the advantage of choosing ERC777 is to facilitate the protection of user assets (as there is no need for users to approve all tokens to the platform for each transaction operation).

Aftermath

The Lendf.Me hack is a huge blow to current DeFi community. In the following, we put together the amount loss of various assets in this incident:

Asset Amount Loss in USD Percentage
WETH 55,159.02 $10,312,674.45 40.86%
USDT 7,180,525.08 $7,179,074.20 28.45%
HBTC 320.28 $2,325,787.05 9.22%
imBTC 290.35 $2,108,462.30 8.35%
USDC 698,916.40 $697,999.26 2.77%
PAX 587,014.60 $586,621.90 2.32%
USDx 510,868.16 $510,400.01 2.02%
BUSD 480,787.89 $480,690.74 1.90%
TUSD 459,794.39 $458,927.21 1.82%
HUSD 432,162.91 $431,687.92 1.71%
CHAI 77,930.93 $79,084.55 0.31%
WBTC 9.01 $65,439.83 0.26%

About us

PeckShield Inc. is an industry leading blockchain security company with the goal of elevating the security, privacy, and usability of current blockchain ecosystem. For any business or media inquiries (including the need for smart contract auditing), please contact us at telegram, twitter, or email.



Published

19 April 2020

Tags