Ethos Reserve contest - peritoflores's results

A CDP-backed stablecoin platform designed to generate yield on underlying assets to establish a sustainable DeFi stable interest rate.

General Information

Platform: Code4rena

Start Date: 16/02/2023

Pot Size: $144,750 USDC

Total HM: 17

Participants: 154

Period: 19 days

Judge: Trust

Total Solo HM: 5

Id: 216

League: ETH

Ethos Reserve

Findings Distribution

Researcher Performance

Rank: 6/154

Findings: 1

Award: $5,858.74

🌟 Selected for report: 1

πŸš€ Solo Findings: 1

Findings Information

🌟 Selected for report: peritoflores

Labels

bug
2 (Med Risk)
downgraded by judge
primary issue
satisfactory
selected for report
sponsor disputed
M-01

Awards

5858.7427 USDC - $5,858.74

External Links

Lines of code

https://github.com/code-423n4/2023-02-ethos/blob/73687f32b934c9d697b97745356cdf8a1f264955/Ethos-Core/contracts/Dependencies/TellorCaller.sol#L44

Vulnerability details

[H1] Low data feed frequency from Tellor makes you protocol vulnerable to flash loan attacks

Impact

​ An attacker can stale Tellor Oracle for several hours cheaply and perform a flash loan attack to profit.

PoC

To explain this issue I will first compare Chainlink to Tellor.

Most ERC-20 tokens are in general much more volatile than ETH and BTC. In Chainlink, there are triggers of 0,5% for BTC and ETH and 1% for other assets. This is to ensure that you are cutting error by those values.

Tellor, on the other hand, is an optimistic oracle. Stakers use the oracle system to put data on chain submitValue(..) that are directly shown in the oracle. The security lies the fact that data consumers should wait some dispute windows in order to give time to others to dispute data and remove incorrect or malicious data.

This is what happened in a Liquity bug found last year, they were reading instant data. [2]

Being explained this and back to your code you have essentially two bugs

First bug : Default disputetime = 20 minutes

In TellorCaller.sol you have the following statement

(bytes memory data, uint256 timestamp) = getDataBefore(_queryId, block.timestamp - 20 minutes)

Maybe you are using 20 minutes because this is the default value in Tellor documentation. However, in Liquity they are using 15 minutes for ETH because they say that have been made an analysis of ETH volatility behaviour.[2]

Basically there is a tradeoff between the volatility of an asset and the dispute time. More time is safer to have time to dipute but more likely to read a so old value. Less dispute time you have less error but no time to dispute can put you at risk of reading a manipulated value.

​ In your case, you are using more volatile assets so in theory, if Liquity analysis is correct, you should be using less time for ERC20 assets.

​ Of course this requires a deeper analysis but I am not doing it because the second bug makes this unnecessary as it has a higher impact.

Second bug : Data feed frequency in Tellor is very low so it is cheap to break

I will briefly explain some Tellor security designs.

Tellor bases his security design in an exponential cost to dispute. They have a several-round voting to dispute a single value but we are interested in the Cost of Stalling (CoS) the System.

To stale the system we need to dipute every single value for a given period, for a given asset.

According to whitepaper cost starts at baseFee and increase with the following formula

​ 𝑑𝑖𝑠𝑝𝑒𝑑𝑒𝐹𝑒𝑒𝑖𝑑,𝑑,π‘Ÿ>1 = 𝑑𝑖𝑠𝑝𝑒𝑑𝑒𝐹𝑒𝑒𝑖 Γ— 2 π‘‘π‘–π‘ π‘π‘’π‘‘π‘’π‘…π‘œπ‘’π‘›π‘‘π‘ π‘–π‘‘,π‘‘βˆ’1

Where

​ 𝑑𝑖𝑠𝑝𝑒𝑑𝑒𝐹𝑒𝑒𝑖 is the initial dispute fee (baseFee)

​ π‘‘π‘–π‘ π‘π‘’π‘‘π‘’π‘…π‘œπ‘’π‘›π‘‘π‘ π‘–π‘‘ is the number of disputes open for a specific ID

In Ethereum, there is a block every 15 seconds so stalling the system for 8 minutes (32 blocks) will cost and attacker around 2^32 * 10 TRB = 687 Billons of dollars! ... (10TRB = 160USD). Not bad at all.

Tellor team has similar values in different docs around internet.

​ However, this is nice if we always assume that one data is sent every block (a ideal system).

and here is where the real nightmare comes. Current frequency for data in Tellor is very low, that you are reading data once an hour or less!!.

Even worse for Optimism and Polygon basedisputeFee is only 1TRB . ( vs 10 TRB in Ethereum)

This design was thought considering that these chains are faster so if you data is sent every block then breaking the system would be prohibitively expensive. Again, security depends on the frequency of data

In our real word, Tellor is producing data in Optimism as low as in Ethereum so in the end it is 10 times cheaper to break.

