In a recent tweet that quickly gained popularity, we described the curious tale of Jhon Doe [sic]. Jhon went to sleep with a false sense of security after withdrawing all his funds from a questionable farming scheme believing no harm could be done as long as the funds were in his wallet.
Much to his surprise, Jhon woke up to discover half of his UNI tokens were removed from the wallet, without him ever authorizing or signing a transaction.
Jhon’s private keys were never compromised, and there was no bug in the wallet. What made this hack possible is a known but commonly overlooked flaw in the design of the ERC20 standard used by most popular tokens on the Ethereum network.
If you would like to read a short primer on what makes this hack possible, read our original post on baDAPProve. In that post, we predicted how the increasing popularity of DeFi Dapps, coupled with an insufficient warning regarding this issue, might lead to loss of funds. This scam is the first time we have seen it publicly exploited. If you want to go down the rabbit hole and discover the true extent of this hack, keep reading.
UniCats was launched as another spin-off from popular DeFi projects such as Sushiswap. Unsurprisingly, they even used the exact same frontend of Sushiswap, because why bother (the official website is down, but an archive view is available).
Users who found their way to UniCats were promised $MEOW tokens if they staked either $UNI or $UNI LP tokens on UniCats. The choice to go with $UNI staking is no coincidence. Many users got a bunch of UNI for free and thus might have felt less concerned with risking their newly found fortunes.
Jhon Doe was one of the users who decided to participate in the protocol. With the current atmosphere of “audits are for noobs” and FOMOing into any new farming scheme, it’s hard to blame him.
Jhon deposits 17K $UNI (~$50K) and another 15K $UNI (~$45K) to the UniCats contract. This, of course, required Jhon to first approve unlimited access of the UniCat contract to their $UNI tokens. Jhon probably saw the generic approve message that everyone gets when first engaging with a new contract, and did not give it much thought.
At first, everything was okay. Jhon started farming and making some $MEOW. After a day of farming, Jhon actually removed(!) all his $UNI from the Unicats contract (1),(2). So far, no harm, no foul. Knowing when to quit is a great virtue. Jhon must have felt really great, going to sleep knowing they have made some gains, and funds are SAFU from any potential rug pull.
These actions were even luckier than one might think. A short time after Jhon removed the funds, the UniCats admin, let’s call them Whiskers, started pulling liquidity out of the contract, draining all the $UNI and $UNI LP tokens deposited by unsuspecting users.
Other users were not as fortunate as Jhon. They found a dried-up pool when they figured out it was all a scam and tried to remove their funds.
Well, nothing surprising so far. A shady project pulls the rug, the main dev says “there was a bug” “that the project got “hacked,” that they “worked really hard” and are now “taking their rewards.” There was also the usual line that states that they are hoping for a successful future for the project and are now “leaving this wonderful project to the community”… Right before deleting the account and disappearing with the gains. You know… the usual.
Last post of the Dev in UniCat’s Telegram group
Jhon was fortunate to avoid this painful loss, and right at the nick of time. Little did he know that the story was not over, and the approved transaction would come back to haunt him.
About a week passes, and the former members of UniCats, come to accept their fate. But then, Whiskers decides to take the scam one step further.
The staking contract of UniCats has an interesting function, un-alarmingly called `setGovernance.` Someone reading the unaudited contracts (that were made public) might have skimmed over this part, as it’s quite popular to have an admin key and governance address in smart contracts (but that’s another story).
The `setGovernance` function is what Whiskers used to fetch funds right from the user’s account. The function receives two parameters, an address and some data. All Whiskers needed to do is set `_governance` to the UNI token address, and for the data, pass the function `transferFrom(from: Jhon Doe, to: UniCats, amount: 10K UNI)`. This initiates a call to the $UNI contract, requesting it to transfer funds on behalf of Jhon to the UniCats contract.
The caller of `transferFrom` is, in fact, the UniCats contract, which, as you recall, Jhon has approved to use all his $UNI at their will. The same `trasnferFrom,` which was used to move Jhon’s $UNI into the contract for the initial staking, is now used to steal funds from their account.
When the UNI contract receives this call, it encounters no error trying to possess the request. All the checks pass, as Jhon had indeed approved the contract to handle his funds. The funds are pulled from Jhon’s accounts into the UniCats contract, and from there, into Whisker’s paws.
The key here is the origin of the call in the UniCats contract that Jhon approved. Since Jhon approved the contract to use all of his funds, Whiskers, through UniCats, could take all of Jhon’s $UNI, without any intervention from Jhon’s side.
In fact, another user of the contract, who also lost 10K (~$30K) UNI through the exact same process, not only had their funds removed from UniCats, but also completely removed from their account, which means there is nothing to take. Unfortunately for them, they moved some UNI back into the account, right before they were fished out by Whiskers.
The important message here is that infinite approval does not disappear once you have 0 tokens in your balance. Accounts with infinite approval remain vulnerable as long as the approval has not been revoked, or the account is completely abandoned.
To cover their tracks, they went a step further and immediately swapped the fetched UNI for ETH, in the same extraction transaction. The ETH was moved to an account controlled by Whiskers and later deposited into Tornado Cash (a mixing service), in batches of 100 ETH each. Completely covering their tracks, almost.
Interesting to see that before executing the second phase of the hack, Whiskers actually practiced the execution of said hack on-chain. To achieve this, Whiskers created a separate contract and admin account (funded from Torando cash) to ensure the hack works.
In this transaction, Whiskers tries to pull out a small amount of UNI directly from the contract, using the same `setGovernance` call, fetching 2.75 UNI ($9) from the contract. This is analogous to the first phase, where the funds from UniCats contract itself were drained.
Later, this same account performs another practice tx, which abuses the baDAPProve vulnerability, with some small sums of $UNI, accounts funded from Tornado cash, and directly converted them to $ETH. Just like in phase 2 of the hack.
These practice runs took place several hours before executing the second phase “live” on real victims. Although there is no direct link to the practicing address, both practice runs follow the same pattern and execute precisely the same hacks.
Following the trace of the practicing address leads here. The account appears to be funded from Binance, so it might still be possible to trace the perpetrator through them.
While draining the UniComp contract directly, whiskers were able to fetch 17K UNI (~$50K) as well as staked LP tokens for the UNI/ETH on Uniswap, SushiSwap, and Balancer. See the full list of transactions here.
In the second phase, using the baDAPProve exploit, Whiskers fished a total of 60K UNI (worth ~$180K). These were taken out of ~30 accounts, with the greatest loser being Jhon Doe, losing a total of 37K UNI, worth approximately $120K at the time. That’s right. More was taken from their account than they have originally staked in the protocol, as not all of Jhon’s UNI were deposited, but were still available in their account after.
Every account that has ever interacted with Unicats, and has not yet disapproved UniCats to use their tokens, remain vulnerable until they do so. Even those who have only approved the token but never actually sent any funds.
The Jhon Doe in this story was a heavy roller and an experienced DeFi player. Their address has over 1600 transactions, dating back a year. Jhon Doe probably knew what they were doing, and indeed managed to withdraw their funds on time. However, these systems are complex and are getting more complex over time. A long-lasting record is not a guarantee of security.
This case is a result of a perfect storm. A standard that requires infinite approvals, that few understand the true meaning of. A FOMO environment, where users try to be the first to degen into risky contracts in search of enormous gains, searching for the next YFI. Finally, a hacker who took advantage of these factors and the complexity that these systems often try to hide.
Our recommendation is that you only interact with contracts you trust. If you insist on trying out a new Dapp, minimize the risk by only approving it to use the amount of tokens you are willing to risk.
The origin of the issue is with how ERC20 works and the limitations it imposes. Although other standards exist that do not have the same problem, ERC20 is still by far the most popular token standard. Understanding how to work with it might just save you some money.
Since neither Dapps nor ERC20 are going away any time soon, the best course of action is to learn and understand how these complex financial systems. Stay safe!