With our new NFTX v2 launch, we have decided to perform a gradual migration of the DAO treasury liquidity provided to PUNK/NFTX. In order to practice caution, we announced that the migration will be gradual and over time, starting with ~10% of the liquidity provided by the DAO.
To begin the migration, custody of the liquidity was handed off to Alex for a simpler migration. Alex approved 2 CryptoPunks for transfer to the vault contract, and immediately after sent the mint transaction to the vault to receive his PUNK tokens. However, this transaction was frontrun and ended up failing, with his 2 PUNK vault tokens going to the frontrunners address. Shortly after noticing something was off, the team's guardian permissions were used to pause all vault functions in order to give us time to investigate.
After the attacker noticed our pause, they quickly sold the 2 PUNK tokens on the (illiquid) Sushiswap PUNK/NFTX pool, and made off with ~6 ETH. Soon after they were sold, we purchased back the 2 PUNK tokens for ~6 ETH, returning the tokens back to our custody. We have also staged an upgrade to solve the attack vector (detailed below), and will unpause the contracts after the upgrade is deployed.
We did have a very successful audit with Code 423n4, however, the unique logic to handle these NFTs was added after the audit was complete, since we made the decision to handle bare CryptoPunks rather than Wrapped Punks later after the audit. We did go through an independent audit after our changes from Code 423n4, but it appears this flaw was missed due to the CryptoPunks contract not being within scope of the audit.
While the impact could have been 2 of the DAOs CryptoPunks (or larger if we were not more careful), after the attacker sold the PUNK tokens on Sushiswap, the team acted quickly to buy them back, reducing the total losses from ~33 ETH, to around 6 ETH. No other vaults are affected with this issue, and nor were any other users affected. All vaults are currently paused, with an upgrade for the vaults already staged to solve the problem.
Due to some NFTs (CryptoPunks, Kitties) not being aligned with the ERC721 standard, we have implemented some unique logic for them.
CryptoPunks, being rather early code before the ERC721 NFT standard, does not support normal approvals. They are performed by “listing” a CryptoPunk for sale to a specific address at a specific price (0 ETH). An approval for a contract to use a CryptoPunk is similar to “listing Punk #2550 for sale at 0 ETH to address nftx.eth”.
However, even though the sale is approved for sale to a specific address, we did not verify that the transaction caller was the owner of the CryptoPunk, allowing anyone to execute that sale. So the moment Alex approved his CryptoPunks for “sale” and tried to mint, he was frontrun, with an attacker “spending” Alex’s Punks and receiving the 2 PUNK tokens instead of Alex.
As a fix, we have staged an upgrade to the vault contracts that now verify that the person executing the order does indeed own the CryptoPunk. Before executing the buy, we check the CryptoPunks contract for who owns the punk ID being transferred, and make sure the sender of the transaction is indeed the owner of the CryptoPunk.
1:03:28 PM UTC: Alex approves 2 CryptoPunks to be spent by the NFTX PUNK Vault.
1:05:04 PM UTC: The attacker executes their frontrun transaction to “spend” Alexs CryptoPunks before his transaction to mint is processed and gains 2 PUNK vault tokens.
1:05:04 PM UTC: Alex’s transaction to mint fails, due to him no longer owning the CryptoPunks. We begin to investigate what could’ve happened.
1:22:06 PM UTC: Since we purposely left the PUNK vault unfinalized for our soft V2 launch, we were able to disable minting and redeeming for it.
1:29:03 PM UTC: The attacker sells their PUNK tokens into the rather illiquid Sushiswap PUNK/NFTX pool, selling both for ~6 ETH worth of NFTX. Later selling the NFTX through 1inch.
1:36:45 PM UTC: We purchased back 2 PUNK (6 ETH worth at the time) of PUNK, retrieving back custody of the original stolen assets.
1:42:58 PM UTC: We execute a pause on the Vaults, using our safety Guardian roles. Just to be safe in case the issue is found elsewhere.
4:05:59 PM UTC: An updated contract with the fix by Kiwi is staged to upgrade through the DAO, after which the vaults will be unpaused and NFTX v2 will be back to functioning as normal.
While we did go through a few audits and independent reviews, we made sure to be careful with our V2 launch as all new code should be used with immense care. Because of this, damages were reduced, and we were able to quickly isolate and solve the attack vector.
Whenever we do support non-standard NFTs, we will be sure to audit the NFTs themselves as well, as their implementation may not be as clear as it seems. We hope this post mortem informs others to make sure native CryptoPunks and other non-standard NFTs are implemented very carefully.
Thank you everyone for your patience and support.