According to the SlowMist Intelligence, UvToken, a project on the BNB Chain, was attacked on October 27, 2022. The hackers stole more than 5,000 BNB exploiting a vulnerability in its mining pool contract. We investigated this incident and these were our findings:
Attacker Contract Address
Attacker EOA Address
vault contract: 0x36F277165C8b1b80cC3418719BADB1864e2687bc
Staking logic contract: 0x5ecfda78754ec616ed03241b4ae64a54d6705a1a
The essential functions of UvToken’s vault contract for withdrawals does not verify the validity of user input, which allows the attacker to feed in the malicious contract address and utilize it to return the withdrawn amount of tokens to deplete the pool.
First, the attacker used 0.5 BNB to swap 313 UVT tokens on PancakeSwap using the exchange path WBNB -> BUSD -> UVT, and then transfers it to the UvTokenWallet Eco Staking contract as well as the vault contract of this mining pool. The attacker’s contract received 100,449,018 UVT tokens, signaling that the contract was hacked.
The transaction details show that more than 100 million UVT tokens comprise the entirety of the total UVT tokens in this mining pool’s vault contract. Previously, the attack contract authorized UVT tokens to its staking logic contract 0x5ecf via the UvToken contract, and then used the transferFrom function to send 313 UVT tokens to the vault contract. The attacker then obtained an enormous quantity of UVT tokens by calling the authorization contract’s 0xc81daf6e function and creating the two parameters as the attacker’s own attack contract address 0x99d4. So, how did the attacker manage to call 0xc81daf6e and receive a large number of UVT tokens?
We followed up by decompiling the 0x5ecf contract and discovered that the 0xc81daf6e function does not verify the parameters passed in by the user and will circumvent the owner check if msg.sender is consistent with varg0. On line 632, the 0x95b81c68 function of the varg1 address passed in by the user is called, and the return value is recorded as v4, and on line 645, the 0x7e39d2f8 function of the 0x36f277 contract is called, and the varg0 passed in by the user, and the v4 is returned from 0x95b81c68.
We continued to monitor 0x7e39d2f8 to see if it transfers UVT tokens directly to the user-supplied varg0 address after assessing whether the contract’s available UVT tokens are sufficient enough to pay for the withdrawal of varg1.
It was discovered that the vault contract only evaluates whether the transfer value of the token is sufficient and does not verify the validity of the parameters passed in by the user. As a result, the attacker simply needs to write the 0x95b81c68 function in the attack contract to return the controllable withdrawal amount, and then pass in the malicious contract address as the 0xc81daf6e function parameter to take the funds from the vault.
So, since the attacked contract wasn’t open source, how was the attacker able to know which function corresponded to 0x95b81c68?
We all know that the function signature is created by taking the first four bytes after keccak256 hashing the function name and its parameters, therefore we can easily obtain the function signature by conducting a hash collision with the same first four bytes. We discovered that the 0x95b81c68 function can be collided in less than three minutes after actual testing.
However, the efficiency of hash collision is minimal. We can also use a simpler and faster method: function signature replacement. This means that when creating an attack contract, we don’t need to think about what the function signature is, but only about making sure the function parameters are of the same type, and then replacing the function signature when the contract is deployed. This function signature in the bytecode can be changed to 0x95b81c68.
At this point, we know that the 0xc81daf6e function does not validate the user-supplied parameters, enabling the attacker to pass in the maliciously built attack contract and return the quantity of tokens to be extracted in order to sweep the UVT tokens in the vault.
Next, we used our AML platform MistTrack to track and analyze the funds. So far, the hackers have profited a total of 5,011 BNB by transferring the funds to Tornado.Cash. Moreover, the initial funding for this attack also came from Tornado Cash.
SlowMist Security team advises projects that while using an external calling contract, the address of the called contract cannot be controlled. It is best to utilize a whitelist to limit the external calling contract address and external calling target function, as well as the validity of the user-supplied parameters.