MetaMoonMars Hack

Reported loss $866
Other

What happened

METAMOONMARS (M3) was a BEP20 token on BNB Smart Chain trading on PancakeSwap. Its _transfer function keeps a percentage fee of every transfer that is deducted from the recipient's amount and credited to no one, and a function named aprove (one p, imitating approve) lets only the deployer address set that percentage to any value. Check Point Research reported that the fee was first set at 8% and later changed to 99%, so a buyer received about 1% of the tokens sent to them and a seller's proceeds were cut the same way; De.Fi's REKT database lists the deployer address 0xda7e1666…a17c96 as the attacker with a profit of 866 USD.

A second hidden function, burn(), lets the same address set its own token balance to about 10^49 base units without changing totalSupply().

How it happened

  1. The constructor takes the starting fee as an argument (fee=fee_;), gives the whole supply to the deployer, and sets _owner = _msgSender().
  2. function aprove(uint256 a) public externelBurn { _setTaxFee( a); ... } looks like approve but changes the fee: _setTaxFee writes fee = newTaxFee;. The modifier externelBurn requires _pancakeRouterV2 == _msgSender(), and _pancakeRouterV2 is hard-coded as 0xdA7e1666aB637609fc2d5833DAE1056976A17C96, the deployer address, despite its name. Nothing in the contract changes it.
  3. Every transfer, including pool buys and sells, runs the same fee math: _fee = (totalSUPERHEROE * fee / 100) / multi; then totalSUPERHEROE = totalSUPERHEROE - (_fee * multi);, with multi fixed at 1. The sender is debited the full amount (_balances[sender] = senderBalance - totalSUPERHEROE) but the receiver is credited only the reduced amount (_balances[receiver] += totalSUPERHEROE). The difference is credited to nobody, so it does not move to the pool or to the deployer; it simply disappears from all balances. There is no exemption list in use (_excluded is never read).
  4. At fee = 99 a recipient receives 1% of each transfer. At a fee above 100 the subtraction underflows and every transfer reverts under Solidity 0.8 checked arithmetic. Check Point Research reports the fee started at 8% and was later changed to 99%.
  5. A separate hidden power sits behind the same gate: function burn() public externelBurn { _tramsferSUPERHEROE(_msgSender()); } calls _balances[accountSUPERHEROE] = (_balances[accountSUPERHEROE] * 3) - (_balances[accountSUPERHEROE] * 3) + (_exemSumSUPERHEROE * 1) -5; which sets the caller's balance to 10000000 * 10**42 minus 5 base units, whatever it was before, with no change to _totalSupply. Despite its name, it creates tokens for the deployer. No opened source shows it being called.
  6. renounceOwnership() only clears _owner, which gates nothing else; aprove and burn depend on the hard-coded address, so renouncing ownership would not remove either power. On-chain owner() currently reads as the zero address.

Protocol details

Classification Token
Protocol Type Exit Scam/Other
Affected asset / contract M3

Build your security review skills

Work through hands-on labs covering real exploit mechanics, review techniques, and defensive patterns.