Our blog

Tornado Cash Encounters Governance Attack | Vulnerability Analysis


Tornado Cash is a well-known cryptocurrency protocol in the field of privacy-focused mixing services, renowned for disguising the origin and destination of funds and protecting user privacy. Due to its frequent use in money laundering activities, the United States banned its citizens from using this service last year. Tornado Cash operates based on a decentralized autonomous organization (DAO), which means that whoever holds the most governance token TORN has the power to determine the protocol’s upgrade direction and even its own survival.


Blockchain researcher samczsun recently pointed out on Twitter that Tornado Cash has fallen victim to a governance attack. The attacker maliciously proposed and granted themselves 1.2 million fraudulent votes, thereby obtaining complete governance control. They subsequently withdrew and sold ten thousand Tron tokens locked in the voting pool.

Vulnerability Analysis

It can be observed that the logic of the 20th proposal presented by the attacker is similar to the previously passed 16th proposal, as shown in the diagram below. It is important to note that proposers can lie in the description, so it is generally advisable to read the code to confirm its alignment with the proposal.
However, what went unnoticed by many is that the 20th proposal secretly added the “emergencyStop()” function, which internally triggers the self-destruction of the contract.
Once the 20th proposal is voted through, the attacker calls “emergencyStop()” to self-destruct the contract. Then, the hacker can deploy a new malicious contract on the same address. In this case, the malicious contract provides the attacker with a significant number of fraudulent votes. Since the contract address has already passed the proposal vote, it can be executed directly upon deployment. As a result, the attacker successfully obtains 1.2 million fraudulent votes, seizing complete governance control over the entire protocol.


To understand how the attacker deploys different contracts at the same address, one must first grasp the mechanisms of CREATE2 and CREATE within the EVM.
When using CREATE to deploy a contract, the address of the new contract is a hash consisting of the deployer’s address and nonce. Since the nonce progressively increases and cannot be reused, the occurrence of duplicate addresses can generally be prevented.
In the case of using CREATE2 for deployment, the address of the new contract is a hash comprising the deployer’s address, a user-defined salt, and the contract’s bytecode. This means that every execution with the same salt and bytecode will generate the same address. However, if the codesize of the target address is non-zero during deployment, the deployment will fail.
And the self-destruct function (selfdestruct) allows for the deletion of the contract, resetting the codesize and nonce to zero.
Therefore, the attack process is as follows:
contract MetaDeployer{
    function deployFactory()public{
        factoryContract a = (new factoryContract){salt: "123"}(); //Use CREATE2

contract factoryContract{
    function deployProposal_20()public{
        proposal_20 b = new proposal_20(); //Use CREATE
    function deployMalicious()public{
        maliciousContract c = new maliciousContract(); //Use CREATE
    function kill() public{

contract proposal_20{  //Proposal 20, including the functionality of proposal 16, and the hidden emergencyStop() function
        included proposal 16 logic
    function emergencyStop()public{

contract maliciousContract{ //The contract facilitating the acquisition of fraudulent votes by the attacker.
    function getFakeVote()public{
        //do something to get fake vote
1. Initially, the attacker deploys the contract “MetaDeployer” by calling “deployFactory()” and uses CREATE2 to deploy the contract to address (0x111).

2. They call “deployProposal_20()” to deploy the contract for Proposal 20, using CREATE with the address (0x111) and nonce (0), deploying the contract to address (0x222). After deployment, the nonce of (0x111) becomes 1.

3. Once Proposal 20 is successfully voted through, ensuring the executability of address (0x222) within the protocol, the attacker invokes “emergencyStop()” and “kill()” to destroy (0x111) and (0x222). At this point, the nonce of (0x111) resets to zero.

4. Now, they call “deployFactory()” again. Since CREATE2 is used, and the salt and bytecode are the same

5. At this point, they call “deployMalicious()” and again use CREATE, using the address (0x111) and nonce (0), resulting in the deployment to the same address (0x222).

6. Since address (0x222) has already passed the vote, the attacker can execute the attacks within the malicious contract to obtain 1.2 million fraudulent votes.

After the Hack

A few hours later, the attacker proposed a new proposal to restore governance. If this proposal successfully passes by the end of May 26th, the malicious code (used for stealing voting rights) previously deployed within the protocol will be removed, and the governance control of Tornado Cash’s DAO will be returned to the TORN token holders. However, the authenticity of this outcome remains to be observed.


This vulnerability is not inherent to the protocol itself but rather a Trojan-like vulnerability designed by the attacker within the proposal. Sufficient checks on codes before they are voted upon could potentially prevent the community from approving such proposals and thus prevent incidents.
Once a smart contract is deployed, it cannot be changed, so it is crucial to conduct multiple stress tests and security audits before deployment to avoid significant financial losses.
If you are looking for a professional auditor, feel free to contact our technical experts.
🌐 AVS Consulting


Metamorphic Smart Contracts: Is EVM Code Truly Immutable?

Tornado Cash 遭遇治理攻擊 | 漏洞解析