Security Incident: Post Mortem
Last week one of the Zerion employee devices was compromised. The attacker gained control of the Zerion Twitter account, posting scam tweets and stealing $2,775.55 in ETH, $18,292.90 in ERC-20 tokens, and $3,756.93 in NFTs. The employee’s personal hot wallet with one of Zerion’s internal addresses was compromised, resulting in a loss of $53k. No user private keys were affected.
Thankfully, we were able to react quickly, deleting tweets, disabling all employee accesses, and changing the passwords to services they had access to.
In this post, we looked at what happened, what we have done so far, and what we’ve learned.
What Happened
On Wednesday, at 9:40 PM UTC, multiple scam tweets were posted from the official Zerion account. The tweets promoted a "Zerion airdrop" and led to a phishing website to claim the tokens. In reality, signing a transaction on the website would lead to ETH, ERC-20 tokens, and NFTs being stolen from the wallet through the following addresses:
- 0x00006deacd9ad19db3d81f8410ea2b45ea570000 (ERC20 + NFTs)
- 0x0000626d6dc72989e3809920c67d01a7fe030000 (ETH)
The culprit was very prepared. They had a fake airdrop site created, leveraged our branding, and created the contracts five days before actually launching the attack. However, the contract most likely has been used in other scams (or by scammers themselves) because transactions were sent before the fake announcement and after it was deleted.
Once they were ready, they published the following tweet:
Luckily, within a few minutes, our team became aware of the incident, deleted the tweets, and changed the account password to lock the attackers out. Right after that, we started the investigation to explore potential ways the attack could've been performed. Since the attackers didn't change the password, we assumed they didn't have access to the 2FA, but only managed to steal either a session or a single 2FA key.
Thanks to our amazing community, we were able to learn a lot about the attacker rather quickly:
Our friends at PeckShield had spotted this person previously, and we were able to track down the hacker actually promoting their services.
While the tweet was posted, the hacker was able to drain $2,775.55 in ETH, $18,292.90 in ERC-20 tokens, and $3,756.93 in NFTs. As this was a particularly convincing hack and not a random phishing site, we plan to reimburse the affected users by sending funds to their addresses.
Sadly, that wasn’t all.
We were not able to identify the compromised device by the time the next attack happened.
On the following night (Friday), a Zerion team member noticed that their wallets were being drained.
At this point, they realized that their device was compromised by malware. They recalled that on Wednesday, around 6:30 PM UTC, they got a message on Telegram from someone impersonating another Zerion employee. They already had some earlier messages in the chat (perhaps, under a different name), creating an impression that this was a real Zerion employee. As a result, the team member was tricked into following a phishing link, downloading, and opening a malicious file.
Unfortunately, besides their personal wallets, they also had a private key from Zerion's POAP fees wallet with about $53k. This is the address that accumulated fees for minting POAPs during Zerion Connect calls.
It appears that this part of the attack wasn’t planned because that wallet was drained manually. They stole funds only after making fake posts on Twitter, where they likely expected to steal even more.
This POAP revenue was held in a hot wallet as it was an ongoing experiment. It was impractical to have a multisig for creating POAPs and interacting with several dapps to distribute them.
Shortly after the attack, we lowered the threshold for how much money we store in hot wallets and moved the excess funds to multisigs. Even before that, our policy demanded holding most funds in multisigs.
What We’ve Done and Learned
Even though the impact of the attack is limited, we take security at Zerion very seriously. We were always strict about our security practices, and that’s why we were able to quickly mitigate the attack and limit its scope. Still, we consider this incident unacceptable and will double down on security across the board.
We also owe a lot of gratitude to various users, teams, and friends in the space who reached out to flag suspicious tweets and raise the alarm.
Here is what we have done immediately in the first few hours:
- Terminated all accesses of the team member whose device was compromised
- Transferred all funds from hot wallets to multisigs
- Changed all passwords to the services the compromised device had accesses to
- Got in touch with Chainalysis to investigate the onchain trail
- Got in touch with our cyber insurance
Things we had right:
- We noticed and reacted to the Twitter hack very promptly and were able to avoid major losses there
- The team member didn't have access to too many Zerion services and accounts, so it was fairly fast to disable their accesses and change the passwords.
Things we could've done better:
- We could've identified the compromised device sooner by questioning all people who had access to the Zerion Twitter account and asking them to think of any suspicious interactions they might've had recently. That could've prevented the second attack and stolen funds.
- We should've realized sooner that Zerion's POAP fees wallet accumulated funds sufficient enough to be transferred to a multisig.
What We’re Doing Going Forward
As with any situation like this, we reflected internally and are thankful that the breach wasn’t worse. Going forward we plan to enact a number of new policies related to password control and account access internally to mitigate the risk and protect our users from scams like this.
This is also another reminder that everybody in Web3 needs to be extra vigilant. Do not click on suspicious links. Always double-check any communications against several channels.
UPD 21/06: This post was updated to include the value of stolen ERC-20 tokens and the smart contract used to do that.
UPD 23/06: Updated values for drained funds to reflect networks other than Ethereum. Also added a callout box with a link to revoke approvals.