ΞthernautDAO Car Token CTF — Vulnerability Report recently participated in the second ethernautDAO CTF Challenge and won it by hacking the smart contract. Read on to find out how we did it: HYDN Twitter Link Challenge Description The challenge consisted of 3 contracts: : An ERC-20 token that allows each unique address to mint 1 . CarToken CarToken : This offers users the ability to purchase a car (a locally stored structure data) for a fixed cost of 100k . Funds are transferred from the creator to the market contract. If the caller holds more than 100k he can mint more than one car for the fixed cost price. The fallback function intends to delegate calls into the Factory (we will come back to this part later). CarMarket CarToken CarTokens Initial state: the contract holds 100k CarToken. : This offers flash loans. It is required that the receiver of the flash loan must have purchased a car from the . No fees apply to the flash loan and the user must return the exact amount borrowed. CarFactory CarToken CarMarket Initial state: the contract holds 100k CarToken. The Schema Now let’s take a look at the schema, because a schema is worth a thousand words… The Contracts Before going into the exploit, let’s first take a look at the contract fallback: CarMarket This fallback intends to approve the transfer to the contract for the amount of the actual balance, and then delegate call into . This allows any CarFactory method to call into the context and storage. CarToken CarFactory CarMarket CarFactory CarMarket We can imagine that it intends to externalize the flash loan logic from the to gain security, separate logic and offer modularity, and upgradability. This flash loan logic can be upgraded without changing the contract by updating the address reference, as offered by the function ‘ ’, see below. CarMarket CarMarket CarFactory setCarFactory From a ‘real world perspective’, the legitimacy of the fallback is not totally clear, but as you have probably guessed by now: CarMarket this is the entry point of our attack vector. The Exploit Let’s first dive into the . One function offers a flash loan: CarFactory The regular flash loan logic without fees here looks correct. But when looking closely, something of interest is the variable (used in line 43 and line 55). CarFactory Let’s check this initialization: The problem is that is never set (the default will be 0). This means that now, the purpose of the becomes clearer. This flashloan() method cannot be called directly due to the missing state initialization. It intends to only be called from the delegate call, state storage match, and no clashing. Here we are. CarFactory CarMarket CarFactory CarMarket Now that we have a clear vision of the smart contracts and their state, let’s move on to the exploit. The target is to be able to purchase two cars. The contract offers, for each unique address, the necessary amount to buy one car. If we try to call it a second time, the method will require the caller to hold at least 100k . This is the exact balance of the (which offers a flash loan) and also the contracts. Looks good… CarToken CARCOST _carCost CarToken CarFactory CarMarket The flashloan function intends to transfer from itself to the flash loan receiver and then ensures it has been sent back. As we explained above, it’s impossible to directly call the flash loan, but it can be called from the fallback that delegate calls into it. CarFactory CarTokens CarFactory CarMarket Now we’re getting to the core of the exploit: the flash loan function line 43 checks the initial balance of the , sends it from itself, calls the flash loan receiver and then checks the balance of again to make sure funds have been sent back. CarFactory CarFactory The problem is that this logic, combined with the delegate call, will create a mixed hidden state. The balance check before/after the flash loan explicitly ensures the balance of the (where the transfer of funds is relative to the context). CarToken This means that in case of the delegate call, the funds will be sent from the , and then will be checked. In our scenario, will not change (before/after check condition will be satisfied) but the balance will be drained to the supposed flash loan receiver. CarMarket CarFactory CarFactory CarMarket At this stage, the sender will hold the necessary 100k to call the method again. CarToken CarMarket purchaseCar Job done. Let’s translate this into code… Alternative Version Above we have detailed the solution to the second version of this challenge. As explained , the first version didn’t end as the ethernautDAO team had intended. The first version was more complex to solve, but, again, HYDN figured out a solution. Let’s take a look… here Contracts The contracts and storage state are very similar to v2. Let’s take a look at the UML diagram: As you can see, it’s very similar to v2, the only difference is in the flash loan method signature. Here there is an added customer parameter that defines a flash loan receiver and borrower. This doesn’t have any consequence and we will not make use of it. CarFactory Contract State The key difference between the two versions is the context — the contract states are: holds 100k (in v2 it was 0) CarToken CarToken still holds 100k, so there is no change there CarMarket doesn’t hold any (in v2 it was holding 100k) CarFactory CarToken Thus, the difference here is that the 100k tokens which were in are now held by itself. CarFactory CarToken How does this change things? This affects the exploit because the flash loan balance check ensures that the amount is lower than the balance (which is now 0), so the previous exploit will no longer work. CarFactory CarFactory During the analysis of the first version, we felt that something wasn’t well defined because after finding the exploit, we could not figure out why the held its own token. After the second version was released, we concluded that this was a setup mistake by ethernautDAO. CarToken Attack Vector The attack vector is similar, but the pre-request is more complicated. We first need to top up the in order to call the fallback (that will delegate call into and flash loan). The offers a mint function that can only be called once per address. This then allows us to write a ‘ ’ smart contract that will be a batch of them. CarFactory CarMarket CarFactory CarToken Minter After the top up, we can then drain the marketplace funds. Because of the gas limitation, all of them cannot be drained in one transaction, and instead, it needs to be split into a sub-batch (We’ve run 20 loop peer transactions). CarFactory For example, let’s say holds 100k tokens, each transaction will drain 20 * 10 = 2000 tokens. Thus, in order to drain all 100k from the the operation needs to be repeated 50 times. CarFactory CarMarketplace, The Code First, the minter contract: Now the exploit contract: The Process As described above, the process is: : in order to register the main contract and be able to call the flash loan method. MintSelf : you call multiple times in order to accumulate enough Mint CarToken CarToken : transfers the previously accumulated to Transfer to CarFactory CarToken CarFactory : now we are in a similar state as during v2. We can call the fallback and delegate the call to the flash loan into . The difference is we can only borrow up to the amount deposited in step 3. This means that this step has to be executed multiple times in order to drain the entire holding. Execute CarFactory CarMarket Now that we have drained the 100k previously held by the , we are able to purchase a second car. CarToken CarMarketplace Problems… Sadly during the real challenge, we only managed to collect 99991 tokens…yes we were 9 short of being able to mint the second car. The first reason is that we locked (and forever lost) too many tokens into the , and the has a hard cap of 210k tokens: 100k to CarMarketplace, 100k to itself, and 100 remain to the participant. Too many people participated in the challenge, which prevented anyone from fully accomplishing it. CarFactory CarToken Try it yourself To try for yourself you can find the full repo containing the exploits on the HYDN GitHub: https://github.com/hydnsec/ethernautDAO-CTF Further Details I hope you enjoyed reading this write-up as much as we enjoyed participating in the ethernautDAO challenge. For more information about HYDN, or to request a Smart Contract Audit, you can contact us in the following places: Website Twitter Telegram This article was first published here.