Deanonymising the Kucoin Hacker
October 3, 2020
Originally posted on weijiek.medium.com.
Kucoin, a well-known cryptocurrency exchange, was recently hacked. It lost more than 150 million USD worth of funds. The perpetrator used Tornado Cash, a noncustodial mixer, to anonymise some of the stolen proceeds. Unfortunately for the hacker, an analysis of on-chain activity reveals information that could lead to their identification. In this post, I will describe how the hacker deposited a large amount of ETH into Tornado Cash. I will then present an analysis of Tornado Cash withdrawals to identify a number of addresses which the hacker probably owns. This information may help to bring them to justice.
The hacker's deposits
On 26 September 2020, a hacker moved large amounts of ETH, ERC20 tokens, and other cryptocurrencies of Kucoin’s hot wallets. Subsequently, the hacker swapped many ERC20 tokens for ETH using Uniswap and deposited the ETH into Tornado Cash’s 100 ETH and 10 ETH contracts.
Addresses that myself and others believe the hacker owns include:
The linked post: https://www.rekt.news/epic-hack-homie/
The following diagram shows the connections between accounts believed to be controlled by the hacker. Each connection represents a transfer of ETH or ERC20 tokens.
The full addresses and amounts are as such:
These deposits occurred between 15 to 27 October 2020, following this timeline:
About Tornado Cash
Tornado Cash helps give privacy to Ethereum users by allowing them to move ETH or tokens between two addresses in a way that prevents anyone else from knowing for sure that they are linked. It achieves this by way of zero-knowledge proofs and transaction relayers, as explained in this blog post. Generally, a user follows these steps to mix funds:
- Select a predefined deposit amount (e.g. 0.1, 1, 10, or 100 ETH). Note that a user may only deposit a fixed amount of ETH per transaction.
- Send the funds to the corresponding Tornado Cash contract (e.g. the 10 ETH contract), along with the hash of some unique and secret data, which is needed later to withdraw funds. Each deposit is also known as a “note”, and can be thought of as an IOU as long as one possesses the secret data.
- Wait for as long as possible, so that others may deposit more notes and the user may blend in with the crowd.
- Generate a zero-knowledge proof that the user knows the secret data, and send the proof along with a desired receiving address to a relayer via an off-chain channel. Note that this proof does not reveal the user’s secret data.
- The relayer sends the proof to the Tornado Cash contract (e.g. the 10 ETH contract), which verifies the proof, and transfers a small fee to the relayer, and the rest of the funds to the receiving address.
Relayers are essential because they execute withdrawals on behalf of users. As such, users do not need to pay gas to make said transactions. Their withdrawal address can therefore be fresh. If a user had to pay gas to withdraw from Tornado Cash, they would have to purchase the ETH from somewhere, and this would likely link their identity with their receiving address, and defeat the very purpose of using Tornado Cash at all.
Generally, each withdrawal goes through one of several relayers. The relayer’s fee should cover the gas cost of the transaction and reward them with a small profit. The Kucoin hacker, however, did not use relayers for the vast majority of their transactions. Instead, they used a self-withdrawal technique, as described below.
The self-withdrawal technique
Consider, for example, a user who owns 510 ETH in address 0xcafe
. They can
perform the following transactions to deposit a 10 ETH note and five 100 ETH
notes, and only use a relayer once:
- Deposit 10 ETH into Tornado Cash’s 10 ETH contract
- Deposit 500 ETH into Tornado Cash’s 100 ETH contract, 100 ETH at a time
- Use a relayer to withdraw 10 ETH into account
0xabcd
. This account should now hold about 9.9 ETH. - For each 100 ETH note, use account
0xabcd
to execute a transaction to withdraw 100 ETH to a receiving address (e.g.0x1234
). Since0xabcd
has about 9.9 ETH, they can pay the gas required to do so. - Ultimately, the user has 500 ETH in
0x1234
and less than 9.9 ETH in0xabcd
.
This method of mixing funds is cheaper than paying a fee to a relayer per withdrawal, but it greatly reduces the user’s privacy. Not only does it link the receiving address 0x1234
to 0xabcd
, it also makes it easier for an observer to surmise that the user’s 5 deposits from 0xcafe
are linked to address 0x1234
. For instance, if there was originally 10 x 100 ETH in the contract before the user deposited 5 x 100 ETH, and if the 10 deposits all came from different addresses, one could argue that it is unlikely that the 5 withdrawals came from 5 separate users instead of the single user who made 5 deposits with 0xcafe
.
Unusual withdrawals from Tornado Cash
I examined withdrawal activity from Tornado Cash’s 100 ETH contract, and found that shortly after the hacker began depositing ETH, two accounts began performing large amounts of self-withdrawals.
Please refer to Appendix I for the full list of receiving addresses.
This is unusual because the number of these zero-fee withdrawals is very large compared to those of other addresses which also performed zero-fee withdrawals.
Why this could be the hacker
Given the on-chain data available, we know that the hacker made 497 deposits of 100 ETH each. We also know that two addresses made a total of 437 withdrawals shortly afterwards. Assuming that these two addresses belong to the same entity, I argue that the hacker who made the 497 deposits made these 437 withdrawals.
For the sake of argument, let us assume that these 437 withdrawals do not belong to the hacker. If so, then at least 14 other depositors need to collude in order for these addresses to perform 437 withdrawals.
The number 14 comes from counting all deposits which were not made by the hacker, and which occurred before the hacker’s first known deposit, grouping them by unique address, sorting them in descending order, and counting the minimum number of addresses whose deposit counts add up to at least 437.
I believe that it is unlikely that at least 14 depositors colluded to perform these withdrawals. My view stems from these assumptions:
- It is unlikely that all 14 depositors are a single entity or a few entities.
- Tornado Cash users value anonymity. If a user colludes with other depositors, they run a high risk of having their identity exposed. As such, it is unlikely that these depositors would do so.
The top 14 addresses besides the hacker’s known addresses made deposits according to the following timeline:
Where the funds ended up
========================
Appendix I contains a list of all receiving addresses of withdrawn funds. I used the Breadcrumbs app to trace a few of these addresses and found that the ETH was sent to an address believed to belong to another cryptocurrency exchange. For instance, the address 0x8e0d…, which received 2200 ETH from via 0x8bd8…, transferred all its ETH to 0xa961…, which in turn made 44 transfers of 50 ETH (excluding the last one, which transferred 49.85925 ETH). Subsequently, each of these addresses sent their funds to the address 0xa305… which the Breadcrumbs app labelled as belonging to Binance. Do note, however, that I do not know if 0xa305… truly belongs to Binance.
I checked a few other receiving addresses and found very similar transactions patterns. The final destination of the funds in all the addresses I checked is 0xa305….
A direct path to 0xa305…
While performing the above analysis, I found a trail of transactions leading directly from the Kucoin hacker’s address to 0xa305…, without going through Tornado Cash_. Assuming that 0xa305… belongs to Binance, this is odd. The hacker was so determined to stay anonymous that they made hundreds of Tornado Cash deposits — but if Binance requires users to provide KYC information, the hacker may be easily identified.
One possible explanation is that the hacker performed an over-the-counter trade with someone who did not perform any due diligence on the source of the funds or the identity of the counterparty. Another possibility is that the hacker made a mistake.
The trail of addresses is as follows:
0xeb31973e0febf3e3d7058234a5ebbae1ab4b8c23
(the Kucoin hacker)0x1c3f856719a91735ccb78506bd504b17907ac814
0x261985b27a12272ad96b21885fe89a1538cbe91c
0xf519e276958c3ef2dffd9b6b2d87d26859526505
0xbe5e1ba665d397d5304f0f546cead0a1e44f242d
0x1a98fcebebfea4ffbd0d5bf5e4650d71344f52ab
0xba3271bf528ca6d63b048ff3388eb6550616ed88
0x8a6fe380ee2b274b0e01c5c89e9861cebe040745
0xc787eeba9f55933ffb4c37a0284029c24444bbd4
0x7374b5d31beda7acedf5dd379ed864b0d63afe1b
(linked to Binance)
Additionally, the flow of funds can be easily seen on this Breadcrumbs report.
Conclusion
It is far more likely than otherwise that the entity who controls the addresses 0x8bd8… and 0x82e6… is the same entity which deposited funds stolen from Kucoin into Tornado Cash.
Note that this post does not argue that Tornado Cash provides insufficient on-chain privacy. Rather, it shows that mixers are not suitable for mixing, in a short span of time, large amounts of funds on the scale of the Kucoin hack.
Nevertheless, regular users should have no issues mixing smaller amounts (e.g. 0.1, 1, or 10 ETH) to enhance their on-chain financial privacy. The apocryphal cryptocurrency user who simply buys coffee should continue to use Tornado Cash to protect their on-chain activity.
I have published all data and code used to perform the above analysis in this repository. Please feel free to replicate this analysis and contribute feedback and corrections.
Appendix I: receiving addresses of 100 ETH notes
The relayer 0x8bd8746310d4ba8a0b044415bfac70db55ada5b0 processed the following withdrawals of 100 ETH each, without charging a fee:
The relayer 0x82e6b31b0fe94925b9cd1473d05894c86f277398 processed the following withdrawals of 100 ETH each, also without charging a fee: