TLDR: On February 18th 2023, a total of just under $30K USD was stolen from users of our recently released “Atomic swaps into V3 positions” feature implemented via our V3Utils contract. Immediately after being notified, we quickly updated the site to prevent other users from being exposed and diagnosed how the exploit took place. ALL AFFECTED USERS HAVE NOW BEEN RESTITUTED FULL TOKEN BALANCES VIA AIRDROPS FROM THE FOLLOWING ACCOUNT ON EACH RESPECTIVE NETWORK.
Mainnet: https://etherscan.io/address/0x94f9FBdf13f19C86D6d07ae500D10e199BC61c4A#tokentxns
Optimism: https://optimistic.etherscan.io/address/0x94f9FBdf13f19C86D6d07ae500D10e199BC61c4A#tokentxns
Polygon: https://polygonscan.com/address/0x94f9fbdf13f19c86d6d07ae500d10e199bc61c4a#tokentxns
Arbitrum: https://arbiscan.io/address/0x94f9FBdf13f19C86D6d07ae500D10e199BC61c4A#tokentxns
This post aims to explain what happened, provide transparency into affected accounts, and to provide accountability - ensuring verifiable refunding for all affected users -, and an increased commitment of security over fast iteration in our operations and product releases.
At 2:56 AM CST on the eve of Saturday, February 18th, one of our users posted on our Discord server the following message:
“WARNING: I just had my polygon USDC stolen after approving it via revert's single token add liquidity feature. I have contacted one of the developers about it and waiting to hear back.”
About half an hour later another user posted a similar message. The whole team being in the same time zone (CST), this was not noticed until 6:46 AM when I woke up and reached for my phone. I alerted the team right away.
We immediately disabled the new “Atomic swap into Uniswap v3 positions” functionality from the site. This feature had been released earlier in the week, and was what being reported as being used by the affected users. If this were an ongoing attack, disabling the Atomic Swap feature would prevent any new users from being harmed.
After disabling the Atomic Swap feature, we started investigating. Inspecting the transactions, it became evident that they did involve our V3Utils contract, and that there was an interaction with a suspicious, unverified, and recently deployed contract. We decided at this point it was best to alert all possible users who might have approved allowances for the exploited contract. At 7:39 AM CST we posted a Tweet and a Discord message notifying all the members with the possible vulnerable contract addresses and urging everyone to revoke any approved balances.
As part of the team focused on working with users to answer questions about the revoke process, the rest of the team worked to diagnose the exploit while also writing code to identify and mitigate any other accounts that could be potentially be at risk (given previously authorized allowances).
Tenderly proved to be an invaluable tool for understanding the exploit. Using Tenderly, our team was able to identify the attack vector:
The V3Utils contract relies on an external aggregator to do the swaps. Our frontend calls this API with the tokens and amounts intended for swapping, and the API returns the swap parameters sent to the V3Utils contract.
There are two important values included in this API response, the contract which works as a router for the swaps, and the contract to approve the tokens for the actual swap. Because these values are dynamic and can be changed at any time with new versions of the aggregator, we had decided not to hard code them in our contracts which was designed to be ownerless and immutable.
By inspecting the reported transactions it became clear that the swapRouter parameter was used as the vector for attack. The swap function was called using a token contract (like USDC) as the router parameter, which allowed the attacker to execute arbitrary code against that contract with our own V3Utils as the caller.
This means that any previously authorized allowances to the V3Utils contract were vulnerable to being drained. Because our frontend only prompts for approval of the amounts to be used as deposits the remaining allowances mostly remain at zero and so not vulnerable. In some cases, however, accounts approved amounts larger than what was swapped leaving them open to be drained by these malicious transactions.
At 8:46 AM, once we verified the exploit was tied to the V3Utils contract, we once again pushed a notification to all Discord members - explaining the diagnosis of the exploit and urging everyone again to check and revoke any allowances to the affected contracts. We relayed the same information via our official Twitter and Telegram accounts.
At that point we had tallied up that there were allowances for the V3Utils contract the low hundreds of thousands of dollars. We urged everyone to echo our information on all channels possible and a lot of the community was there to help amplify the message. We added a bright orange banner to the top of the website that would display to the vulnerable accounts that their funds could be at risk.
Alerting our community of the situation was successful, we quickly started seeing allowances revoked, and a few minutes before 12pm we saw the single largest one was revoked.
After these wave of revokals, the total vulnerable allowances were down to close to $30k USD, a considerable but much less catastrophic amount. We also knew that the total amount stolen from victims was also close to but under another $30k, with $25k being from a single user who had reported the attacks on mainnet.
The following day, by midday Sunday, February 19th, there were still close to $20,000 USD of unrevoked permissions across all networks. We’d taken every measure possible to contact the possibly affected users, and decided that focusing on deploying a way to whitehat vulnerable accounts would be the best possible thing to do. We deployed our own attacker contract and started running a bot that would monitor any transfers into the vulnerable accounts to execute the described attack ourselves for refunding the users. This worked and we were able to get small amounts of tokens before the attackers and were used as part of the refunding.
By the end of the weekend, only a few thousand dollars that users had not revoked remained at risk though ready to be captured by our counter measure bot.
First, we want to apologize to all of our users, particularly those that were a victim of this exploit. At Revert Finance, our mission aims to empower our users, AMM LPs, and ensuring the safety and security of the tools we provide is paramount to achieving that.
This exploit was disappointing, painful and frustrating for us, but more importantly for those affected – and does reflect the way we aim to operate here at Revert.
We sub-estimated the this contract could present for our users. Since it was not a contract that would hold user funds, we assessed incorrectly that it was less of a risk and hiring a full audit was not necessary.
This is contrary to our other deployed contracts, the auto-compounder which was audited by Peckshiend before deployment and has a running bounty for $100k on immunefy, and the time-vested v3 staker where a full audit was also conducted by PeckShield before deployment.
Going forward, and before resuming functionality of the affected contract, all of our deployments will receive a proper external audit, but more importantly we will not make the same mistake of assuming any contract that handles user funds, even temporarily, is less of a risk. While the “move fast and break things” philosophy is tempting when you are simply providing analytics, when smart contracts are involved “move at a steady pace and be very mindful of the things” is the only sensible approach.
Finally we would like to express sincere gratitude to our users, and the different folks who reached out to us privately offering their help and support. We will not forget it.
---
Below is a list of accounts with the amount of tokens that were withdrawn with the exploit, and the script we used to determine the amounts. Those same tokens and amounts have been now airdropped back as a refund from this account on each respective network. This script we used to generate the list of attacked victims.
MAINNET 0x067D0F9089743271058D4Bf2a1a29f4E9C6fdd1b USDC 19305.581627
MAINNET 0x4107A0A4a50AC2c4cc8C5a3954Bc01ff134506b2 USDC 500
MAINNET 0x067D0F9089743271058D4Bf2a1a29f4E9C6fdd1b USDT 4106.316699
OPTIMISM 0xbB8694519BC68B337663F0BDbE79847b49000b6B OP 403.5786287699002
OPTIMISM 0xD5dE81E7e5E4F740a26Ebb254d6052e1D03B4787 OP 32.00000000000003
OPTIMISM 0xD5dE81E7e5E4F740a26Ebb254d6052e1D03B4787 USDC 228.935843
OPTIMISM 0x89425a801dbbacD4562638161198b6d3b85A314c WETH 0.06919111687156008
OPTIMISM 0x50EE407e77EBfD8A6b0F22AC74719f63C8a0A7Db USDC 36.736549
OPTIMISM 0x8cadb20A4811f363Dadb863A190708bEd26245F8 DAI 10
OPTIMISM 0x5C0843066F64aA0D9858611401cD2DD6F2e1f7E4 DAI 17
OPTIMISM 0x50EE407e77EBfD8A6b0F22AC74719f63C8a0A7Db WETH 0.003659440712231981OPTIMISM 0x5C0843066F64aA0D9858611401cD2DD6F2e1f7E4 PREMIA 22
OPTIMISM 0x50EE407e77EBfD8A6b0F22AC74719f63C8a0A7Db USDC 106.557557
OPTIMISM 0xD5dE81E7e5E4F740a26Ebb254d6052e1D03B4787 USDC 6
OPTIMISM 0xe48fE6012f97b6A13C0Ce5cEF314cAF66E972deB USDC 41.296177
POLYGON 0x4E23d9Bb84359e2a20D3aBC6985d6c55c37062D6 WMATIC 15
POLYGON 0xb9B69B3B969E8Fa9EcF9815b396A77fB53267ED1 WMATIC 3.5972237426468854
POLYGON 0x225170393fCD06F3295aDa2bF33002C8ec94b8E4 USDC 194.316778
POLYGON 0x8cadb20A4811f363Dadb863A190708bEd26245F8 WBTC 1e-8
POLYGON 0x859Bb79038C20F09A7Ad5726e036028f301d5de6 USDC 0.76822
POLYGON 0x0B82F6a646cD5C07da58EBDDBd8b21caBD9E3632 USDC 111.918734
POLYGON 0x31Df5f1437eC5fA7BE165a84ED9635e9b0f57536 USDC 373.40421
POLYGON 0xD2eDb935FE14C80747E782FA870116D5E1C1E744 USDC 158
POLYGON 0x225170393fCD06F3295aDa2bF33002C8ec94b8E4 USDC 922.439314
POLYGON 0xD2eDb935FE14C80747E782FA870116D5E1C1E744 USDC 664.599662
POLYGON 0x5593095740485D30cd2171Eb12a1F174A1Ccb085 USDC 165.702693
POLYGON 0xb7A3E33A76c359349a2aED0A7833DCDB3E2D8337 WMATIC 0.004956763791274464
POLYGON 0x637c25D0f2CCa040D025e4c5b0332e6146869C56 USDC 6.488701
POLYGON 0x637c25D0f2CCa040D025e4c5b0332e6146869C56 DAI 7.590931982602232
POLYGON 0xA0f7F9F79f619BD86849747Be69Fb7a257fF01C0 WBTC 0.00000679
POLYGON 0x8cadb20A4811f363Dadb863A190708bEd26245F8 USDC 0.7853
POLYGON 0xA0f7F9F79f619BD86849747Be69Fb7a257fF01C0 oMATIC 0.21822824027535612
POLYGON 0xA0f7F9F79f619BD86849747Be69Fb7a257fF01C0 USDC 0.02
POLYGON 0xA0f7F9F79f619BD86849747Be69Fb7a257fF01C0 WBTC 0.00046135
ARBITRUM 0xf6b9Cb280Ae3c5dd5eC22D7A1A2A7f5F0a1AE879 USDC 200
ARBITRUM 0x19D32180BC2210eA3b500553d3Db90ef248A38f3 WETH 0.08983629296701101
ARBITRUM 0x7C14D5dB2A4417901E45D2Ab7fE33D916d18F0e1 WETH 0.01
ARBITRUM 0x19D32180BC2210eA3b500553d3Db90ef248A38f3 WETH 0.005984559978510494
ARBITRUM 0x5A12D9cd39Bc86C510aDA067F662c7bf796f8De6 WETH 0.002690670523787123
On a post-mortem analysis we have determined that there were five EOA accounts involved in the attack. This information has been forwarded to Chainalysis for inclusion in their database.
MAINNET: 0x38F887a0FE01b9e4960D5C727519408fA7f32F70
The first funding transactions for this account comes from a multi-chain bridging from BNB chain, where that account was originally funded on August 30th, 2022 via tornado cash.
POLYGON: 0x31487bBD8c2E79465940CC07DEc049dC0845331B
The first funding of this account was via a bridge called AnySwap, and this account was also funded via Tornado Cash on the same date as the mainnet attacker account, August 30th. Given this, it seems apparent that at least this one and the mainnet accounts belong to the same attacker.
OPTIMISM: 0xF93CEE8fdbC59BcA3A74A2F08d60a3e366E4768c
This account appears to also have been first funded on BNB, but through a centralized non-custodial exchange called FixedFloat on February 14th. This same account also performed the same attack on the Arbitrum contract.
OPTIMISM: 0x9b6Bb041B79B83b2dF1839d51Cd654cc6444e852
This account was originally funded on Optimism via the HOP bridge on October 12th 2022 from Ethereum. On the Ethereum the account was originally funded on the same day (October 12th) by account 0xbce37312886b43721bf488bda0f5d58871b310cf That account in turn was first funded on June 7th 2021 by account 0xb7f830845e9f385372ae6fb160aa968908f5bfbc which has a long history across several blockchains due to the huge number of transactions Etherscan does allow to paginate that far back, but trackable for sure by inspecting the blockchain.
OPTIMISM: 0x5504790a2426A312194270C7a7443a54AF9F75AE
This account can be traced back to Ethereum where it originally bridged from on December 13th 2022. That account itself was originally funded on the same day by account 0xc5e3605480fad6b9acb84585fb56860c835f6dd7, that account was funded by account 0x077d360f11d220e4d5d831430c81c26c9be7c4a4 on December 02 2022. That account has a long history across chains and the original funding is unavailable on ethersan.
Not included in the list above is our own bot 0x54ab50dd18305e4280e41af1923b38835363ecf1 which white hatted a small portion of funds on Optimism, Polygon, and Arbitrum.
If any of the above addresses was also performing white hatting service, please return the funds to this account: 0x54ab50dd18305e4280e41af1923b38835363ecf1 Keep 20%. No questions asked.