PCORE Rug Pull

Reported loss $40K
Rugpull

What happened

PCORE is a BNB Chain token whose transfer function gives the deployer a set of owner-only switches over who can trade. The deployer is exempt from every check, can turn on a switch that makes all non-pair transfers revert (so holders cannot sell while buys still work), can blacklist addresses from selling one by one or for every recorded buyer in one call, and keeps these powers even if ownership is renounced. The code only shows what the deployer can do; it does not show which of these functions were called.

On 2023-04-07 CertiK Skynet reported that the PCORE deployer removed liquidity in 3 transactions for a profit of about 37.5k USD. De.Fi's REKT database lists the case as a liquidity-removal rug pull of about 40,000 USD, says the token fell by about 90% from its initial price, and links the deployer address 0xe42e5670…b2ac71 and the liquidity removal transaction 0xcabd9ccd…2d7ad7.

How it happened

What the code allows:

  1. The constructor mints the whole supply to the deployer and records it as the special address: _owner=_msgSender(); and _mint(msg.sender, totalSupply_* 10 ** uint256(decimals()));. _mint is internal virtual onlyOwner, so nobody can call it again from outside.
  2. Every restriction in _transfer sits inside if(_owner!=from && _owner!=to && from!=_airTokenAddr){ ... }. A transfer where the deployer is sender or receiver skips all of them, so the deployer can sell, receive pool tokens and move supply no matter what the switches below are set to.
  3. A trading switch locks holders out. DepositTransfer() is onlyOwner and sets tradeStatus=true; the check is if(tradeStatus && from!=_swap){require(false,"Block is busy, please try again");}. While it is on, any non-exempt transfer not sent from the pool address _swap reverts: a holder selling to the pool (sender is the holder) and a holder-to-holder transfer both fail, while buys (sender is the pool) still succeed. WithdrawalTransfer() turns it off. _swap is set by the owner through SwapContract(address swap_).
  4. A per-address sell blacklist. AirDropGift(address user) is onlyOwner and sets isStatus[user]=true; the sell check is if(to==_swap){ if(isStatus[from]){require(false,"Block is busy, please try again");} }. The check reads the transfer's from (the holder), so it blocks the blacklisted holder's sell, including sells through the router. AirDrop(address user) clears the flag.
  5. A bulk blacklist of buyers. Every buy from the pool records the buyer: if(from==_swap){ if(haveToken[haveTokenCount]==deadwallet && examinationAirDropList(to)){ haveToken[haveTokenCount]=to;haveTokenCount++;} }. The owner-only Bonous() loops over that list and calls AirDropGift(user) for each address not yet flagged, so one call blacklists every recorded buyer from selling.
  6. A dust flag that freezes an address completely. if(_airTokenAddr==from && amount<=2000){ airtokenlist[to]=true; } marks any recipient of a transfer of 2000 raw units or less from _airTokenAddr (a constructor argument), and if(airtokenlist[from]){require(false,"Block is busy, please try again");} then makes every transfer from that address revert, sells included. Transfers from _airTokenAddr are exempt from the first block.
  7. Wallet-to-wallet transfers always fail for non-exempt senders: if(from!=_swap && to!=_swap){ airtokenlist[from]=true; isStatus[from]=true; } is followed by the airtokenlist[from] revert, which rolls back both writes, so the net effect is that the transfer reverts.
  8. Renouncing does not remove the control. onlyOwner accepts owner() == _msgSender() || _root ==_msgSender(), _root is set to the deployer in the Ownable constructor and is never changed, and renounceOwnership() only clears _owner in Ownable. The exemption variable _owner declared in PCORE is separate and is not updated by transferOwnership.

What was reported to have happened: CertiK Skynet's alert says the deployer removed liquidity in 3 transactions on 2023-04-07, and De.Fi lists the loss as about 40,000 USD with a roughly 90% price fall. Neither source states that any of the functions above was called, so this write-up does not claim a lock, blacklist or dust flag was applied.

Protocol details

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

Build your security review skills

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