Web3 Security Alert | On-chain New Investment Strategies, Decoding Large-scale Rug Pull Techniques
Author: CertiK
Recently, the CertiK security expert team has frequently detected multiple instances of the same "exit scam," commonly known as a Rug Pull.
Upon deeper investigation, we found that several incidents with the same method pointed to the same group, ultimately linking to over 200 Token exit scams. This suggests that we may have discovered a large-scale automated hacker team that conducts asset harvesting through "exit scams."
In these exit scams, attackers create a new ERC20 token and use pre-mined tokens along with a certain amount of WETH to create a Uniswap V2 liquidity pool.
When on-chain new token bots or users purchase a certain number of new tokens in that liquidity pool, the attackers will deplete all the WETH in the liquidity pool using tokens generated out of thin air.
Since the tokens obtained by the attackers do not reflect in the total supply (totalSupply) and do not trigger Transfer events, they are not visible on etherscan, making it difficult for outsiders to perceive.
The attackers not only considered concealment but also designed a nested scheme to numb users with basic technical skills who look at etherscan, using a small issue to cover their true intentions…
In-depth Scam
Taking one of the cases as an example, let's detail the specifics of this exit scam.
What we detected was actually a transaction where the attackers used a massive amount of tokens (secretly minted) to drain the liquidity pool and profit, in which the project party exchanged a total of 416,483,104,164,831 (approximately 416 trillion) MUMI tokens for about 9.736 WETH, depleting the pool's liquidity.
However, this transaction was just the final link in the entire scam; to understand the whole scam, we need to trace back further.
Token Deployment
On March 6 at 7:52 AM (UTC time, same below), the attacker address (0x8AF8) Rug Pull deployed an ERC20 token named MUMI (full name: MultiMixer AI) (address: 0x4894) and pre-mined 420,690,000 (approximately 420 million) tokens, all allocated to the contract deployer.
The number of pre-mined tokens corresponds to the contract source code.
Adding Liquidity
At 8:00 AM (8 minutes after token creation), the attacker address (0x8AF8) began adding liquidity.
The attacker address (0x8AF8) called the openTrading function in the token contract, creating a MUMI-WETH liquidity pool through the Uniswap V2 factory, adding all pre-mined tokens and 3 ETH to the liquidity pool, and finally obtaining about 1.036 LP tokens.
From the transaction details, it can be seen that of the originally intended 420,690,000 (approximately 420 million) tokens for adding liquidity, 63,103,500 (approximately 63 million) tokens were sent back to the token contract (address 0x4894). By examining the contract source code, it was found that the token contract charges a certain fee for each transfer, and the address that collects the fee is the token contract itself (specifically implemented in the "_transfer function").
Strangely, although the contract has already set the tax address 0x7ffb (the address that collects transfer fees), the final fees were sent to the token contract itself.
Thus, the final number of MUMI tokens added to the liquidity pool was 357,586,500 (approximately 35.7 million) after deducting the tax, rather than 420,690,000 (approximately 420 million).
Locking Liquidity
At 8:01 AM (1 minute after the liquidity pool was created), the attacker address (0x8AF8) locked all 1.036 LP tokens obtained through adding liquidity.
Once the LP was locked, theoretically, all MUMI tokens owned by the attacker address (0x8AF8) would be locked in the liquidity pool (excluding the portion used as fees), so the attacker address (0x8AF8) would not have the ability to conduct a Rug Pull by removing liquidity.
To reassure users when purchasing newly launched tokens, many project parties lock the LP, meaning they are saying, "I won't run away, everyone can buy with confidence!" However, is this really the case? Clearly not, as this case illustrates; let's continue analyzing.
Rug Pull
At 8:10 AM, a new attacker address ② (0x9DF4) appeared, deploying the tax address 0x7ffb as stated in the token contract.
There are three noteworthy points here:
The address that deployed the tax address is not the same as the address that deployed the token, which may indicate that the project party intentionally reduced the correlation between various operations and addresses to increase the difficulty of tracing actions.
The tax address contract is not open source, meaning there may be hidden operations that do not want to be exposed.
The tax contract was deployed later than the token contract, while the tax address has been hardcoded in the token contract, indicating that the project party could predict the tax contract's address. Since the CREATE instruction determines the creator's address and nonce, the deployed contract address is fixed, allowing the project party to simulate and calculate the contract address in advance using the creator's address.
In fact, many exit scams are conducted through tax addresses, and the deployment pattern of tax addresses aligns with the above points 1 and 2.
At 11:00 AM (3 hours after the token was created), the attacker address ② (0x9DF4) executed the Rug Pull. They called the "swapExactETHForTokens" method of the tax contract (0x77fb), exchanging 416,483,104,164,831 (approximately 416 trillion) MUMI tokens for about 9.736 ETH, depleting the liquidity in the pool.
Since the tax contract (0x77fb) is not open source, we reverse-engineered its bytecode, and the decompiled result is as follows:
https://app.dedaub.com/decompile?md5=01e2888c7691219bb7ea8c6b6befe11c
After reviewing the decompiled code of the "swapExactETHForTokens" method of the tax contract (0x77fb), we found that the main function implemented by this method is to exchange the number of MUMI tokens owned by the tax contract (0x77fb) for ETH through the uniswapV2 router and send it to the "_manualSwap" address declared in the tax address.
The storage address of the _manualSwap address is 0x0. After querying with the json-rpc getStorageAt command, we found that the _manualSwap corresponds to the address of the tax contract (0x77fb) deployer: attacker ② (0x9DF4).
The input parameter xt for this Rug Pull transaction was 420,690,000,000,000,000,000,000, corresponding to 420,690,000,000,000 (approximately 420 trillion) MUMI tokens (the decimal of MUMI tokens is 9).
In other words, the project party ultimately used 420,690,000,000,000 (approximately 420 trillion) MUMI tokens to drain the WETH in the liquidity pool, completing the entire exit scam.
However, there is a crucial question: where did the tax contract (0x77fb) get so many MUMI tokens?
From the previous content, we learned that the total supply of MUMI tokens at the time of deploying the token contract was 420,690,000 (approximately 420 million), and after the exit scam ended, we still found that the total supply in the MUMI token contract was 420,690,000 (shown as 420,690,000,000,000,000 in the image, needing to subtract the number of zeros corresponding to the decimal, which is 9). The tokens in the tax contract (0x77fb) far exceeding the total supply (420,690,000,000,000, approximately 420 trillion) seemed to have appeared out of thin air. It should be noted that, as mentioned earlier, 0x77fb, as the tax address, was not even used to receive the fees generated during the MUMI token transfer process; the fees were received by the token contract.
Method Revelation
- Where the Tax Contract Got Its Tokens
To explore the source of the tokens in the tax contract (0x7ffb), we checked its ERC20 transfer event history.
The result showed that in all 6 transfer events related to 0x77fb, there were only events of tokens being transferred out from the tax contract (0x7ffb), with no events of any MUMI tokens being transferred in. At first glance, it seems that the tokens in the tax contract (0x7ffb) really appeared out of thin air.
Thus, the massive MUMI tokens that appeared in the tax contract (0x7ffb) have two characteristics:
They did not affect the totalSupply of the MUMI contract.
The increase in tokens did not trigger Transfer events.
Therefore, the thought process becomes clear: there must be a backdoor in the MUMI token contract that directly modifies the balance variable, and while modifying the balance, it does not correspondingly modify the totalSupply or trigger Transfer events.
In other words, this is a non-standard, or rather malicious, ERC20 token implementation, where users cannot perceive whether the project party is secretly minting tokens based on changes in total supply and events.
Next, we verify the above idea by directly searching for the keyword "balance" in the MUMI token contract source code.
As a result, we found a private type function "swapTokensForEth" in the contract, with a parameter of type uint256 tokenAmount. In line 5 of this function, the project party directly modifies the MUMI balance of taxWallet, which is the tax contract (0x7ffb), to tokenAmount * 10**decimals, which is 1,000,000,000 (approximately 1 billion) times tokenAmount, and then exchanges tokenAmount of MUMI from the liquidity pool for ETH and stores it in the token contract (0x4894).
Next, we search for the keyword "swapTokenForEth."
The "swapTokenForEth" function is called in the "_transfer" function. Upon closer inspection of the calling conditions, we find:
When the recipient address "to" is the MUMI-WETH liquidity pool.
When other addresses purchase MUMI tokens in the liquidity pool more than _preventSwapBefore (5 times), the "swapTokenForEth" function will be called.
The tokenAmount passed in is the smaller value between the MUMI token balance of the token address and _maxTaxSwap.
In other words, when the contract detects that users have exchanged WETH for MUMI tokens in the pool more than 5 times, it will secretly mint a massive amount of tokens for the tax address and exchange a portion of those tokens for ETH to store in the token contract.
On one hand, the project party appears to be collecting taxes and periodically automatically exchanging a small amount of ETH to the token contract, which is for the users to see, making everyone think this is the source of the project party's profits.
On the other hand, what the project party is really doing is directly modifying the account balance once the number of user transactions reaches 5, draining the entire liquidity pool.
- How to Profit
After executing the "swapTokenForEth" function, the "_transfer" function will also execute sendETHToFee to send the ETH obtained from taxes to the tax contract (0x77fb).
The ETH in the tax contract (0x77fb) can be withdrawn using the "rescue" function implemented within its contract.
Now, looking back at the last profit transaction exchange record in the entire exit scam.
In the profit transaction, there were two exchanges: the first was 4,164,831 (approximately 4.16 million) MUMI tokens exchanged for 0.349 ETH, and the second was 416,483,100,000,000 (approximately 416 trillion) MUMI tokens exchanged for 9.368 ETH. The second exchange corresponds to the exchange initiated within the "swapExactETHForTokens" function of the tax contract (0x7ffb). The reason the quantity does not match the input parameter representing 420,690,000,000,000 (approximately 420 trillion) tokens is that some tokens were sent as taxes to the token contract (0x4894), as shown in the image below:
The first exchange corresponds to the exchange initiated when the tokens were sent from the tax contract (0x7ffb) to the router contract during the second exchange, which triggered the "swapTokensForEth" function due to meeting the triggering conditions of the backdoor function in the token contract, and was not a key operation.
- The Big Scythe Behind It
From the above, it can be seen that the entire exit scam cycle of the MUMI token, from deployment to creating the liquidity pool to the Rug Pull, took only about 3 hours, yet it achieved a profit of over 50% with a cost of less than approximately 6.5 ETH (3 ETH used for adding liquidity, 3 ETH used to exchange MUMI from the liquidity pool as bait, and less than 0.5 ETH used for deploying contracts and initiating transactions) to obtain 9.7 ETH.
The attackers conducted 5 transactions to exchange ETH for MUMI, which were not mentioned earlier. The transaction information is as follows:
- https://etherscan.io/tx/0x62a59ba219e9b2b6ac14a1c35cb99a5683538379235a68b3a607182d7c814817
- https://etherscan.io/tx/0x0c9af78f983aba6fef85bf2ecccd6cd68a5a5d4e5ef3a4b1e94fb10898fa597e
- https://etherscan.io/tx/0xc0a048e993409d0d68450db6ff3fdc1f13474314c49b734bac3f1b3e0ef39525
- https://etherscan.io/tx/0x9874c19cedafec351939a570ef392140c46a7f7da89b8d125cabc14dc54e7306
- https://etherscan.io/tx/0x9ee3928dc782e54eb99f907fcdddc9fe6232b969a080bc79caa53ca143736f75
By analyzing the EOA addresses involved in operations within the liquidity, we found that a significant portion of the addresses were on-chain "new token bots." Given the rapid in-and-out nature of the entire scam, we have reason to believe that this entire scam targeted various active on-chain new token bots and scripts.
Therefore, both the seemingly unnecessary but complex contract design, contract deployment, liquidity locking process, and the suspicious behavior of the attacker-related addresses actively exchanging ETH for MUMI tokens can be understood as the attackers attempting to deceive the anti-fraud programs of various on-chain new token bots.
By tracking the flow of funds, we found that all the profits obtained from the attack were ultimately sent to the address (0xDF1a) for fund accumulation by attacker address ② (0x9DF4).
In fact, we have recently detected multiple exit scams where the initial source of funds and the final destination of funds all point to this address. Therefore, we conducted a rough analysis and statistics of the transactions associated with this address.
Ultimately, we found that this address has been active for about 2 months and has initiated over 7,000 transactions to date, and this address has interacted with over 200 tokens.
We analyzed the transaction records of about 40 of these tokens and found that in almost all the liquidity pools we examined, there would eventually be an exchange transaction with an input amount far exceeding the total supply of the token, draining the ETH in the liquidity pool, and the entire exit scam cycle was relatively short.
Some of the deployment transactions for certain tokens (famous cigarette Zhonghua) are as follows:
https://etherscan.io/tx/0x324d7c133f079a2318c892ee49a2bcf1cbe9b20a2f5a1f36948641a902a83e17
https://etherscan.io/tx/0x0ca861513dc68eaef3017e7118e7538d999f9b4a53e1b477f1f1ce07d982dc3f
Thus, we can conclude that this address is essentially a large-scale automated "exit scam" harvesting machine, targeting on-chain new token bots.
This address is still active.
Final Thoughts
If a token does not correspondingly modify the totalSupply when minted and does not trigger Transfer events, it becomes very difficult for us to perceive whether the project party is secretly minting tokens, which will exacerbate the situation where "the safety of tokens entirely relies on the project party's integrity."
Therefore, we may need to consider improving the existing token mechanisms or introducing an effective total supply detection scheme to ensure the public transparency of token quantity changes. Relying solely on events to capture changes in token status is insufficient.
Moreover, we need to be aware that although people's awareness of fraud prevention is increasing, the attackers' counter-fraud measures are also improving. This is an ongoing game, and we need to keep learning and thinking to protect ourselves in such a game.