The Euler Labs team recently deployed a preventative update to the Euler smart contracts that protects contracts integrating with Euler against a class of manipulation attacks. As far as we know, no users were ever exploited by this issue, but it is quite an interesting attack vector and very likely applies to other projects as well.
Background
Like many other DeFi systems, Euler created a wrapper asset around deposited tokens. For example, eDAI is a wrapper for DAI. Since this is an interest-earning asset, each unit is redeemable for a growing amount of DAI. At any given point in time, the amount of DAI a user can receive for a unit of eDAI is given by the “exchange rate”. This value is computed by dividing the pool’s assets (balance plus outstanding borrows) by the total outstanding eToken balances. Protocols that integrate with Euler may use the exchange rate to compute the underlying value of an eToken deposit by calling convertBalanceToUnderlying()
on an eToken.
On July 25th, Sherlock notified the Euler Labs team about a bug bounty submission they received for their staking system, which uses Euler. It was a sophisticated attack that relied on the interactions between three separate protocols: Euler, Sherlock’s staking system, and the 1inch DEX aggregator.
The bug bounty detailed that the swap module allowed a user to exchange one eToken for another using either Uniswap or 1inch. However, with 1inch, the swap data was treated as a black box by the Euler contracts, and the 1inch router allowed the invocation of arbitrary contracts (including attacker created ones). Fortunately, the Euler system used a reentrancy guard that prevented re-entering into Euler at that time.
Unfortunately, during the 1inch execution, after a swap was performed the funds would have been moved into the Euler contract but have not yet been credited to the total balances, and in fact cannot have been because it did not know the precise amount that would be returned from 1inch. This meant that at this point calling convertBalanceToUnderlying()
would return an incorrect value. An attacker could have invoked a victim contract, such as Sherlock’s staking contract, and it would have used an incorrect exchange rate for its computations.
Another way of describing this is that Swaps were not atomic operations in Euler: They consisted of underlying token transfers, followed by updates to EToken balances. Critically, due to the 1inch router’s flexibility, an attacker could have run code in between those two operations.
Mitigation
The Euler Labs team together with Sherlock discussed several ways of fixing this issue. Simply removing 1inch swapping support altogether was considered, but this is undesirable because Euler users can often receive better prices and execution from 1inch. Additionally, there may be other opportunities for attackers to run arbitrary code at times when Euler’s internal data was in an inconsistent state, so this was not a comprehensive solution.
Improving the atomicity of the swapping operation, or sanitising the 1inch “black box” swap data prior to a swap were other options, but these were brittle and still not comprehensive fixes.
The option selected was to add what we are calling a read-only re-entrancy guard. This works by having convertBalanceToUnderlying()
(and all other methods that use the internal loadAssetCacheRO
utility) verify that the re-entrancy guard has not been locked. If it has, then an error is thrown. Typical integrations will propagate this error and revert, preventing an attacker from taking advantage of inconsistent exchange rates.
On Aug 4th, this fix was deployed, which transparently fixed the issue for all existing and future Euler integrations.
While this was technically a breaking change, existing integrations were analysed and future users were considered before determining that this restriction would not cause any significant issues for legitimate users. Instead, this provided a comprehensive solution to the attack identified in Sherlock’s bug bounty, as well as any future cases where users are able to execute arbitrary code during the execution of an Euler operation.
Thanks
Euler Labs would like to thank the Sherlock team, who brought this issue to attention and collaborated on designing and deploying the mitigation described above.
The Labs team would also like to thank the teams behind the various other Euler integrations for acting promptly and professionally to ensure no users’ funds were at risk.
About Euler
Euler is a capital-efficient permissionless lending protocol that helps users to earn interest on their crypto assets or hedge against volatile markets without the need for a trusted third-party. Euler features a number of innovations not seen before in DeFi, including permissionless lending markets, reactive interest rates, protected collateral, MEV-resistant liquidations, multi-collateral stability pools, sub-accounts, risk-adjusted loans, and much more. For more information, visit euler.finance.
Join the Community
Follow Euler on Twitter. Join the Discord. Keep in touch on Telegram (community, announcements). Check out the website.
This content is brought to you by Euler Labs, which wants you to know a few important things.
This piece is provided by Euler Labs Ltd. for informational purposes only and should not be interpreted as investment, tax, legal, insurance, or business advice. Euler Labs Ltd. and The Euler Foundation are independent entities.
Neither Euler Labs Ltd., The Euler Foundation, nor any of their owners, members, directors, officers, employees, agents, independent contractors, or affiliates are registered as an investment advisor, broker-dealer, futures commission merchant, or commodity trading advisor or are members of any self-regulatory organization.
The information provided herein is not intended to be, and should not be construed in any manner whatsoever, as personalized advice or advice tailored to the needs of any specific person. Nothing on the Website should be construed as an offer to sell, a solicitation of an offer to buy, or a recommendation for any asset or transaction.
This post reflects the current opinions of the authors and is not made on behalf of Euler Labs, The Euler Foundation, or their affiliates and does not necessarily reflect the opinions of Euler Labs, The Euler Foundation, their affiliates, or individuals associated with Euler Labs or The Euler Foundation.
Euler Labs Ltd. and The Euler Foundation do not represent or speak for or on behalf of the users of Euler Finance. The commentary and opinions provided by Euler Labs Ltd. or The Euler Foundation are for general informational purposes only, are provided "AS IS," and without any warranty of any kind. To the best of our knowledge and belief, all information contained herein is accurate and reliable and has been obtained from public sources believed to be accurate and reliable at the time of publication.
The information provided is presented only as of the date published or indicated and may be superseded by subsequent events or for other reasons. As events and markets change continuously, previously published information and data may not be current and should not be relied upon.
The opinions reflected herein are subject to change without being updated.