Farseen0/scar-pairs huggingface dataset:
HIGH severity: SumerMoney — - The reentrancy in the function `repayBorrowBehalf` allows the attacker to manipulate the exchanged rate of `ETH:sdrETH` by borrowing against all of the assets held in the contract. Then he could redeem all the tokens to gain profits.. https://medium.com/neptune-mutual/how-was-sumer-money-exploited-2c114ebffaa8 https://twitter.com/0xNickLFranklin/status/1778986926705672698

Vulnerable code:
function repayBorrowBehalf(address borrower) external payable {
  uint256 received = msg.value;
  uint256 borrows = CEther(payable(this)).borrowBalanceCurrent(borrower);
  if (received > borrows) {
    // payable(msg.sender).transfer(received - borrows);
    (bool success, ) = msg.sender.call{value: received - borrows}("");
    require(success, "Address: unable to send value, recipient may have reverted");
  }
  (uint256 err, ) = repayBorrowBehalfInternal(borrower, borrows);
  requireNoError(err, "repayBorrowBehalf failed");
}

Vulnerability type: Reentrancy
HIGH severity: DEUS DAO — Incorrect price calculation via balanceOf.. The vulnerable code snippet attempts to calculate the on-chain LP price. However, it uses `balanceOf` to retrieve token balances, which can be manipulated by attackers exploiting flashloans. This manipulation leads to inaccurate price calculations and potential exploitation.

Vulnerable code:
/// @notice returns on chain LP price
function getOnChainPrice() public view returns (uint256) {
    return
        ((dei.balanceOf(address(pair)) * IBaseV1Pair(address(pair)).getAmountOut(1e18, address(dei)) * 1e12 / 1e18) + (usdc.balanceOf(address(pair)) * 1e12)) * 1e18 / pair.totalSupply(); // vulnerable point
}

Vulnerability type: DAO, Flashloans
HIGH severity: Eleven Finance — Incorrect logic flow. In emergencyBurn function, we can see incorrect logic, withdraw token but without burning token. So attacker can withdraw again after calling emergencyBurn.

Vulnerable code:
function emergencyBurn() public {  //vulnerable point
        uint balan = balanceOf(msg.sender);
        uint avai = available();
        if(avai<balan) IMasterMind(mastermind).withdraw(nrvPid, (balan.sub(avai)));
        token.safeTransfer(msg.sender, balan);
        emit Withdrawn(msg.sender, balan, block.number);
    }

Vulnerability type: Incorrect logic, Yield
HIGH severity: XCarnival — call XToken contract's borrow(), there is no judgment that the NFT has been withdrawn.. Parameter xToken can control, attakcer can withdraw pledged NFT.

Vulnerable code:
function pledgeAndBorrow(address _collection, uint256 _tokenId, uint256 _nftType, address xToken, uint256 borrowAmount) external nonReentrant {
        uint256 orderId = pledgeInternal(_collection, _tokenId, _nftType);
        IXToken(xToken).borrow(orderId, payable(msg.sender), borrowAmount); //vulnerable point
    }

Vulnerability type: ERC721, Insufficient validation
HIGH severity: swapX — Contract X mentioned above is the implementation contract of SwapX Proxy (not yet verified and audited), the Proxy will allow users to pass data and call the implementation contract: [https://bscscan.com/address/0x0ccee62efec983f3ec4bad3247153009fb483551]. https://twitter.com/BlockSecTeam/status/1630111965942018049 https://twitter.com/peckshield/status/1630100506319413250

Vulnerable code:
function swap(
    IERC20 srcToken,
    IERC20 dstToken,
    address dstReceiver,
    uint256 amount,
    uint256 minReturnAmount,
    address referrer,
    bytes calldata data
) 
    external 
    payable 
    whenNotPaused
    returns (uint256 returnAmount)
{
    require(minReturnAmount > 0, "Min return should not be 0");
    require(data.length > 0, "Call data should exist");

    dstReceiver = (dstReceiver == address(0)) ? msg.sender : dstReceiver;
    uint256 initialSrcBalance = srcToken.universalBalanceOf(msg.sender);
    uint256 initialDstBalance = dstToken.universalBalanceOf(dstReceiver);

    {
    (bool success, bytes memory returndata) = swapXImpl.call{value: msg.value}(data); // vulnerable point
    if (!success)  {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly

            // solhint-disable-next-line no-inline-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert("swap failed");
        }
    }
    }
}

