Back to all stories
Blogs
Security
Tax-Free Fraud: Exposing the ZhongHua Scam
5/10/2024

In a previous article titled The Vanishing Act: How Exit Scammers Mint New Tokens Undetected, we unveiled a large-scale exit scam targeting IPO robots, orchestrated by the automation address 0xdf1a. This address was responsible for over 200 exit scams within approximately two months.

Previously, we used the $MUMI token to illustrate the exit scamming tactics of this group: implementing a backdoor in the code to directly alter the token balance of the tax collection address, without adjusting the total supply of tokens and without triggering Transfer events. These tactics were designed to obscure any secretive token minting by the project team from users reviewing transactions on Etherscan.

In this article, we use the ZhongHua token as an example to explore another rug pull tactic employed by the same address — this one utilizing complex tax function logic to conceal transfer functions that facilitate rug pulls.

Tax-Free Fraud: Exposing the ZhongHua Scam

The Scam Itself

The project team managed to exchange 9.99 trillion ZhongHua tokens for approximately 5.884 WETH, effectively draining the pool's liquidity.

To fully understand this scam, let’s start with a chronological analysis of the events.

Token Deployment

At 1:40 AM on January 18 (UTC time, used hereafter), the attacker's address (😈0x74fc) deployed the ERC20 token named ZhongHua (🪙0x71d7) and pre-mined one billion tokens, which were subsequently transferred to the attacker's same address (😈0x74fc).

Blockchain Transaction Overview

The quantity of pre-mined tokens aligns with the specifications in the contract's source code.

Smart Contract Code - Total Token Supply Declaration

Adding Liquidity

Ten minutes after the token's creation, at 1:50 AM, the attacker's address (😈0x74fc) granted approval for the ZhongHua token to the Uniswap V2 Router, setting the stage to add liquidity.

Blockchain Approval Transaction Details

A minute later, the attacker used the addLiquidityETH function on the Router to establish a ZhongHua-WETH liquidity pool (🦄0x5c8b), contributing all the pre-mined tokens and 1.5 ETH, and subsequently receiving 1.225 LP tokens.

Series of Blockchain Token Transactions

From the token transfer records mentioned, we observe a peculiar transfer where the attacker sent 0 tokens to the ZhongHua token contract itself.

This transfer, not typical for added liquidity, prompted a review of the token contract source code, revealing a function named _getAmount. This function is responsible for deducting the transfer fee from the sender's address and sending this fee to the token's address, thereby triggering a Transfer event to record the receipt of the fee by the token address.

Smart Contract Code - Token Transfer Fee Logic

The _getAmount function verifies if the sender of the transfer is the _owner. If so, the transaction fee is set to zero. The _owner is designated during the deployment of the Ownable contract, based on the input parameter of the constructor.

Solidity Code - Contract Ownership Constructor

The ZhongHua token contract, being an extension of the Ownable contract, utilizes the deployer's msg.sender as the input parameter for the Ownable constructor.

As a result, the attacker's address (😈0x74fc) is the _owner of the token contract. The aforementioned transfer of 0 tokens during the liquidity addition process is conducted through the _getAmount function, which is invoked within the transfer and transferFrom functions.

Permanently Locking Liquidity

At 1:51 AM, within a minute of creating the liquidity pool, the attacker's address sent all of the 1.225 LP tokens—acquired through liquidity addition—directly to the 0xdead address, effectively locking the LP tokens permanently.

Token Transfer to Burn Address

Similar to the MUMI token scenario, once the LP tokens are locked, the attacker's address (😈0x74fc) theoretically loses the ability to conduct a rug pull by withdrawing liquidity.

In the rug pull scam orchestrated by address 0xdf1a, which targets new coin listing robots, this step is designed to mislead the anti-fraud scripts of these bots.

Up to this point, from the perspective of users, it appears that all pre-mined tokens have been added to the liquidity pool without any irregularities.

The Rug is Pulled

At 2:10 AM, approximately 30 minutes after the creation of the ZhongHua token, the second attacker's address (👹0x5100) deployed a specific contract (🔪0xc403) specifically for the purpose of the rug pull.

Transaction Hash and Associated Contract Address

Similar to the MUMI token scenario, the attackers did not use the same address that deployed the ZhongHua token contract for the rug pull, and the contract used (🔪0xc403) is not open source. These tactics are designed to complicate the tracing process for technical analysts, a common feature in most rug pull scams.

At 7:46 AM, roughly six hours after the token contract was initiated, the second attacker's address (👹0x5100) executed the rug pull. They invoked the swapExactETHForTokens method of the attack contract (🔪0xc403), transferring out 9.99 trillion ZhongHua tokens in exchange for approximately 5.884 ETH, effectively draining most of the liquidity from the pool.

