0xInuarashi Profile picture
Apr 23, 2022 22 tweets 11 min read Read on X
etherscan.io/address/0xf42c…
34 Million USD gone. Just like that. Locked in the contract forever.

A lot of people put light on the grieving which locked processRefunds() for a bit, that was the first exploit.

Luckily that was unlocked, but funds are still locked forever. How?

🧵 1/
2/ Let's take a look at an overview.

People took bids. It stored their data. Then, they were eligible for refunds.

By calling processRefunds(), a loop is made according to a refundProgress counter which then does the refund.
3/ This was the cause of the more-so-well-known exploit of a griever contract that can call the bid function (because they did not disable contract calling) which as a fallback that fails.

In short, someone could have bid and broke the processRefunds() by bidding from a contract
4/ This was exploited and done. An example had been posted on GitHub as proof of concept.

Also another person sent some on-chain messages to prove that this was the case, and to invest more in contract auditing.

etherscan.io/tx/0x3ded3a94e…
5/ Essentially what this does is:

Bid with a malicious contract that fails on fallback when receives ETH.

This makes the function fail, and stops the "chain" of refunds from continuing.
6/ If you decompile the bytecode, they have a trigger on a fallback to either enable the stuck or disable it.

require (uint8) is probably a trigger to enable or disable the failing of receiving ETH.

Nice touch! That means it can be unstuck.
7/ As for a solidity readable, it was probably something similar to this.

If block, fail the receivable fallback. Otherwise, let it succeed.

Demonstration was written by @notchefbob which tried to notify the team of the issue.
@notchefbob 8/ Thus refunds AND withdrawals were stuck in a limbo, under the power of a single contract. Such power.

No refunds were able to be made, and in addition to that, the claimProjectFunds() logic of the owner required that all refunds were made first, before they can withdraw.
@notchefbob 9/ Funds were getting stuck and refunds were failing.

That would wrench any project owner's gut.
@notchefbob 10/ With the gods in favor, the anonymous contract deployer had unstuck the contract and processRefunds() worked again. He even sent a little message to let people know it was a demonstration. Invest in devs. Invest in security.

etherscan.io/tx/0x2f667bb69…
@notchefbob 11/ Crisis averted!.... Or so they thought...

Now, this next part is truly painful.
@notchefbob 12/ Process Refunds started working again and people were getting their ETH back. However, there was a second exploit in the code.

Refunds work. Emergency withdrawals work.
etherscan.io/tx/0xd62f044cf…

However, the team will never be able to withdraw their ETH. Ever.
@notchefbob 13/ As a developer this is gut wrenching to even type...

A require of refundProgress >= totalBids was made

The assumption is that all refunds has to be processed first before withdrawing. It makes sense in their logic, and crisis was averted.

However...
@notchefbob 14/ We take a look at the _bid() function which takes in two arguments:

uint8 amount
uint256 value
@notchefbob 15/ Bids are stored in an index and that index is linked to the user. This is to store their bidding data for refunds.

There are a total of 5495 items for auction thus index "should" increase accordingly, based on their withdraw logic.
@notchefbob 16/ However, taking in an argument of

uint8 amount

but always incrementing the index by 1 (++) is the devil here.

After the mint-out, the bidIndex only went up to 3669, this is because of multi-mints in a single TX.
@notchefbob 17/ In processRefunds() code, there is a require statement that

requires _refundProgress < _bidIndex

this essentially means that _refundProgress can never be above 3669.
@notchefbob 18/ Looking back to claimProjectFunds(), there is a require statement as well.

require(refundProgress >= totalBids).

This means as long as totalBids is higher than refundProgress, project cannot withdraw their funds.
@notchefbob 19/ However, if you take a look at the value of totalBids...

It is 5495.

3669 will never be higher than 5495, which means this function is stuck. Forever.
@notchefbob 20/ A summary

Exploit 1: processRefunds() able to get stuck

Exploit 2: bids count did not increment correctly with mint amount

Exploit 3: withdraw requires bids count to increment correctly

Final Caveat: funds stuck forever.
@notchefbob 21/ I would like to make some ending remarks but it's hard to find the words.

Devs, and Artists, run the NFT space.

I would suggest to never skimp out of them.
Good devs know and will demand their worth.

Invest in audits. Invest in security.
@notchefbob 22/ I would never wish this upon anyone.

