New Ethereum talks, every Monday. The week's conference uploads by event, in your inbox.

Double entry point issues - From breaking Compound to Uniswap v4

Devcon 7 SEAFri, Nov 15, 2024, 01:15 PM · 09:09

A short explanation of a critical-severity vulnerability we found in the Uniswap V4 core contracts that would have caused a ~$15M loss in Uniswap's pools. The goal is to explain the risks of double entry points, from the $30M+ TUSD issue in Compound to the Uniswap V4-specific case where protocols use native tokens and operate on chains where the native token has a corresponding ERC-20 token, and how to prevent them.

Transcript

My name is J. Carpanelli. As he already said, I'm Head of Security Services at Open Zeppelin. I'm here today to talk to you about double entry point issues. So, first things first, what is the double entry point?

As we all know how Solidity works, users or protocols interact with contracts by knowing where the address is. So basically they know the address and that address is pointing to a specific smart contract. A double entry point happens when actually there's not only one address that points to that storage of the contract, but actually there's many addresses. Could be two, could be two could be n and also protocols can talk to this double entry point so not only users but also protocols but also tokens basically anything that can interact with an address can interact with a contract that can have more than one entry point this can have a couple of problems, which are unclear to end users to which contract they should be talking to, unclear to protocols which contracts they should be interacting with when they're integrating, based on the fact that basically all smart contracts are interacting with other smart contracts all the time, and that introduces security caveats that we're going to see now with some examples. So, first case, compound 2022.

This was an issue that we found at the beginning of 2022 in a token named TUSD. This TUSD token had two entry points, so basically anyone would be able to, for example, if you want to transfer some of your TUSD tokens from one address to the other, you could use any of the two entry points that you had. And how it worked, the legacy address, they did some kind of migration. This legacy address would, when you talk to the transfer function, would forward the transfer function to the new contract. And the same with all the functions defined in the contract.

And again, as I said, not only with users, also with protocols. And the case was Compound, the ctoken contract had a sweep token function that had here a required statement that you can see that would check that the address that you want to sweep, the typical sweep token, the sweep token function that you want to remove certain tokens of a specific contract, will check that the address that you're sending wasn't the underlying. So if you have the CTUSD token and you want to sweep all the TUSD tokens, you shouldn't be able to do that. But if you want to sweep the DAI tokens, you should be able to do that and send it to the admin of the contract that I think it was the time lock button. The problem is that the underlying could be two addresses.

So actually you can call this function, bypass the required statement, and actually move all the funds that were in the C token or the C to USC contract back to the admin, basically creating chaos. One of the, or a couple of things that we saw that happen is that the C token to token price got completely screwed, so any mint or any burn would account wrongly. And also there was the possibility to steal funds from the pool because all the calculations were made wrong. So if an attacker would send a transaction to the sweep token function to reduce to zero the underlying token balance and then touch a little bit what was happening in the, like, send other transactions to other functions would have been able to steal around $50 million. Second case, 2024, UNICEF v4.

We did an audit for the v4 core code, and we found that the case was with another representation of double entry points that happens not when you have two addresses, but when you have a native token and an ERC20 representation of that ERC20 token. And basically, users can interact with both at will as well as protocols. Uniswap, we're not going to talk much about Uniswap because it's very complex, but there's a settle function. This settle function allows users basically to modify their account deltas, which is basically the amount of money that they hold in the pool manager at will by adding tokens or removing tokens. And it has this if statement that actually checks if the currency that we want to interact with in the pool manager is the native token.

And if it's not the native token, it will enter to the else. So basically, we can interact with in the pool manager is the native token. And if it's not a native token, it will enter to the else. So basically, we can interact with another entry point. And an attacker could call the sync function, selling the Zillow address.

The issue was actually with the, I didn't mention it, but it was with the Zillow token address. Basically, to put in transient storage the Zillow address to then interact and do other operations. Then they could call the settle function that would enter to the if statement and account the value that you're sending as a parameter as the new account delta, so the new balance of the user. And then call the other settle function. I mean, it's the same settle function, but would enter to the else statement and will basically account for 20 tokens instead of 10.

It's the same set of functions, but we'll enter to the else statement. And we'll basically account for 20 tokens instead of 10. And in the end, the user would only deposit 10 tokens, but get a balance of 20. And then the same user could, without any problems, take that money from the pool. If they do this many times, or if they use higher values, that is not 10, they could drain the pool.

So in v3, CLO has like $15 million of TBL, I believe, and if they were to migrate things from v3 to v4, it would have cost around $50 million of losses. So some takeaways. First of all, this is an issue that we found in 2022. Also in 2024. It's an issue that can still happen because it doesn't depend on our protocol.

It depends on other protocols that we integrate with. So we should never trust any protocol or token that we interact with. Always double check that when we integrate with a protocol or a token, it doesn't have a double entry point. And actually, if we do, we have to also check and code in a defensive way so that if they introduce a double entry point, we can account for that and we don't have issues. And that's all.

Thank you very much. Thank you, Jota. And thank you again for all that you are doing for the security of our apps. Okay, we have the full room almost, so I expect some questions. Raise your hand, please.

You can just say something nice to the speaker. That's also accepted. Are you going to say something nice to the speaker. That's also accepted. Are you going to say something nice or you have a question?

Let's see. Let's see. Yeah, hello. So with the race of AI that can create code, how do you think AI will blend into the development? How would you blend this into development with AI?

Yeah, how are you going to use AI to basically secure check the system? It's a good question. You can write rules around the things that you want to check around double entry points and put it in an LLM to learn from different code bases and maybe check if they have a double entry point issue. You could also use AI to see if storage is modified by more than two addresses out there, I guess. Okay, thanks for the question.

More? Yes, there in the back. In Arremeda. Thank you. Yes, there in the back.

Thank you. I want to ask if there are many special tokens, like this double entry, some with hooks. So why is there not any framework that kind of specifies testing tokens like these ones? So like do fasting or this kind, or I know just standardize the testing for all the special tokens that we know that might have prevented, for instance, this issue in development. What do you think about this?

I think that's a good idea, but it would be more like we have to come up with preventative measures rather than how to mitigate these sort of problems once they're introduced. The way of doing that is just like have good standards on things that we have to do and things that we shouldn't do. For example, never have two or more addresses pointing to the same storage layout of the same contract. I would always go through the preventative measurements, measures, rather than for trying to fix things when you're already wrong. Okay.

Thank you for the question and for the answer. We are out of time. Big round of applause again, please, for Jota. I'm going to be outside in case anyone wants to talk about this topic. Thank you very much.

Okay. Thanks again.

Automatic transcript — names and jargon may be misspelled.