Token Swap Transactions on Uniswap V2

Since the attack contract (🔪0xc403) is not open source, we decompiled its bytecode, with the results viewable here.

The primary function of the swapExactETHForTokens method in the attack contract (🔪0xc403) starts by using approve to grant the Uniswap V2 Router the authority to transfer the maximum quantity of ZhongHua tokens, then uses the Router to exchange a specified number of "xt" ZhongHua tokens (held by the attack contract 🔪0xc403) for ETH. The ETH is subsequently transferred to the _rescue address specified within the attack contract (🔪0xc403).

Solidity Storage and Decompiled Contract Code

unnamed - 2024-05-10T010411.934

unnamed - 2024-05-10T010437.468

unnamed - 2024-05-10T010459.822

The _rescue address matches the deployer of the attack contract (🔪0xc403), which is the second attacker's address (👹0x5100).

The input parameter "xt" for this rug pull transaction was 999,000,000,000,000,000,000, corresponding to 9.99 trillion ZhongHua tokens (ZhongHua has 9 decimals).

Blockchain Transaction Parameters

Ultimately, the project team used 9.99 trillion ZhongHua tokens to drain the liquidity pool of WETH, thus completing the RugPull.

Similar to the MUMI case discussed earlier, it is essential to verify the origin of the ZhongHua tokens in the attack contract (🔪0xc403). Despite the earlier finding that the total supply of ZhongHua tokens is 1 billion, post-rug pull, the block explorer still shows a total supply of 1 billion. Yet, the quantity of tokens sold by the attack contract (🔪0xc403) was 9.99 trillion, 999 times greater than the recorded total supply. The question then arises: where did these excess tokens, far surpassing the total supply, originate?

unnamed - 2024-05-10T010654.743

We examined the ERC20 transfer event history of the contract and observed that, similar to the rug pull case of the MUMI token, there were no incoming ERC20 token transactions for the attack contract (🔪0xc403) in the case of ZhongHua.

Series of Token Swap Transactions on Uniswap V2

In the MUMI instance, tokens in the tax contract originated from a direct modification of the balance within the token contract itself, enabling the tax contract to possess an amount of tokens vastly exceeding the total supply. As the MUMI token contract did not adjust the totalSupply in correspondence with the balance changes, nor did it trigger a Transfer event, the influx of tokens into the tax contract seemed to materialize from nowhere.

Turning back to the ZhongHua case, the tokens in the attack contract (🔪0xc403) also appeared to materialize without a trace. Therefore, we inspected the ZhongHua token contract for any modifications to the "balance" keyword.

Smart Contract Code - Balance Update and Fee Transfer

Smart Contract Code - Conditional Token Transfer Logic

Smart Contract Code - Basic Transfer Logic

Our findings indicated only three instances where the balance variable was altered, specifically in the _getAmount, _transferFrom, and _transferBasic functions.

The _getAmount function deals with transaction fee logic, while _transferFrom and _transferBasic manage the transfer processes. Notably, none of these modifications involved direct alterations to the balance similar to what was seen in the MUMI token scenario.

Smart Contract Code - Tax Wallet Balance Update

Crucially, whereas the MUMI token contract's direct balance modifications did not trigger a Transfer event, in the ZhongHua token contract, modifications by _getAmount, _transferFrom, or _transferBasic all properly triggered a Transfer event following balance changes. This discovery contradicts our initial findings—we could not identify any Transfer events linked to incoming tokens when we examined the event history for the attack contract (🔪0xc403).

This raises the question: could it be possible that, unlike in the MUMI case, the tokens in the attack contract (🔪0xc403) for the ZhongHua token genuinely materialized from nothing?

Revealing the Method: The Origin of the Attack Contract's Tokens

During our analysis, we noted that each modification of the balance by the ZhongHua contract correctly triggers a Transfer event. Despite this, we found no record of token inflow or any Transfer events linked to the attack contract (🔪0xc403), prompting us to seek a new analytical approach.

We scrutinized numerous transfer records and initially considered the performZhongSwap function as a potential breakthrough. This function is designed for selling tokens within the token contract. In other rug pull cases we've analyzed, similar functions have been exploited as backdoors for the scam.

After inspecting various functions without success, we shifted our focus to the core transfer function, which is central to how the attacker executed the rug pull. The most critical insights were found here.

unnamed - 2024-05-10T011300.907

The transfer function in the token contract invokes the _transferFrom function directly.

