DeFiVulnLabs靶场全系列详解(二十五)未经检查的返回值(不符合ERC20标准)

admin 2025年4月8日12:49:20评论5 views字数 3155阅读10分31秒阅读模式
01
前言
此内容仅作为展示Solidity常见错误的概念证明。它严格用于教育目的,不应被解释为鼓励或认可任何形式的非法活动或实际的黑客攻击企图。所提供的信息仅供参考和学习,基于此内容采取的任何行动均由个人全权负责。使用这些信息应遵守适用的法律、法规和道德标准。
DeFiVulnLabs一共有47个漏洞实验,包括各种经典的合约漏洞和一些少见的可能造成安全问题的不安全代码,本系列将逐一解析每个漏洞,包括官方的解释和自己的理解。
02
未经检查的返回值(不符合ERC20标准)

漏洞解析:

在ERC20标准里,标准规定了transfer/transferFrom转账类的相关方法需要返回true或者false来表示成功或者失败,但是USDT代币里交易成功和失败都没有返回值,这是不遵循ERC20标准的,可能是因为USDT是一个早期的代币合约,由于历史原因导致的。 
    我认为这并不是一个漏洞,而是一种写法规范的问题,可能会因为ERC20的这种写法规范,导致其他合约可能出现安全问题

代码解析:

import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";interface USDT {    function transfer(address to, uint256 value) external;    function balanceOf(address account) external view returns (uint256);    function approve(address spender, uint256 value) external;}contract ContractTest is Test {    using SafeERC20 for IERC20;    //引入USDT    IERC20 constant usdt = IERC20(0xdAC17F958D2ee523a2206206994597C13D831ec7);    //fork主网,高度为16138254    function setUp() public {        vm.createSelectFork("mainnet"16138254);    }    //给该合约转账123个USDT    function testTransfer() public {        vm.startPrank(0xef0DCc839c1490cEbC7209BAa11f46cfe83805ab);        //普通的转账        usdt.transfer(address(this), 123); //revert        vm.stopPrank();    }    //给该合约转账123个USDT    function testSafeTransfer() public {        vm.startPrank(0xef0DCc839c1490cEbC7209BAa11f46cfe83805ab);        //使用SafeERC20的safeTransfer转账,会revert回滚        usdt.safeTransfer(address(this), 123);        vm.stopPrank();    }    receive() external payable {}}

上面这个合约代码的意思就是展示了了USDT两种方式的转账,如果按照标准的ERC20格式来转账,就会失败回滚。

使用safeTransfer,也是一样的回滚,但是它更标准化。

对于使用Foundry和remix可能小伙伴们使用已经忘记了,可以查看我的第一篇文章:

DeFiVulnLabs靶场全系列详解(一)溢出漏洞

也可查看该文章:

https://mp.weixin.qq.com/s/IuLD--prbWJYgrrsWRZr_A

USDT 合约地址:

0xdAC17F958D2ee523a2206206994597C13D831ec7

function transferFrom(address _from, address _to, uint _valuepubliconlyPayloadSize(3 * 32) {        var _allowance = allowed[_from][msg.sender];        // Check is not needed because sub(_allowance, _value) will already throw if this condition is not met        // if (_value > _allowance) throw;        uint fee = (_value.mul(basisPointsRate)).div(10000);        if (fee > maximumFee) {            fee = maximumFee;        }        if (_allowance < MAX_UINT) {            allowed[_from][msg.sender] = _allowance.sub(_value);        }        uint sendAmount = _value.sub(fee);        balances[_from] = balances[_from].sub(_value);        balances[_to] = balances[_to].add(sendAmount);        if (fee > 0) {            balances[owner] = balances[owner].add(fee);            Transfer(_from, owner, fee);        }        Transfer(_from, _to, sendAmount);    }

可以看到transferFrom没有返回任何值,这也就意味着其他交易智能合约调用USDT进行交易的时候需要小心。

如果用户交易失败了,但是没有正确处理返回值的话,可能会造成用户体验上的问题。且较难定位,所以这个点严格意义上算是一种写法规范的问题,并不是实际的安全问题。

解决方法就是所有的ERC20标准化代币交易,都引入SafeERC20的safeTransfer即可。

03
如何修复该问题
所有的标准化ERC20都要记得引入SafeERC20,USDT就是一个不标准的ERC20代币
// 添加导入声明import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";import "@openzeppelin/contracts/utils/math/SafeMath.sol";// 使用 SafeERC20 和 SafeMathusing SafeERC20 for IERC20;using SafeMath for uint256;...
    // 使用 SafeERC20 进行转账    IERC20(address(this)).safeTransferFrom(_from, _to, sendAmount);    // 手续费处理    if (fee > 0) {        IERC20(address(this)).safeTransferFrom(_from, owner, fee);        emit Transfer(_from, owner, fee);    }
04
感谢关注
个人语雀账号:https://www.yuque.com/iceqaq

原文始发于微信公众号(Ice ThirdSpace):DeFiVulnLabs靶场全系列详解(二十五)未经检查的返回值(不符合ERC20标准)

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年4月8日12:49:20
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   DeFiVulnLabs靶场全系列详解(二十五)未经检查的返回值(不符合ERC20标准)https://cn-sec.com/archives/3929246.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息