Vulnerability type: Access Control, Price Manipulation
HIGH severity: Orion Protocol — Reentrancy in the doSwapThroughOrionPool function. https://twitter.com/BlockSecTeam/status/1621263393054420992 https://www.numencyber.com/analysis-of-orionprotocol-reentrancy-attack-with-poc/

Vulnerable code:
function doSwapThroughOrionPool(
    address user,
    address to,
    IPoolFunctionality.SwapData calldata swapData
) external override returns (uint256 amountOut, uint256 amountIn) {
    bool withFactory = swapData.path.length > 2 &&
        (supportedFactories[swapData.path[0]] != FactoryType.UNSUPPORTED);
    address curFactory = withFactory ? swapData.path[0] : factory;
    address[] memory new_path;

    uint256 tokenIndex = withFactory ? 1 : 0;
    new_path = new address[](swapData.path.length - tokenIndex);

    for ((uint256 i, uint256 j) = (tokenIndex, 0); i < swapData.path.length; (++i, ++j)) {
        new_path[j] = swapData.path[i] == address(0) ? WETH : swapData.path[i];
    }

    (uint256 amount_spend_base_units, uint256 amount_receive_base_units) = (
        LibUnitConverter.decimalToBaseUnit(
            swapData.path[tokenIndex],
            swapData.amount_spend
        ),
        LibUnitConverter.decimalToBaseUnit(
            swapData.path[swapData.path.length - 1],
            swapData.amount_receive
        )
    );
    {
    (uint256 userAmountIn, uint256 userAmountOut) = _doSwapTokens(InternalSwapData( //vulnerable point
        user,
        amount_spend_base_units,
        amount_receive_base_units,
        withFactory ? swapData.path[1] : swapData.path[0],
        new_path,
        swapData.is_exact_spend,
        to,
        curFactory,
        supportedFactories[curFactory],
        swapData.supportingFee
    ));

    //  Anyway user gave amounts[0] and received amounts[len-1]
    amountOut = LibUnitConverter.baseUnitToDecimal(
        swapData.path[tokenIndex],
        userAmountIn
    );
    amountIn = LibUnitConverter.baseUnitToDecimal(
        swapData.path[swapData.path.length - 1],
        userAmountOut
    );
    }
}

Vulnerability type: Reentrancy
HIGH severity: ZunamiProtocol — Zunami Protocol's contract calculates account balances using the cacheAssetPrice() value, which depends on totalHoldings. The attacker manipulated totalHoldings by flash loaning in funds from multiple sources, then swapping these funds for 4.8M UZD on Curve.. https://twitter.com/peckshield/status/1690877589005778945 https://twitter.com/BlockSecTeam/status/1690931111776358400

Vulnerable code:
function cacheAssetPrice() public virtual {
    _blockCached = block.number;
    uint256 currentAssetPrice = assetPrice();
    if (_assetPriceCached < currentAssetPrice) {
        _assetPriceCached = currentAssetPrice;
        emit CachedAssetPrice(_blockCached, _assetPriceCached);
    }
}

Vulnerability type: Price Manipulation
HIGH severity: CurveBurner — This attack was made possible by maliciously calling the `execute()`function of the `UnderlyingBurner` contract(which is slippage tolerant), the attacker uses the execute function to further imbalance the 3pool allowing the attacker to buy tokens (like DAI and USDC) low, and sell them at a high prices. https://medium.com/@Hypernative/exotic-culinary-hypernative-systems-caught-a-unique-sandwich-attack-against-curve-finance-6d58c32e436b

