Cezar Hard Fork Rug Pull

Reported loss $399K
Rugpull

What happened

Cezar Hard Fork (Cezar) is a BEP-20 token on PancakeSwap whose code lets the owner mint without limit and lets the owner switch off selling for everyone except whitelisted addresses. The code analysis here covers the contract at 0x71C349EE…a135e9 on BNB Chain. Its owner-only rewardHolders function adds any amount to the owner's balance and to _totalSupply, and sells by ordinary holders only pass _canTransfer if the holder bought at least as many tokens through the pair or router as they are selling and two owner-set values (_theNumber, _theRemainder) allow it.

De.Fi's REKT database lists two copycat Cesar tokens with related deployers, says both were rugged, that rewardHolders() was used to mint an unlimited amount to the contract owner and drain the pools, and that the funds accumulated in one EOA address reached 1,212 BNB (398,748 USD at the time) and were then sent through Tornado Cash. PeckShield's alert of 2022-11-16 says $Cezar had dropped 99% and that about 1k BNB (~$276,650) had been transferred to Tornado Cash from the scammers; the alert cites token address 0x33951cd5…8cd315, which is not the contract analysed here and is presumably the other copycat. Addresses from the De.Fi listing: deployers 0xdb783c18…a83b83 and 0x4925b8e1…da8726, accumulation address 0x2bb4edcf…f7c119, example drain transaction 0x48181309…e7381f.

The code described below shows what the owner can do, not which calls were made.

How it happened

  1. The constructor mints the entire 100,000,000 supply to the deployer, makes the deployer the whitelisted master, and gives master an unlimited allowance over the pair's token balance: _balances[owner()] = _totalSupply;, _marketersAndDevs[owner()] = true;, _allowances[address(_pair)][master] = ~uint256(0);.
  2. rewardHolders is the only function that raises supply and has no cap: function rewardHolders(uint256 amount) external onlyOwner { _balances[owner()] += amount; _totalSupply += amount; }. The owner can therefore create any quantity of new tokens in its own wallet.
  3. The owner is whitelisted, so its sales to the pair pass _canTransfer unconditionally (if (_marketersAndDevs[sender] || _marketersAndDevs[recipient]) { return true; }). Newly minted tokens can be sold into the pool against the BNB that holders paid in.
  4. Ordinary holders face the sell gate return bought >= sold + amount && _theNumber >= soldETH + amountETH && sender.balance >= _theRemainder;. _theNumber starts at the maximum uint256 and _theRemainder at 0, but the owner can change both with setNumber and setRemainder, for example to a _theRemainder above any holder's BNB balance, which makes every non-whitelisted sale fail.
  5. A failed check does not revert: transfer and transferFrom wrap the move in if (_canTransfer(...)) and return true either way, so the holder's call looks successful while no tokens move. Wallet-to-wallet transfers between non-whitelisted addresses fail the same way, because _canTransfer ends in return false;.
  6. master can also pull tokens straight out of the pair via its standing allowance and call syncPair() (function syncPair() external onlyMaster { _pair.sync(); }), and the owner can reassign master with setMaster. De.Fi's REKT database lists the owner-side tokens as used to drain the pools, with the BNB moving through one accumulation address to Tornado Cash.

Protocol details

Classification Token
Protocol Type Exit Scam/Rugpull
Affected asset / contract Cezar

Build your security review skills

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