Unsafe Transfers Leading to an Attack

On March 14, 2025, H2O Token suffered an exploit that resulted in a loss of approximately $22,000. Let's examine how this attack occurred.

Attacker: https://bscscan.com/address/0x8842dd26fd301c74afc4df12e9cdabd9db107d1e

Thanks for reading! Subscribe for free to receive new posts and support my work.

Attack Contract: https://bscscan.com/address/0x03ca8b574dd4250576f7bccc5707e6214e8c6e0d

Vulnerable Contract: https://bscscan.com/address/0xe9c4d4f095c7943a9ef5ec01afd1385d011855a1#code

Attack Tx: https://bscscan.com/tx/0x729c502a7dfd5332a9bdbcacec97137899ecc82c17d0797b9686a7f9f6005cb7

https://bscscan.com/tx/0x994abe7906a4a955c103071221e5eaa734a30dccdcdaac63496ece2b698a0fc3

Exploit analysis

H2O is a BEP-20 token derived from the basic ERC20 standard. The token incorporates both H2 and O2 tokens. One of H2O's key features is a logical flaw in its transfer() implementation — when tokens are transferred from the pair, a mechanism called _calulate enables the minting of additional H2 or O2 tokens.

The system first calculates a rating amount that scales with the user's H2O balance—a higher balance results in a higher rating. Subsequently, H2 and O2 tokens are minted in random quantities determined by the getRandomOnchain function.

If the minting amount of H2 is greater than 10 and O2 is greater than 5 (based on the 2:1 ratio of H2O — perhaps to simulate the H2O chemical formula), the token will burn an amount of both tokens to reduce the mint amount. The contract transfers the mint amount in H2O to the to address.

An important note is that the getRandomOnchain function is not completely random. Its randomness mechanism relies on block.timestamp, msg.sender, and block.number — all parameters that can be predicted or calculated.

The issue specifically occurs with the _calulate function. When the from parameter is a pair, _calulate is triggered. The attacker transfers H2O to the PancakeSwap USDT-H2O pair, which creates an imbalance between USDT and H2O reserves. By calling the skim function to rebalance, the transfer is executed again — this time with the pair msg.sender and the attacker as the to parameter.

After each loop, an amount of H2/O2 is minted. The first attack gives the attacker approximately 169,731,921 O2.

At this time, if the attacker wants to access H2O, they need to attack again using the H2 token. One method is to predict the getRandomOnchain function by manipulating parameters. However, since the attacker encounters two revert transactions, they must simply run the attack script multiple times and hope for success.

In the fourth transaction, he successfully mints H2 and burned all available O2 to drain approximately 22,000 USDT.

Conclusion:

The vulnerability occurred because the H20 Token contract, when designing the buying mechanism from the PancakeSwap Pair, failed to consider that skim could achieve the same effect while modifying the ERC20 transfer function, leading to the attack.

Thanks for reading! Subscribe for free to receive new posts and support my work.

Source
Disclaimer: The content above is only the author's opinion which does not represent any position of Followin, and is not intended as, and shall not be understood or construed as, investment advice from Followin.
Like
Add to Favorites
Comments