Vulnerable code:
@external
 def execute() -> bool:
    """
    @notice Add liquidity to 3pool and transfer 3CRV to the fee distributor
    @return bool success
    """
    assert not self.is_killed  # dev: is killed

    amounts: uint256[3] = [
        ERC20(TRIPOOL_COINS[0]).balanceOf(self),
        ERC20(TRIPOOL_COINS[1]).balanceOf(self),
        ERC20(TRIPOOL_COINS[2]).balanceOf(self),
    ]
    if amounts[0] != 0 and amounts[1] != 0 and amounts[2] != 0:
        StableSwap(TRIPOOL).add_liquidity(amounts, 0)

    amount: uint256 = ERC20(TRIPOOL_LP).balanceOf(self)
    if amount != 0:
        ERC20(TRIPOOL_LP).transfer(self.receiver, amount)

    return True

@payable
@external
 def burn(_coin: address) -> bool:
    """
    @notice Receive `_coin` and swap for USDC if not a 3pool asset
    @param _coin Address of the coin being received
    @return bool success
    """
    assert not self.is_killed  # dev: is killed

    # transfer coins from caller
    amount: uint256 = ERC20(_coin).balanceOf(msg.sender)
    if amount != 0:
        response: Bytes[32] = raw_call(
            _coin,
            concat(
                method_id("transferFrom(address,address,uint256)"),
                convert(msg.sender, bytes32),
                convert(self, bytes32),
                convert(amount, bytes32),
            ),
            max_outsize=32,
        )
        if len(response) != 0:
            assert convert(response, bool)

    # if coin is not in 3pool, swap it for USDC
    if not _coin in TRIPOOL_COINS:
        registry_swap: address = AddressProvider(ADDRESS_PROVIDER).get_address(2)

        if not self.is_approved[registry_swap][_coin]:
            response: Bytes[32] = raw_call(
                _coin,
                concat(
                    method_id("approve(address,uint256)"),
                    convert(registry_swap, bytes32),
                    convert(MAX_UINT256, bytes32),
                ),
                max_outsize=32,
            )
            if len(response) != 0:
                assert convert(response, bool)
            self.is_approved[registry_swap][_coin] = True

        if _coin == SUSD:
            # if the coin is sUSD, settle prior to exchanging
            Synthetix(SNX).settle(SUSD_CURRENCY_KEY)

        # get actual balance in case of transfer fee or pre-existing balance
        amount = ERC20(_coin).balanceOf(self)
        if amount != 0:
            RegistrySwap(registry_swap).exchange_with_best_rate(_coin, USDC, amount, 0)

    return True