Cost to Stale ETH/USD pair in Optimism

Lets calculate the CoS ETH/USD pair for 4 hours

Watching this Tellor contracts we can get that baseFee is 1TRB

https://optimistic.etherscan.io/address/0x46038969d7dc0b17bc72137d07b4ede43859da45#readContract ==> getDataFee() = 1TRB

Now, read data in a four hour range using the function.

getMultipleValuesBefore()

Parameters passed

queryId = 0x83a7f3d48786ac2667503a61e8c415438ed2922eb86a2906e4ee66d9a2ce4992 (ID for asking ETH/USD pair value)

timestamp = 1678147200 (7 march 2023 at 0:00)

_max age = 14400 (4 hours earlier = 14400 seconds)

_maxCount = 1000 (doesn't really matter)

We get only 4 values with the following timestamps [1678135628,1678139237,1678142833,1678146437]

The CoS Tellor ETH/USD pair for these four hours would have been

1TRB + 2TRB + 4TRB + 8 TRB = 15TRB

15TRB * 16USD = 240 USD

This means that for a little 240 bucks you can Stale 4 hours the Oracle which is not acceptable at all as I will show you an attacking scenario.

​ Ethereum has moved only 1%, not so critical this time, however volatille ERC20 used as a collateral can have much bigger changes.

You can query more data with different timestamps

Attacking Scenario: Flash Loan to profit

Steps:

  1. Write a contract that checks if Chainlink is working

  2. Meanwhile a second script that tracks values for all your collateral assets

  3. When Chainlink is broken do

  4. Stall all your collateral data from Tellor using dispute. (10 collateral for $2500)

  5. Suppose that you see an increase of 10% one of the colaterral call it ABC and ETH not moving so much

  6. Ask for a Flash Loan ETH in Uniswap

  7. Mint LUSD for ETH at Ethos

  8. Redeem LUSD for collateral ABC. You get a 10% discount because Oracle is staled 4 hours ago

  9. Exchange LUSD for ETHEREUM in Uniswap.

  10. Return ETH to the flash loan plus interest

  11. Enjoy!

​

Note that this attack can be improved if you perform the loan on a falling collateral to mint more LUSD.

The only level of protection you have is the fact that Chainlink is working, in Liquity it is more difficult because it should be an important change of ETH value in those 4 hours.

There are two solutions in my opinion

Solution 1 : Tellor tip mechanism

Tellor whitepaper:

"Parties who wish to build reporter support for their query should follow best practices when selecting data for their query (publish data specification on github, promote/ educate in the community), but will also need to tip a higher amount to incentivize activity"

​ This means that in order to use data safely you need to pay to be sure that frequency is secure taking into account the impact of the volatility and the time to dispute.

I din't mention earlier but the cost to dispute is exponential but capped by the staking amount of the reporter, so no real billons of dollars in fact.

​ Here is the documentation how you can fund for a feed https://docs.tellor.io/tellor/getting-data/funding-a-feed

Solution 2. Do not use Tellor

​ Unlike Liquity, you need to fund several feeds so I don't know if this is cost effective but you have options to fund fees only when Chainlink is broken but you need to investigate on that.

​ In any case you have a function to set the oracle that I am reporting as medium so no sure if you need to use two oracles.

References.

  1. Tellor White Paper https://tellor.io/whitepaper/
  2. Liquity Tellor issue 2022 https://www.liquity.org/blog/tellor-issue-and-fix

#0 - trust1995

2023-03-08T10:21:54Z

This submission is interesting, but may be invalid. The cost of stalling calculation assumes that attacker would only need to stall 4 price updates in 4 hours, however once stalling starts it would make sense for new price reports to appear very quickly, as soon as the next block. In other words, the sample warden has looked at only contains 4 updates because they were all legit.

I have not verified this reasoning, but ask for sponsor to take a look and give their thoughts.

#1 - c4-judge

2023-03-08T15:07:02Z

trust1995 marked the issue as primary issue

#2 - c4-judge

2023-03-08T15:07:08Z

trust1995 marked the issue as satisfactory

#3 - tess3rac7

2023-03-13T14:49:48Z

It relies on ALL of the following to be true:

  • chainlink broken
  • tellor stalled for > 4 hours somehow by a malicious attacker
  • no other tellor reporter realizing/recognizing this
  • sharp movement in one collateral price during that timeframe
  • not much movement in another collateral price during that timeframe

Probability of all of the above happening together almost negligible. Moreover, it seems the report doesn't take into account:

  • Redemption fee, which could be a very large % if the attacker is looking to drain the system

Also forwarded this to our contacts at Tellor and this was their take:

So for the first one, it’s fair. A lot of times people have trouble waiting the 20 minutes and there always is a risk that the price will change drastically in those 20 minutes, but unfortunately it’s just something that happens when you deal with decentralized systems (e.g. exchanges wait several blocks for confirmation, Maker waits an hour before updating its oracle). And not to mention, the price can move very drastically even in the 12 second block time of Ethereum, so if the goal is to never have a stale price, it’s literally impossible. You just need to design a system that can slow down and won’t break if this happens (which Liquity did and I’m presuming you guys did something similar)

For the second β€œbug”, the main flaw comes in the presumption of no outside actors looking to save the system or benefit from the attack. When a good value is disputed on tellor, this is actually a profitable opportunity for any reporter. Assuming the voting mechanism is not broken (as in the analysis), anyone who simply submits a good value will (after 4 rounds), double their TRB in 2 days. (the voting period). This means that for 4 hours, you would need all of the reporters to not realize this. This is a similar assumption as saying you could throw a uniswap pool and expect no one to see an arbitrage opportunity. The second false assumption is that the current optimism report rate is how often the ETH/USD feed will update forever. This is just wrong. There are few reports on Optimism because there are no tips on Optimism and no one has even told any reporters (or the team) that they are live and would like more reports. For relatively cheap, you can easily have several reports an hour, and even more if you’d like to pay for it. Honestly however, you should probably just keep it on a 4 hour pace or a something that looks at the price change so as you aren’t over-paying or updating a same value.

Additionally, there are measures that you as a team could take to dissuade an attacker. For one, you could move the 4 hour stale period longer (no reason 4 hours is a golden number). Second, you can stake reporters and report yourself. If you have a large number of stakes ready to report on Optimism, you could simply listen for disputes and act as a reporter of last resort while you alert the other reporters to come and join the fun. Simply having this amount publicly known or ready would probably be enough to raise the costs to a high enough to prevent any attack.

Based on all of the above, I'm leaning towards "not an issue."

#4 - c4-sponsor

2023-03-13T14:49:56Z

tess3rac7 marked the issue as sponsor disputed

#5 - peritoflores

2023-03-14T13:27:18Z

Hi Team, sorry for that but it is either I am so wrong or you could be drained.
First, I have explained @trust1995 that reporters cannot just send reports every block after being disputed. This depends of how much they have staked.
image

Secondly, there are only two reporters to protect the real system with a total staked valued at 5kUSD

TELLOR ORACLE AT OPTIMISM

image

Tellor reporters are

0x50a86759d495ecfa7c301071d6b0bdd4bd664ab0 ---> 200 trb locked 0xaac7da260fb6d047314e213f672b7d3d9503a1f7 ---> 130 trb locked

Unless people is watching off-chain to bridge TRB to optimism, dispute, stake and then submit a new value it is possible to take the oracle with only 5k or less.
After disputing is not mandatory to submit a value.

About Tellor answer

I agree with almost all they said. The dispute is profitable even to double your money, but I am just interested in stale the system. The question is that if it is possible to get a correct value on time. What they explained about tips, of course I agree, in fact it is the solution that I proposed.

About your words

chainlink broken

---> This is the only condition

tellor stalled for > 4 hours somehow by a malicious attacker --->

This is just an example can be more or even less time.

no other tellor reporter realizing/recognizing this -->

As I explained you there are only 2 reporters in the live system to protect you

sharp movement in one collateral price during that timeframe not much movement in another collateral price during that timeframe

That happened last weekend when USDC was falling down

On March 11 From 1:00 AM to 5AM USDC lost almost 15% of its value. However other cripto where not moving.

Note that uniswap fees are only 0,3% plus gas cost.

An improved flash loan attack to drain collateral

Note that this attack is devastator because it is like an incredibly profitable arbitrage that can be performed in every block.

While in arbitrage oportunities prices got balanced after the swap.

Here the attacker can still hold the oracle and repeat the attack.

#6 - trust1995

2023-03-20T10:21:30Z

  1. Ethos relies on Tellor and plenty of logic faciliates the use of both CL/Tellor oracles, so the assumption of Chainlink downtime is definitely in-scope for medium severity.
  2. Having heard both sides, it seems the required attack indeed does not require unreasonble amount of effort by an attacker (esp. regarding two live reporters).
  3. Given the execution of the attack is possible, likelihood of it generating big profits at protocol's expense is high.

For these reasons, medium severity is most appropriate here.

#7 - c4-judge

2023-03-20T10:21:38Z

trust1995 changed the severity to 2 (Med Risk)

#8 - c4-judge

2023-03-20T15:47:20Z

trust1995 marked the issue as selected for report

#9 - tess3rac7

2023-03-20T15:59:26Z

I'm still unclear as to how we can overlook redemption fees and claim that this will be profitable for an attacker looking to drain collateral.

Redemption fees scales with:

If the attacker decides to redeem in one large TX, the fee will be very high, not nearly enough to offset the hypothetical 10% gain in the warden's example. Here are some discord messages of Liquity's dev explaining: Screenshot from 2023-03-20 11-55-09

If the attacker, decides to redeem in smaller successive TXs, the base rate would keep increasing as explained in the blog post linked above.

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax Β© 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter