"Here is a small story of one vulnerability that could lead to a disaster but luckily didn’t.
It turned out that over 30 contracts of Zapper.fi had a critical bug since November 2020. All of them used similar logic of external calls to exchanges like 0x. As a result, anyone who gave an allowance to spend ERC20 tokens to any of those contracts were in danger of losing tokens from their wallet at any time.
Over $100M of user funds exposed to that attack. Every user that has used Yearn zap or Zapper native functions once in the past 7 months could wake up one morning without anything in their wallet. Luckily, all of the vulnerable contracts were fixed in 4 iterations of fixes that happened during the past 3 months.
We independently found that issue reported it and exploited the last remaining contract with the bug to secure user funds.
Recently, we’ve been developing a new project, and we were looking for a solution to zap user funds. During this research, we independently found a significant security breach in one of the Zapper.fi contracts on June 8th 2021. It turned out that this issue exists in the other 35 deployed contracts by Zapper from November 2020. However, most of the contracts were paused in mid-April and the end of May.
On June 10th 2021, white-hat hacker lukash_dev reported the same issue in the newly deployed contracts that still had been there. Zapper.fi had paused the contracts and took a few days to release a post-mortem. Pausing the contracts was a solution, but sadly not the solution. Later, Zapper.fi released a post-mortem to disclose the vulnerability, so we decided to act.
The vulnerability in those 35 contracts potentially lets anyone call any method of any contract on behalf of Zapper.fi contracts. Therefore all wallets that had any ERC20 approvals for that contract turn out to be vulnerable. And any exploiter could transfer funds from user wallets to their own directly via specially engineered calls to Zapper.fi contracts.
Although Zapper team had paused most of the vulnerable contracts, one contract had been still there. Luckily, it was under the radar for most users. The one (due to an engineering error) lacked a “pausing” modifier for its Zap function. So, in contrast to all other Zapper contracts, this particular contract is unable to be paused.
The contract itself is an outdated Polygon bridge zapper
It has the modifier
stopInEmergency , however, it’s not used for ZapBridge.
And here is a line contains the vulnerability that is quite similar to all of the rest 35 contracts.
(bool success, ) = swapTarget.call.value(valueToSend)(swapCallData);
It allows anyone to call any contract with any data on behalf of Zapper’s contract. It was intended to be used to integrate with external exchanges like 0x or 1inch.
The problem there is that it is possible to set the address of
swapTarget to any ERC20 token and encode a call to
transferFrom(userWithAllowance, exploiterAddress, amount) method into
swapCallData and it will go through. As a result, funds will be transferred to
exploiterAddress from the
userWithAllowance wallet, which gave an allowance to the vulnerable contract any time in past.
It was a huge risk to users’ funds, so we’ve decided to step in. The biggest problem with this broken contract is that there is no “right” solution to the vulnerability. Only performing your own attack.
By the time of the lukash_dev disclosure, we had already developed two smart contracts for the exploit; we were also checking if a similar issue exists in other big projects so that our disclosure won’t potentially damage anyone else. (Luckily, most of the other contracts had been implemented safer, except one minor contract that we’ve reported to another team).
The first smart contract: 0x7284E5CD49F47Da42d355BcB5ab64Fbb45D7eff6 exploited the logic; took all funds from users with non-zero allowance for the vulnerable contract; and deposited it into the second contract: storage.
The second contract 0x63cd1f35063e1bdd01355fb2bee4ecee05d94b84, the safe storage, allows to deposit user’s tokens, but it doesn’t allow to claim them until the user removes the ERC20 allowance to the vulnerable Zapper contract.
After development, we’ve reached Zapper, collaborated with their dev team, deployed contracts, and performed an attack. In the end, we’ve transferred ownership 0x6a3eedcd970b3ba2c2d24942aa81e46ab07479be02e95e709308e82592615fca of the safe storage contract to the Zapper team.
We hadn’t used any private pool, and as a result, part of our transactions had been frontrunned by some bots. We apologize for that.
Luckily, the bot owners have contacted the Zapper developer team, and most of the funds appear to be restored. The rest will be covered by Zapper. No user will lose funds.
Overall ~$2.5mln worth of tokens were affected by the attack. Around $600k of them were frontrunned by a white-hat bot and should be returned soon.
We really want to thank Zapper for their rapid response and competence.
All users who interacted with this particular contract 0x1f0d1927498FBD4f9E8558704Ce5B658929527Ec should remove all their approvals to that contract as soon as possible.
Users that got their funds taken should remove approvals and claim their funds from storage at any time.
It is available via Zapper front-end.
It’s recommended for users to remove all approvals from all old Zapper contracts. Being those contracts accidentally unpaused, users are risking losing everything. (check the additional remark)
All currently working Zapper contracts are safe to use and vulnerable free in our opinion.
Right now, the other 34 Zapper contracts still have the same issue but have been paused. (Here’s the list of them)
It means that all users who have approved their tokens to Zapper might lose their funds if contracts become unpaused. Tens of millions of dollars worth of user tokens are at risk and can be taken in 3 minutes, in case Zapper deployer wallet become compromised, or someone unpauses vulnerable contracts by mistake 0xfF350eDc2242Ca4d7252A64746aec4A5487a852B. We urge the Zapper team to renounce the ownership of the vulnerable contracts, thus guaranteeing that those contracts will never be unpaused again.
The bug was there for over 7 months. During that time period, anyone could have take tokens from all users that interacted with those vulnerable contracts. Over $100M worth of tokens were at risk. Yearn.fi had integrated Zapper in March, and most people who used yearn zap function between March and April were susceptible to this exploit.
The bug was partially fixed in April, partially fixed in May and finally resolved in June. However, the time frame for the potential exploit was alarmingly wide. This shows how fragile DeFi is.
We urge all other developer teams that using similar logic (integration with external services like 0x, 1inch, paraswap) to pay close attention to their code and re-review it for potential problems.
The next big DeFi exploit may come from the fact that we, as users, carelessly leave many approvals for every contract that we have ever used. This time, the ecosystem managed to dodge the bullet. As a user, manage your approvals with more accuracy. Unlimited approvals have been rarely abused in the past. However, they might damage the ecosystem really hard in the future.
Stay tuned for some amazing news onwards.
AndreiKei , VV."
I have the following questions:
Are other projects vulnerable to this kind of issue?
The authors state that they "hadn’t used any private pool, and as a result, part of their transactions had been frontrunned by some bots. ", Do you think they are telling the truth? Was there really an attack?
Well, and the third question, as practice has shown frontrun bots can protect the project from the withdrawal of money under “certain” conditions, whether their use is justified ethically? Looks like they might own this bots.