Vulnerability type: Price Manipulation, Sandwich
HIGH severity: DePayRouter — A flash loan attack on the DePay platform resulted in the theft of 827 USDC. The exploiter took advantage of a security issue in the DePay router to execute the theft.. [CertiK Alert on Twitter / X](https://x.com/CertiKAlert/status/1709764146324009268)

Vulnerable code:
// The main function to route transactions.
function route(
    // The path of the token conversion.
    address[] calldata path,
    // Amounts passed to processors:
    // e.g. [amountIn, amountOut, deadline]
    uint[] calldata amounts,
    // Addresses passed to plugins:
    // e.g. [receiver]
    address[] calldata addresses,
    // List and order of plugins to be executed for this payment:
    // e.g. [Uniswap,paymentPlugin] to swap and pay
    address[] calldata plugins,
    // Data passed to plugins:
    // e.g. ["signatureOfSmartContractFunction(address,uint)"] receiving the payment
    string[] calldata data
) external payable returns (bool) {
    uint balanceBefore = _balanceBefore(path[path.length - 1]);
    _ensureTransferIn(path[0], amounts[0]);
    _execute(path, amounts, addresses, plugins, data);
    _ensureBalance(path[path.length - 1], balanceBefore);
    return true;
}

Vulnerability type: Business Logic Flaw, Flashloans
HIGH severity: SDAO — sDAO contract have the following pitfalls:, 1. missing target token validation or access control on `withdrawTeam`, as the result, attacker may manipulate the reward output, 2. logic flaw on reward generation, the user can get reward in the same block when `to == address(LPInstance)`. 1. The attacker borrowed $500 `USDT` from `DPPOracle` in a flash loan. 2. They converted half of the `USDT` into `sDAO。 3. They added liquidity to the `sDAO/USDT` pool with half of the remaining `USDT` and the `sDAO` they had just converted. 4. They staked half of the LP tokens they received in the

Vulnerable code:
function getPerTokenReward() public view returns(uint) {
    if ( LPInstance.balanceOf(address(this)) == 0) {
        return 0;
    }

    uint newPerTokenReward = (totalStakeReward - lastTotalStakeReward) * 1e18 / LPInstance.balanceOf(address(this));
    return PerTokenRewardLast + newPerTokenReward;
}

function transferFrom(
    address from,
    address to,
    uint amount
) public virtual override returns (bool) {
    address spender = _msgSender();
    if ( to == address(LPInstance) && tx.origin != address(0x547d834975279964b65F3eC685963fCc4978631E) ) {
        totalStakeReward += amount * 7 / 100;
        _standardTransfer(from, address(this), amount * 7 / 100 );
        _standardTransfer(from, address(0x0294a4C3E85d57Eb3bE568aaC17C4243d9e78beA), amount / 100 );
        _burn(from, amount / 50);
        amount = amount * 90 / 100;
    }

    _spendAllowance(from, spender, amount);
    _transfer(from, to, amount);
    return true;
}

function withdrawTeam(address _token) external {
    IERC20(_token).transfer(TEAM, IERC20(_token).balanceOf(address(this)));
    payable(TEAM).transfer(address(this).balance);
}

Vulnerability type: Business Logic Flaw, Flashloans
HIGH severity: SNK — Phalcon: The attacker exploits the `bindParent` function in contract SNKMiner to amplify his staking rewards. Specifically, this function binds one account as a staking account's child.

SNKMiner depends on SNK balance of staking accounts' children to calculate the amount of reward(SNK) distributing to staking accounts. As a result, the attacker leverages flashloan to increase the SNK balance of children, which can further lift their parents' staking rewards.

KALOS: The vulnerability lies in the bindParent() and getReward() functions. The bindParent() function allows setting the parent and child nodes, while the dynamicEarned() function within the getReward() function calculates rewards based on the balances of those child nodes. As a result, it is possible to receive a much larger amount of rewards than what should actually be obtained.. https://twitter.com/Phalcon_xyz/status/1656176776425644032 https://twitter.com/kalos_security/status/1668092691262091265

Vulnerable code:
function getReward() public updateReward(msg.sender) checkStart {
    uint256 reward = dynamicEarned(msg.sender) + privateEarned(msg.sender); // vulnerable point
    if (reward > 0) {
        prewards[msg.sender] = 0;
        drewards[msg.sender] = 0;

        token.safeTransfer(msg.sender, reward);

        totalRewards = totalRewards.add(reward);
    }
}

function privateEarned(address account) public view returns (uint256) {
    if (block.timestamp < starttime) {
        return 0;
    }

    return
        balanceOf(account)
            .mul(rewardPerToken().sub(userRewardPerTokenPaid[account]))
            .mul(35)
            .div(precision)
            .div(100)
            .add(prewards[account]);
}

function dynamicEarned(address account) public view returns (uint256) {
    if (block.timestamp < starttime) {
        return 0;
    }

    if (balanceOf(account) < 10e18) {
        return 0;
    }
    
    return
        _getMyChildersBalanceOf(account)
            .mul(rewardPerToken().sub(userRewardPerTokenPaid[account]))
            .mul(45)
            .div(precision)
            .div(100)
            .add(drewards[account]);
}

function _getMyChildersBalanceOf(address user) private view returns (uint256) {
    address[] memory childers = inv.getInviterSuns(user);

    uint256 totalBalances;
    for (uint256 index = 0; index < childers.length; index++) {
        totalBalances += balanceOf(childers[index]); // vulnerable point
    }

    return totalBalances;
}

Vulnerability type: Flashloans, Reward
HIGH severity: Umbrella Network — Arithmetic Overflow. The vulnerable code snippet shows a function `_withdraw` that calculates the updated total supply and user balance by subtracting the withdrawal amount. However, it does not use safe math functions to prevent arithmetic overflow. This vulnerability allows an attacker to manipulate the balance values

Vulnerable code:
/// @param amount tokens to withdraw
/// @param user address
/// @param recipient address, where to send tokens, if we migrating token address can be zero
function _withdraw(uint256 amount, address user, address recipient) internal nonReentrant updateReward(user) {
    require(amount != 0, "Cannot withdraw 0");

    // not using safe math, because there is no way to overflow if stake tokens not overflow
    _totalSupply = _totalSupply - amount;
    _balances[user] = _balances[user] - amount;  // vulnerable point, overflow
    // not using safe transfer, because we working with trusted tokens
    require(stakingToken.transfer(recipient, amount), "token transfer failed");

    emit Withdrawn(user, amount);
}

Vulnerability type: Oracle, Under/Overflow, staking
HIGH severity: LFI Token — There is no time lock for the user 'staking' amount(balance).. https://twitter.com/AnciliaInc/status/1660767088699666433

Vulnerable code:
function claimRewards(address to) external {
    require(to != address(0), "HOUSEPOOL:to address can't be zero");
    cleanUserMapping();
    FarmInfo memory farm = updateFarm();
    UserInfo storage user = userInfo[msg.sender];
    int256 accumulatedReward = int256(   // vulnerable point: no time lock check
        (balance0f(msg.sender) * farm.accRewardsPerShare) /
            ACC_REWARD_PRECISION
    );

    uint256 _pendingReward = uint256(accumulatedReward - user.rewardDebt);
    pendingRewards[msg.sender] = 0;
    user.rewardDebt = accumulatedReward;
    bool success = ILFIToken(STAKED_TOKEN).transfer(to, _pendingReward);

    if (success) {
        emit RewardsClaimed(msg.sender, to, _pendingReward);
    } else {
        revert();
    }
}

Vulnerability type: Business Logic Flaw
HIGH severity: ALP — The `_swap()` is public which should be internal. https://twitter.com/0xNickLFranklin/status/1765296663667875880

Vulnerable code:
function _swap(
    IERC20 tokenForSwap,
    bytes memory aggregatorData
) public returns (uint256) {
    SafeERC20.forceApprove(
        tokenForSwap,
        oneInchAggregatorAddress,
        tokenForSwap.balanceOf(address(this))
    );
    // slither-disable-next-line Low-Level-Calls
    (bool succ, bytes memory data) = address(oneInchAggregatorAddress).call(
        aggregatorData
    );
    require(
        succ,
        "Aggregator failed to swap, please update your block_number when running hardhat test"
    );
    return abi.decode(data, (uint256));
}

Vulnerability type: Business Logic Flaw
HIGH severity: Chainge — setReward without access control. Everyone can set reward.. https://x.com/ChainAegis/status/1779809931962827055 https://x.com/0xNickLFranklin/status/1779782276198682993

Vulnerable code:
function setReward(
    address rewardSender,
    uint256 amount,
    uint256 remain,
    uint256 price
) public {
    if (reward[rewardSender].length == 0) {
        rewardKeys.push(rewardSender);
    }

    reward[rewardSender].push(
        RewardData(rewardSender, amount, remain, price, block.timestamp)
    );
    _totalRemainCnt += remain;
}

Vulnerability type: Business Logic Flaw
HIGH severity: ShadowFi — incorrect access control. Due to the public access of burn() method, an attacker burned 10.3M SDF in the pair and then sync'd the price.

Then they swap 8.4 SDF for 1078 BNB (~$298.2K) and transfer to tornato

Vulnerable code:
function burn(address account, uint256 _amount) public { //vulnerable point
    _transferFrom(account, DEAD, _amount);

    emit burnTokens(account, _amount);
}

Vulnerability type: Access Control, payment
HIGH severity: AnnexFinance — Missing validation on target token for the liquidator. 1. The attacker prepares a new token and creates pool(add liquidity) for the new token
2.  Invoke the swap function with new pair and trigger the liquidating process
3. Since there is no limitation on the liquidate target, the attacker put the attack contract address for three times for the liquidat

Vulnerable code:
function pancakeCall(address sender, uint amount0, uint amount1, bytes calldata data) override external {
        // Unpack parameters sent from the `liquidate` function
        // NOTE: these are being passed in from some other contract, and cannot necessarily be trusted
        (address borrower, address repayAToken, address seizeAToken) = abi.decode(data, (address, address, address));

        address token0 = IPancakePair(msg.sender).token0();
        address token1 = IPancakePair(msg.sender).token1();
        require(msg.sender == IPancakeFactory(FACTORY).getPair(token0, token1));

        if (repayAToken == seizeAToken) {
            uint amount = amount0 != 0 ? amount0 : amount1; 
            address estuary = amount0 != 0 ? token0 : token1; //@note -> BNB

            // Perform the liquidation
            IERC20(estuary).safeApprove(repayAToken, amount);
            ABep20(repayAToken).liquidateBorrow(borrower, amount, seizeAToken);

            // Redeem aTokens for underlying ERC20
            ABep20(seizeAToken).redeem(IERC20(seizeAToken).balanceOf(address(this)));

            // Compute debt and pay back pair
            IERC20(estuary).transfer(msg.sender, (amount * 1000 / 997) + 1);
            return;
        }

        // ... (remaining code omitted for brevity)
    }

Vulnerability type: Insufficient validation
HIGH severity: 0x0DEX — The 0x0DEX contract was exploited through parameter manipulation. It allows private fund transfers (ETH) via `deposit` and `withdraw` functions and added a `swapOnWithdrawal` function for private swaps of deposited funds. The `swapOnWithdrawal` function was exploited. The amount withdrawn, after fees, is set as `_lastWithdrawal` only when `withdrawalData.wType` is `WithdrawalType.Swap`. For normal withdrawals (`withdrawalType.Direct`), `_lastWithdrawal` isn't set. This oversight allowed exploitation: if Alice swaps 10 ETH (`withdrawalType.Swap`) and Bob swaps 0.1 ETH (`withdrawalType.Direct`), the `_lastWithdrawal` remains 10 ETH. Thus, Bob could receive 10 ETH worth of tokens instead of his intended 0.1 ETH.. https://0x0ai.notion.site/0x0-Privacy-DEX-Exploit-25373263928b4f18b31c438b2a040e33

Vulnerable code:
function swapOnWithdrawal(
        address tokenOut,
        address payable recipient,
        uint256 relayerGasCharge, 
        uint256 amountOut,
        uint256 deadline,
        WithdrawalData memory withdrawalData
    ) external {
        require(recipient != address(0), "ZERO_ADDRESS");
        
        withdrawalData.wType = Types.WithdrawalType.Swap;
        withdraw(
            recipient, 
            withdrawalData,
            relayerGasCharge
        );

        uint amountIn = _lastWithdrawal;
        uint relayerFee = getRelayerFeeForAmount(amountIn);

        address payable relayerAddress = IOxODexFactory(factory).relayerAddress();
        (bool sent, ) = relayerAddress.call{value: relayerFee}("");
        require(sent, "FAILED_TO_SEND_RELAYER_FEE");

        amountIn -= relayerFee;

        IUniswapV2Router router = IUniswapV2Router(UNISWAP_V2_ROUTER);
        address;
        path[0] = WETH;
        path[1] = tokenOut;

        router.swapExactETHForTokens{value: amountIn}(
            amountOut, 
            path, 
            recipient, 
            deadline
        );
        _lastWithdrawal = 0;
        emit Swap(tokenOut, withdrawalData.amount, amountIn);
    }

Vulnerability type: Business Logic Flaw