During a comprehensive audit of the Compound Finance protocol, OpenZeppelin reviewed a vulnerability in the cToken smart contract for TrueUSD (TUSD) previously discovered and reported to Compound Labs by ChainSecurity. OpenZeppelin determined that the vulnerability was not limited only to Compound, but had wider implications for the DeFi (decentralized finance) community as a whole. As many as 30 DeFi protocols may have been similarly affected.
OpenZeppelin developed a plan to protect Compound while also preventing exposure for other protocols and the broader DeFi ecosystem. As a result—and in coordination with OpenZeppelin—TrustToken developed and tested a patch for the vulnerability. The patch was successfully deployed on February 23, 2022, fixing the root cause of the vulnerability with no funds lost. OpenZeppelin has chosen to make the details of this incident public to advance the conversation on best practices for token integration and vulnerability disclosure.
The Compound community is committed to strengthening the security and functionality of its protocol. In keeping with this commitment, Compound recently retained OpenZeppelin as its protocol security solutions provider, which led to OpenZeppelin’s involvement in resolving the vulnerability.
To contextualize the vulnerability and OpenZeppelin’s response, it is helpful to have an understanding of Compound’s business model, the Compound Governance process, and the TUSD stablecoin.
Compound Finance is one of the most widely used protocols in the DeFi ecosystem. Deployed on Ethereum, its purpose is to issue automatic, permissionless loans of Ether and various ERC20 tokens. As of February 2022, the protocol held more than $10 billion in assets across 18 markets.
To invest in Compound, users deposit Ether or supported ERC20 tokens into one of the protocol’s markets. In exchange, they receive cTokens for that market, with which they can redeem their investment. Compound’s cTokens are differentiated and denominated according to the underlying asset. For example, investors who deposit Ether (ETH) receive cETH tokens, which are redeemable for ETH. Similarly, investors who deposit USDC receive cUSDC tokens, which are redeemable for USDC, and so on. In addition to being redeemable for the underlying asset, cTokens can be traded according to their market value.
The total value of each market increases as funds are lent out and repaid. As the value held in a market grows, the cTokens for that market increase in value and can be redeemed for more of the underlying asset. In this way, investors accrue interest. When the protocol operates as intended, the value of a cToken relative to its underlying asset should only increase, i.e., only positive interest rates should be possible.
Although originated by Compound Labs, Compound Finance has evolved into a DAO (Decentralized Autonomous Organization). As such, it is governed by consensus among its community of tokenholders through a process controlled by a Governance smart contract. In this governance process, proposals are initiated by the community and voted on by its members, whose votes are weighted according to their relative holdings of the community’s governance token, COMP.
Once a proposal is initiated by a member of the Compound community, it cannot be voted on until two days later. Voting remains open for a period of three days. This schedule ensures that COMP holders are able to debate the proposal in the community’s forums and come to an informed decision. Successful proposals are entered into a Timelock, where they are held inert for two days before coming into effect. This allows investors who wish to do so to withdraw or reallot their assets in the intervening period.
While this process offers the Compound community significant benefits in terms of transparency, it greatly complicates the process of fixing a live vulnerability. Proposed security fixes are publicly visible for seven days, during which anyone can view them and attempt to discover the corresponding vulnerability.
In anticipation of this issue, the community created a privileged role known as the Pause Guardian. The Pause Guardian is controlled by a multisig (multi-signature Gnosis wallet) that is composed of six trusted signers from the community. The Pause Guardian can suspend specific market functions on Compound—except those in which users withdraw their own funds—instantly, outside the governance process described above. However, the Pause Guardian cannot reinstate paused functions. Reinstatement must happen through the Compound Governance process.
TUSD is a stablecoin originally developed by TrustToken. It is tradable for assets and redeemable for fiat currency. It has very low volatility, and can be used as a hedge against market instability. All TUSD in circulation is collateralized on a 1:1 basis by U.S. Dollars held in escrow by banks, and these reserves undergo continuous attestations by a third-party. As of February 2022, $1.5 billion worth of TUSD was in circulation. At that time, approximately $88 million was invested in Compound, making Compound’s TUSD market its eighth largest. Investors who deposit TUSD into Compound receive cTUSD tokens, which have the properties outlined for cTokens above.
ChainSecurity discovered a critical vulnerability in the Compound-TUSD integration during a security audit of the Compound cToken smart contracts conducted prior to OpenZeppelin’s security partnership with Compound. It is important to note that the vulnerability did not put user funds deposited with TUSD at risk, but it did affect the amount of TUSD redeemable by Compound’s investors.
What follows is a high-level introduction to the vulnerability and its implications for Compound and the wider DeFi community. For a more detailed technical description of the problems of double-entry points, ChainSecurity has written a helpful blog post, which can be found here .
For the purposes of this discussion, it is helpful to have a layman’s understanding of three concepts that made it possible for the vulnerability to occur: (1) the difficulty of upgrading smart contracts; (2) the way in which Web3 developers deal with stray tokens; and (3) the process by which markets are established on Compound.
First, consider the difficulty of upgrading smart contracts. Unless potential upgrades are contemplated in advance, Web3 smart contracts are immutable, i.e., impossible to update. This is a feature, not a bug. It allows users to use them without having to trust third parties. However, this also means that it can be difficult to fix bugs or add functionality.
Modern implementations of upgradeability rely on the deployment of two contracts: a first contract, which operates as a proxy, and a second contract, which contains the logic. Users interact with the proxy contract, which “wraps” the logic contract, i.e., forwards directions to it. Both contracts remain immutable, but the logic contract can be replaced, enabling upgrades. (A helpful discussion of Proxy Upgrade Patterns can be found here.)
TrustToken’s original deployment of TUSD predated this proxy-based approach, anticipating upgrades in a different way. It had been built as a system that forwarded calls to specific functions (e.g., transfer) to a new contract—then as-yet undeployed. This would allow the TUSD team to deploy such a contract in the future if it needed to make changes. When the need for changes eventually arose, TrustToken deployed a second TUSD contract. (This new contract was upgradeable—a fact that was essential to the eventual resolution of the vulnerability for the wider DeFi community.)
From a user’s point of view, nothing changed. Underneath the hood, however, if someone called the transfer() function on the legacy contract, the call would be forwarded to the new contract.
Second, consider the problem of stray tokens. In the Web3 environment, it is possible for a wallet to send tokens to any smart contract, even if doing so has no purpose in keeping with the intended use of the smart contract. This can result in tokens becoming stuck. To address this problem, many smart contracts contain a function that sweeps stray tokens into a wallet so that an administrator can return them. All cToken smart contracts contain such a sweepToken function, which is configured to sweep stray tokens into the Compound Timelock described above. It is important to note that the function includes a check to ensure that the token address parameter to be swept is not the same as the underlying asset.
Third, consider the process of establishing a market on Compound. The basis of each of Compound’s markets is an implementation of the cToken contract. The cToken contract is a configurable smart contract template designed to integrate other ERC20 smart contracts (i.e., tokens, such as TUSD, DAI, BAT, etc.) with the Compound protocol. Once configured for the underlying asset and deployed, a cToken contract constitutes a market. It accepts investments of the underlying asset and supplies depositors with corresponding cTokens. The cToken contract template has been audited before and is considered secure.
The “two-contract” implementation of TUSD was already in place at the time the Compound TUSD market was inaugurated. As the cToken contract template can only hold one underlying token, the TUSD team configured the template to interact with the newer TUSD contract implementation. Unfortunately, this created a double-entry point through two different token addresses, which allowed an attacker to sweep all TUSD tokens in the cTUSD contract into the Timelock—not just stray tokens. This behavior could be triggered by anyone by calling:
function sweepToken(EIP20NonStandardInterface token) override external { require(address(token) != underlying, "CErc20::sweepToken: can not sweep underlying token"); uint256 balance = token.balanceOf(address(this)); token.transfer(admin, balance); }
The behavior described above has implications for the redemption of TUSD deposited with Compound. Compound’s underlying token/cToken exchange rate is calculated based, in part, on the amount of underlying tokens available to be borrowed. If all of a market’s underlying tokens are swept, the interest rate on positions opened before the exploit decreases—and has the potential to drop below zero. To take advantage of this phenomenon, an attacker would call the function described above and then deposit a large amount of TUSD. The attacker, along with any other suppliers, would get a massive discount on the newly opened positions at the expense of previous depositors.
It would be possible to return the swept tokens to the cTUSD contract, but deposits made before that could be done would cause irreparable damage. At the time TUSD deployed its patch, Compound’s TUSD market held approximately $88 million worth of TUSD, of which roughly half was redeemable and roughly half had been lent out. If exploited to the greatest extent possible, this vulnerability would have approximately halved the value of any redemptions made using cTUSD tokens issued prior to the exploit.
The Compound DAO hired OpenZeppelin as its protocol security partner at the end of last year under the terms outlined in Proposal 76. OpenZeppelin was made aware of the vulnerability during its comprehensive audit when Compound Labs provided the team with a private version of ChainSecurity’s audit report. OpenZeppelin took ChainSecurity’s report of this issue as an input into its own baseline audit.
Prior to OpenZeppelin’s involvement, the plan in place to address the vulnerability was to roll the fix into an ongoing cToken refactoring project being conducted by Equilibria under a Compound grant. This “silent fix” approach was intended to remedy the problem while calling the minimum amount of attention to it. OpenZeppelin was made aware of this plan, and elected to investigate the matter further before making its own recommendation.
On February 4, the TUSD team initiated Proposal 84, which would have made updates to the TUSD market. OpenZeppelin had concerns. First, OpenZeppelin had not received sufficient prior notice of the proposal to assess the potential need for a security review. Second—and more important—OpenZeppelin was concerned that the proposal could draw undue attention to the cTUSD contract. Overall, the proposal created risks that were simply better to avoid. Therefore, OpenZeppelin recommended that the community wait to approve the proposal until it had been fully reviewed.
Unbeknownst to the community—and at the same time that it publicly recommended voting against the proposal—OpenZeppelin also privately reached out to both the TUSD and TrustToken teams to alert them to the existence of a vulnerability. At the time, it appeared that the vulnerability was Compound-specific, so in consultation with the Compound Multisig signers, OpenZeppelin opted not to disclose any details before the issue was remedied. OpenZeppelin pledged to explain the situation further once it had been resolved.
Ultimately, the proposal was defeated—but it had altered the way OpenZeppelin triaged the components of its baseline audit. The deployed cTUSD contract was now an even higher priority.
In conducting its own due diligence, OpenZeppelin confirmed ChainSecurity’s finding of a critical vulnerability. In light of Compound’s Governance structure—which required that the fix would be publicly viewable for seven days before it took effect—OpenZeppelin noted that the planned “silent fix” posed certain risks. Seven days was potentially enough time for a skilled attacker to discover the vulnerability and exploit it.
OpenZeppelin also came to an additional unsettling conclusion: variants of the same vulnerability existed in other DeFi protocols. None of the variants that OpenZeppelin discovered were as severe as Compound’s, but their existence demonstrated that the “double entry” vulnerability was not Compound-specific. As TUSD is listed on as many as 30 DeFi protocols, it was certainly possible that other critical vulnerabilities existed in the DeFi ecosystem.
This discovery raised a new consideration. Up to this point, OpenZeppelin had not envisioned involving the TUSD team in the resolution. However, since the team now understood that the vulnerability was systemic rather than Compound-specific, it reasoned that the TUSD team would be best positioned to implement a fix at the root of the problem. On the other hand, OpenZeppelin also assessed that such a fix might have adverse impacts on markets using the legacy smart contract implementation, which could pose a difficult business decision for TUSD.
In considering its next move, OpenZeppelin weighed how to best manage the risks involved.
It reasoned that the probability of an exploit was a function that multiplied the degree of the exposure by the duration of the exposure. Over a long enough duration, the probability that an unpatched vulnerability would be exploited approached a certainty. While estimating the value of these factors was a subjective exercise, doing so nonetheless informed OpenZeppelin’s approach. In essence, it had two options:
OpenZeppelin decided that the optimal strategy was to pursue Option 2, but retain Option 1 as a fallback position. To limit the duration of the exposure, OpenZeppelin would explain the vulnerability to the TrustToken team and give them 24 hours to commit to implementing a fix. Failing that, the Pause Guardian would halt Compound’s TUSD market, and Equilibria would propose its refactored cTUSD contract.
In the event that TrustToken signaled it was unwilling to take action in the 24-hour timeframe, OpenZeppelin planned for the Pause Guardian to halt the TUSD market immediately. In such an event, OpenZeppelin was also prepared to execute a contingency plan for notifying other affected projects and protocols.
OpenZeppelin presented this plan to the signers of the Compound Multisig at a meeting on February 22 at 12 PM U.S. Central Time (CST). They approved it and stood by for any indication that the Pause Guardian should halt the TUSD market.
Immediately following its discussion with the Compound Multisig signers, OpenZeppelin contacted TrustToken and TUSD teams to request an urgent meeting. Due to time zone differences, the TUSD team responded shortly after 7 PM CST. They were eager to meet. The two teams agreed to set up a call for 7 AM CST the following morning to accommodate participants in all relevant time zones.
In the meeting, OpenZeppelin demonstrated the vulnerability and explained its implications. For its part, TrustToken quickly came to a decision to take action while still in the meeting, committing to remedy the vulnerability within 18 hours. OpenZeppelin updated the Compound Multisig signers on TrustToken’s prompt decision.
The two teams were in touch throughout the day on February 23. OpenZeppelin kept the Compound multisig signers apprised of the progress being made. The TrustToken team developed a patch that blocked calls from the legacy contract to the current implementation and deployed it at approximately 10 PM CST.
OpenZeppelin ran an exploit test, which confirmed that the vulnerability was no longer present. Shortly thereafter, OpenZeppelin reached out to report the good news that Compound and the other affected protocols had been secured to the Compound multisig signers. They congratulated the team: “Thank you, OZ, for taking care of this! Incredible outcome!”
Following the resolution, Joyce of the TrueUSD team reflected on the process: “We really appreciated how OpenZeppelin brought the matter directly to us. Our teams worked together like partners to find the best solution possible for a shared goal—ensuring a secure DeFi system.
This retrospective highlights certain points that may be instructive for protocols seeking to improve their security posture or for security researchers working in the Web3 space. While several lessons could be drawn from this incident, OpenZeppelin believes two in particular deserve mention.
First, it is essential to exercise due care in integrating a new, non-native token into a protocol. The fact that the smart contracts for a protocol and a non-native token have both been audited does not necessarily mean that the integration between the two is secure. The integration itself constitutes an important part of the attack surface. OpenZeppelin notes that the vulnerability described above had existed since the inception of Compound’s TUSD market on May 21, 2021.
Second, OpenZeppelin believes this incident highlights the importance of collegiality and respectful collaboration among Web3 professionals. After the resolution, the TrustToken team said, “To all teams involved, thank you for the excellent work, and we look forward to further collaborations!”
The OpenZeppelin team echoes this statement wholeheartedly. The Security Research Team believes that this incident reached a successful resolution because all involved parties demonstrated high levels of technical skill, professional conscientiousness, and diplomatic ability. OpenZeppelin commends the TrustToken and TUSD teams on their expeditious business decision to remedy the vulnerability and their rapid and successful implementation of that remedy.
OpenZeppelin also wishes to commend the signers of the Compound Multisig, who intuitively grasped the situation and demonstrated calm resolve and due care for the wider community. The OpenZeppelin team looks forward to continued successful collaboration on security matters with Compound.
Finally, OpenZeppelin again wishes to convey its respect to ChainSecurity. The vulnerability was in code that was not part of OpenZeppelin’s originally intended audit scope; if ChainSecurity had not found it, it would not have been patched at this time. OpenZeppelin believes the future security of Web3 will be a shared enterprise, and its Security Research Team considers itself fortunate to be able to draw on the insights of talented industry colleagues.
In conclusion, OpenZeppelin notes that while vulnerabilities are relatively common, that is no reason for complacency. The open-source nature of the DeFi industry means that such vulnerabilities can be found by anyone. OpenZeppelin is committed to acting ethically in protecting its clients and the wider Web3 community. OpenZeppelin encourages DeFi developers to incorporate battle-tested security tools—such as Defender, Forta, and OpenZeppelin Contracts—into their projects.