Smart Contract Code - Conditional Token Transfer Logic

At first glance, it appears to perform standard token transfer operations, triggering a Transfer event upon completion.

However, before executing the transfer, the transfer function uses the _isNotTax function to check if the sender is a tax-exempt address. If the sender is not tax-exempt, the _getAmount function is employed to collect taxes; if the sender is tax-exempt, it skips tax collection and directly sends the tokens to the recipient. The critical issue lies in this mechanism.

As previously mentioned, the _getAmount function verifies the sender's balance, deducts the required amount, and then transfers the trading fee to the token contract.

unnamed - 2024-05-10T011505.951

unnamed - 2024-05-10T011546.428

The problem arises when the sender is a tax-exempt address. In such cases, _getAmount is bypassed, and the transfer adds tokens directly to the recipient's balance without verifying or deducting from the sender's balance. Thus, a tax-exempt address, as defined in the token contract, can effectively send any amount of tokens to any recipient. This loophole is what allowed the attack contract (🔪0xc403) to transfer an astronomical amount of tokens—999 times the total declared supply.

Further investigation revealed that the token contract designates only the _taxReceipt address as tax-exempt in its constructor. Crucially, the address assigned to _taxReceipt is precisely the attack contract (🔪0xc403).

unnamed - 2024-05-10T011641.257

unnamed - 2024-05-10T011701.732

unnamed - 2024-05-10T011725.748

This analysis uncovers how the ZhongHua token's rug pull was pulled off. The attacker exploited a designed oversight in the token contract that exempted certain addresses from balance checks, allowing these addresses to issue vast numbers of tokens out of thin air, thus completing the rug pull. This methodical exploitation of the contract’s features highlights a critical vulnerability in its design, emphasizing the need for rigorous security checks in smart contract development.

Profiting from the Scam

unnamed - 2024-05-10T011829.501

Leveraging the vulnerability previously described, the second attacker's address (👹0x5100) directly invoked the swapExactETHForTokens function of the privileged attack contract (🔪0xc403) to execute the rug pull. Within this function, the attack contract (🔪0xc403) authorized the Uniswap V2 Router to transfer tokens and subsequently initiated the Router's token exchange function, exchanging 999 billion ZhongHua tokens for 5.88 ETH from the liquidity pool.

Additionally, beyond the primary rug pull transaction, the project team conducted 11 separate sales of tokens via the attack contract (🔪0xc403) midway through the operation, amassing a total of 9.64 ETH. Combined with the final rug pull transaction, they accrued a total of 15.52 ETH. The associated costs were minimal, consisting only of 1.5 ETH used to add liquidity, a modest contract deployment fee, and a small amount of ETH spent on transactions designed to lure market-making bots.

Moreover, the project team utilized different Externally Owned Account (EOA) addresses to engage with this attack contract (🔪0xc403) for intermittent token sales, creating the illusion of diverse sellers. This strategy was intended to mask their ongoing cashing-out.

unnamed - 2024-05-10T011928.673

Summary

Reflecting on the entire ZhongHua token rug pull scenario, we understand that the underlying method was straightforward—disabling the balance checks for privileged addresses. However, analyzing this case was challenging for several reasons:

  • Security experts typically view balance verification as a fundamental security feature, often assuming such functions will automatically perform balance checks. This assumption leads to a decrease in vigilance against such vulnerabilities, which are deemed too basic for attackers to exploit. From the attacker's perspective, however, the simplest methods—like bypassing balance checks—tend to be the most effective and the least likely to be anticipated.

  • The project team deliberately obscured the code that exempted privileged addresses from balance checks. They also implemented comprehensive tax, transfer, and reinvestment logic for non-privileged addresses, making the overall transfer logic of the token appear legitimate and indistinguishable from regular transactions without a thorough inspection.

In comparison, both the MUMI and ZhongHua token cases allowed privileged addresses to control substantial volumes of tokens covertly. The MUMI case involved direct manipulation of balances without triggering a Transfer event or altering totalSupply, preventing users from noticing the privileged addresses' extensive token holdings.

The ZhongHua scenario was more sophisticated, completely bypassing balance checks for privileged addresses, which meant that conventional detection methods were ineffective unless the source code was inspected.

The ZhongHua token rug pull underscores the inherent security vulnerabilities within the ERC20 token standard. While this standard is designed to ensure operational uniformity and security under normal circumstances, it is ill-equipped to thwart malicious entities who embed hard-to-detect backdoors within the standard business logic. Standardizing token behavior, although restricting functional flexibility, might help eliminate such hidden backdoors and enhance overall security measures against such exploitation.