It is truly gut wrenching and I am really sad to see this happen.

• • •

Missing some Tweet in this thread? You can try to force a refresh
 

Keep Current with 0xInuarashi

0xInuarashi Profile picture

Stay in touch and get notified when new unrolls are available from this author!

Read all threads

This Thread may be Removed Anytime!

PDF

Twitter may remove this content at anytime! Save it as PDF for later use!

Try unrolling a thread yourself!

how to unroll video
  1. Follow @ThreadReaderApp to mention us!

  2. From a Twitter thread mention us with a keyword "unroll"
@threadreaderapp unroll

Practice here first or read more on our help page!

More from @0xInuarashi

Dec 4, 2022
Let's take a look at the CryptoPunks contract which predates the ERC721 standard,

and also explain why a CryptoPunk was unintentionally sold today for 0.01 ETH (13$)

👇
CryptoPunks, deployed on 22 June 2017, is an NFT that actually pre-dates the ERC721 standard that powers all NFT collections now and is actually one of the inspirations for the ERC721 standard.
This means that the creators, Larva Labs, had to invent their own "NFT" mechanisms.

While creating their contract, they had to solve an issue that creators do not have anymore - supporting the purchasing and selling of their tokens.
Read 19 tweets
Apr 20, 2022
A guide to DMing @0xInuarashi

1/ I won't reply if you just say hi, let me know what you want lol

2/ Ask about GAS chips dm @0xRimuru_eth

3/ Ask about GAS collabs dm @gronkwizard

4/ I can take like a month to reply, I rarely check but I read them all when I do Image
@0xRimuru_eth @gronkwizard thanks for art done by @nliel4
@0xRimuru_eth @gronkwizard also a realistic depiction of me replying to messages
Read 5 tweets
Feb 23, 2022
Here's a fun one...

A contract-agnostic global walletOfOwner for you to call any contract without the native function with

Just put the contract address, then the wallet address, then the tokenId you want to start at (generally this is 0 or 1)

etherscan.io/address/0x7e57…

1/X Image
2/ First, I created an interface. This allows us to call the methods of other contracts. Image
3/
The contract itself just has one function -- walletOfOwner that takes in a
- contract address
- wallet address
- id to start iteration from Image
Read 8 tweets
Jan 11, 2022
1/ 🤑 How to reduce gas and achieve Enumerable functions for NFTs 101

Reduce gas by up to 80% on mints and achieve Enumerable functions such as walletOfOwner with view-only functions

A thread! 🧵

👇
2/ First, we have to understand the difference between a basic ERC721 vs an ERC721Enumerable implementation.

The two main things that people look for in ERC721Enumerable are:

1. TotalSupply tracker
2. WalletOfOwner (TokenOfOwnerByIndex)

This makes contracts easier to write.
3/ TotalSupply tracker is self-explanatory.

They can use it to increment token ID when minting. By calling totalSupply() its easy to increment the ID as it automatically increases when you mint.

Instead of having to create your own counters.

Tradeoff is that it gas-expensive
Read 20 tweets
Oct 31, 2021
1/ So there is a social engineering scam going on in servers, targeted at NFT figures and Project Moderator / Owners.

Have a read in this thread below.
2/ Firstly, the scammer will join the server with a new, burner account (or I guess, they could be an existing account) and frame you to the mods that you are a scammer. They will send your discord ID for the mod to ban, which instead of the supposite scammer, it's you instead.
3/ Then, once you are banned from the server (because they social engineered the mod to do so), they will contact you with an impersonator of the moderator. They will contact you in order to get you unbanned but you need to prove to them that you are innocent.
Read 9 tweets

Did Thread Reader help you today?

Support us! We are indie developers!


This site is made by just two indie developers on a laptop doing marketing, support and development! Read more about the story.

Become a Premium Member ($3/month or $30/year) and get exclusive features!

Become Premium

Don't want to be a Premium member but still want to support us?

Make a small donation by buying us coffee ($5) or help with server cost ($10)

Donate via Paypal

Or Donate anonymously using crypto!

Ethereum

0xfe58350B80634f60Fa6Dc149a72b4DFbc17D341E copy

Bitcoin

3ATGMxNzCUFzxpMCHL5sWSt4DVtS8UqXpi copy

Thank you for your support!

Follow Us!

:(