DeFiVulnLabs靶场全系列详解(三十六)没有设置滑点保护,允许最小代币接收数量为0,导致代币价值遭受损失

admin 2025年5月7日16:58:39评论0 views字数 4092阅读13分38秒阅读模式
01
前言
此内容仅作为展示Solidity常见错误的概念证明。它严格用于教育目的,不应被解释为鼓励或认可任何形式的非法活动或实际的黑客攻击企图。所提供的信息仅供参考和学习,基于此内容采取的任何行动均由个人全权负责。使用这些信息应遵守适用的法律、法规和道德标准。
    DeFiVulnLabs一共有47个漏洞实验,包括各种经典的合约漏洞和一些少见的可能造成安全问题的不安全代码,本系列将逐一解析每个漏洞,包括官方的解释和自己的理解。
02
没有设置滑点保护,允许最小代币接收数量为0,导致代币价值遭受损失
滑点:交易执行时的实际价格与预期价格之间的差异,通常是因为巨大波动引起的流动性不足所产生的。例如我预计价格是100,滑点设置为1%,那么价格在101——99直接都是可以成交的,那么按照这个理论,如果滑点设置为0%,那么价格则是必须以100块成交,多一分或者少一分都不行。
而滑点设置为0几乎是不可能实现的,因为市场价格变动非常频繁。
DeFiVulnLabs靶场全系列详解(三十六)没有设置滑点保护,允许最小代币接收数量为0,导致代币价值遭受损失
DeFiVulnLabs靶场全系列详解(三十六)没有设置滑点保护,允许最小代币接收数量为0,导致代币价值遭受损失
但是实际上呢,我对于这里翻译“If both the slippage is set to 0 and there is no deadline, users might potentially lose all their tokens.
如果滑点设置为0,且没有截止期限,用户可能会损失所有代币。应该是表述不准确的。
应该不是滑点设置为0,而是将amountOutMin设置为0,所以用户才可能损失所有代币。
代码地址:
https://github.com/SunWeb3Sec/DeFiVulnLabs/blob/main/src/test/Slippage-deadline.sol
漏洞解析:
    同上面所说的,本篇案例中说的滑点为0实际上并不是滑点为0,而是愿意接受的最小代币数量被设置为了0,因此滑点实际上变成了100%了,那么也就意味着愿意接收在超大波动的范围下接收0个代币,损失所有代币价值。
代码解析:
    代码的主要意思就是通过Uniswap V2去中心化交易所将WETH兑换成USDT币。
    而amountOutMin设置为0了,即期望至少获得0数量的USDT,代表愿意接受的USDT数量可以为0。
DeFiVulnLabs靶场全系列详解(三十六)没有设置滑点保护,允许最小代币接收数量为0,导致代币价值遭受损失
下面的合约代码首先是进行了Uniswap V2路由一些接口和方法的实例化说明
所以下面的合约在通过uniswap V2交易时,这个合约不会因滑点而拒绝任何交易,即使实际输出为0,交易也会被认为是成功的。
interfaceIUniswapV2Router02{function  swapExactTokensForTokens(        uint256 amountIn,  //输入代币数量(用户卖出数量        uint256 amountOutMin,  //用户希望输出的代币数量        address[] calldata path, //交易路径,例如weth到usdc        address to, // 接受代币的地址        uint256 deadline // 截止交易时间    )externalreturns(uint256[] memory amounts);}//实际返回的交易数量interfaceIWETH{function   deposit()externalpayable;function   approve(address guy, uint256 wad)externalreturns(bool);function   withdraw(uint256 wad)external;}//设置一个uniswap地址池,交易币种为IWETH和USDTcontract ContractTest is Test {    address UNISWAP_ROUTER = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D// Uniswap Router address on Ethereum Mainnet    IWETH WETH = IWETH(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);    address USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7;//它创建了一个以太坊主网的分叉,从区块号17568400开始function  setUp()public{        vm.createSelectFork("mainnet"17568400);    }functiont  estswapTokensWithMaxDeadline()externalpayable{        WETH.approve(address(UNISWAP_ROUTER), type(uint256).max);        WETH.deposit{value1 ether}();        uint256 amountIn = 1 ether;        //定义了最小接收数量,这里设置为0        uint256 amountOutMin = 0;//uint256 amountOutMin = 1867363899; //1867363899 INSUFFICIENT_OUTPUT_AMOUNT// Path for swapping ETH to USDT        address[] memory path = new address[](2);        path[0] = address(WETH); // WETH (Wrapped Ether)        path[1] = USDT// USDT (Tether)// No Effective Expiration Deadline// The function sets the deadline to the maximum uint256 value, which means the transaction can be executed at any time,// possibly under unfavorable market conditions.        IUniswapV2Router02(UNISWAP_ROUTER).swapExactTokensForTokens(            amountIn,            amountOutMin,            path,            address(this),            type(uint256).max // Setting deadline to max value        );        console.log("USDT"IERC20(USDT).balanceOf(address(this)));    }    receive() external payable {}}
这是存在问题的地方uint256 amountOutMin 设置为了0.
functiont  estswapTokensWithMaxDeadline()externalpayable{        WETH.approve(address(UNISWAP_ROUTER), type(uint256).max);        WETH.deposit{value: 1 ether}();        uint256 amountIn = 1 ether;        //定义了最小接收数量,这里设置为0        uint256 amountOutMin = 0;//uint256 amountOutMin = 1867363899; //1867363899 INSUFFICIENT_OUTPUT_AMOUNT// Path for swapping ETH to USDT        address[] memory path = new address[](2);        path[0] = address(WETH); // WETH (Wrapped Ether)        path[1] = USDT; // USDT (Tether)// No Effective Expiration Deadline// The function sets the deadline to the maximum uint256 value, which means the transaction can be executed at any time,// possibly under unfavorable market conditions.        IUniswapV2Router02(UNISWAP_ROUTER).swapExactTokensForTokens(            amountIn,            amountOutMin,            path,            address(this),            type(uint256).max // Setting deadline to max value        );
03
复现
可以看到获取了1867363879的USDT,并且之后我尝试了多次,都是这个值,也就意味着正常来说获得兑换成0代币,是非常非常极端的事件。
毕竟WETH的价格不可能出现瞬间为0的时候。
DeFiVulnLabs靶场全系列详解(三十六)没有设置滑点保护,允许最小代币接收数量为0,导致代币价值遭受损失
04
如何修复该问题
允许用户自己指定滑点和截止日期值。这个值可以设置为如下
    设置1%的滑点:
uint256 amountOutMin = amountIn * 99 / 100
    设置0.5%的滑点
uint256 amountOutMin = amountIn * 995 / 1000
 还要设置截止日期,不能让截止日期为永久,一般dealline设置久一点可以是1星期,设置少点10min。
虽然本漏洞案例几乎不可能发生,但是可能存在一些垃圾山寨币会瞬间归0再反弹(波动很大)就可能产生问题,虽然概率也很小,但是一旦发生损失是极为严重的。
05
感谢关注
个人语雀账号:https://www.yuque.com/iceqaq

原文始发于微信公众号(Ice ThirdSpace):DeFiVulnLabs靶场全系列详解(三十六)没有设置滑点保护,允许最小代币接收数量为0,导致代币价值遭受损失

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年5月7日16:58:39
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   DeFiVulnLabs靶场全系列详解(三十六)没有设置滑点保护,允许最小代币接收数量为0,导致代币价值遭受损失https://cn-sec.com/archives/4036816.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息