TrueUSD ↔ Compound Vulnerability

ChainSecurity
ChainSecurity
Published in
4 min readMar 21, 2022

TrueUSD (TUSD) is a stablecoin on the Ethereum blockchain. Until recently, it had multiple entry points, which could cause issues for various protocols. We discovered such an issue in our audit of the Compound cToken contract. The issue, with several millions at risk, has been mitigated by Compound and OpenZeppelin and a detailed write-up can be found here.

The Source of the Problem

How can a contract have multiple entry points? Well, a single contract can’t. In the case of TUSD, there were in fact two different contracts that both interact with the same balances. Essentially, the real implementation of TUSD is at the address 0x0000000000085d4780b73119b644ae5ecd22b376, and the secondary entry point 0x8dd5fbce2f6a956c3022ba3663759011dd51e73e simply forwards any calls to the primary contract. From an external point of view, this means that interacting with one of the contracts affects the balances of both. In most cases, this is not a problem, because you just supply one of the addresses to your contract and ignore the other. However, if you interact with both, you can run into issues.

Interacting with either contract will modify the same token balances

The Dangers of Interacting with Arbitrary Contracts

Why would a developer ever want to interact with two different addresses of the same token? Well, of course they wouldn’t. In fact, it never even makes sense to interact with the secondary address, since the cost will be higher and the outcome is the same. Hence, there is no well-meaning reason to call the secondary contract. But, if your contract can make calls into arbitrary addresses, this can be abused. In the specific case of Compound, the following function was susceptible to this:

As you can see, an arbitrary address of a contract that implements the transfer and balanceOf functions can be supplied. Interestingly, this function was only added so that users who accidentally transferred their tokens to the contract could rescue them.

So, how can we exploit this function? Let’s take a closer look at it. First, we make sure that the token we are sweeping is not the underlying token of the CToken contract. Next, we check the balance of our contract and transfer any funds it might have to the admin. Recall that TUSD has two entry points — if the underlying token is the primary TUSD address, but we call this function with the secondary address, the require condition will pass. Hence, the contract will transfer its entire underlying balance to the admin address. Luckily, the developers had the foresight to at least transfer the tokens to the administrator, otherwise this exploit would have been far worse.

How This Exploit Can Be Abused

Initially, it seems we can only transfer the underlying funds to the admin. This would effectively disable the contract, as no more funds could be borrowed or withdrawn, at least until the funds can be returned. While harmful, this is more of a griefing exploit and not profitable for the attacker. What else is affected by the missing funds? The exchange rate of the cTUSD token is calculated as follows:

The exchange rate of TUSD / cTUSD is defined as:
(totalCash + totalBorrows - totalReserves) / totalSupply
Here, totalCash is the total amount of TUSD in the contract, totalBorrows is the amount of TUSD borrowed from the contract, and totalReserves is the amount that belongs to the protocol. Lastly, totalSupply is the total amount of cTUSD that have been minted.
What happens to the exchange rate if all the underlying tokens are transferred away? It means that totalCash will be 0, the ratio of TUSD to cTUSD will drop and hence the value of cTUSD will suddenly precipitously drop. Later, when the funds are returned, the exchange rate will suddenly jump back to its initial value. How can an attacker profit? There are several options:

  1. Liquidate users who provided TUSD as collateral for their loans.
  2. Borrow TUSD, execute the attack, then pay back less TUSD.
  3. Execute the attack, then mint cTUSD. When the funds are returned to the contract, redeem the cTUSD for a profit.

Currently, TUSD can not be used as collateral, so the first attack does not work.
At the time we discovered this exploit, the second attack would have yielded a profit of around 12% of the TUSD contained in the contract, which would have been around $3.1 million. Since then, the amount of funds in the contract has only increased.

How To Avoid This Issue

During our audit sweepToken function behind admin privileges. Since the funds are transferred to the admin anyway, their involvement is necessary regardless, so they might as well execute the call themselves. Of course, this may not be the right fix in all cases. A more generally applicable fix could be the following:

Essentially, what we’re doing is checking that the contract’s underlying balance stays the same before and after the sweep. This does incur additional costs due to the cross-contract calls, but means we never have to worry about any other token with multiple entry points.

Conclusion

In the meantime, the secondary TUSD contract has been disabled, so the token now only has a single entry point. Naturally, this does not mean that this vulnerability can never arise again, as there might be more tokens out there with multiple entry points. For example, Tether USD (USDT) could enable a secondary entry point in the future when upgrading their contract. As a consequence, great care should be taken when calling into arbitrary contracts. If the caller can specify any contract that happens to conform to your required interface, you should expect that anything can happen.

--

--

ChainSecurity
ChainSecurity

ChainSecurity provides security audits and conducts research and development for blockchain platforms.