DeFiVulnLabs靶场全系列详解(三十五)ERC20代币不同精度导致的精度损失:四舍五入为0

admin 2025年5月6日16:52:19评论0 views字数 2255阅读7分31秒阅读模式
01
前言
此内容仅作为展示Solidity常见错误的概念证明。它严格用于教育目的,不应被解释为鼓励或认可任何形式的非法活动或实际的黑客攻击企图。所提供的信息仅供参考和学习,基于此内容采取的任何行动均由个人全权负责。使用这些信息应遵守适用的法律、法规和道德标准。
DeFiVulnLabs一共有47个漏洞实验,包括各种经典的合约漏洞和一些少见的可能造成安全问题的不安全代码,本系列将逐一解析每个漏洞,包括官方的解释和自己的理解。
02
四舍五入为0
漏洞解析:
所有的遵循ERC20规范的代币,都是可以自由控制精度的。例如USDT和USDC有6位小数,但是可能其他的代币有更小的精度范围。
而如果计算的时候分子小于分母,那么在计算的时候会进行向下取整,尤其是代币有不同精度计算的时候,可能容易会出现这种错误。
代码解析:
下面的代码展示了一个简单的如果分子小于分母,那么计算时就会进行向下取整(0)的案例
pragma solidity ^0.8.0;contract Calculator {functioncalculate(uint256 a, uint256 bpublicpurereturns (uint256{  require(b != 0"Denominator cannot be zero");  return a / b;    }}

我输入了9999999和10000000,可看到,因为分子小于分母,所以直接向下取整为0。

DeFiVulnLabs靶场全系列详解(三十五)ERC20代币不同精度导致的精度损失:四舍五入为0

如果是10/4=2.5,也会被向下取整为2

DeFiVulnLabs靶场全系列详解(三十五)ERC20代币不同精度导致的精度损失:四舍五入为0

说明完合约进行计算的特征后,进入借贷池合约中来进行模拟漏洞:https://github.com/SunWeb3Sec/DeFiVulnLabs/blob/main/src/test/Precision-loss.sol

如下合约展示了一个简单的借贷池合约SimplePool,部署合约时即开始计算利息_reward,但是以下代码存在利息计算公式 (totalDebt * _timeDelta) / (31536000 * 1e18) 中的除法可能会导致精度损失。

// SPDX-License-Identifier: MITpragma solidity ^0.8.0;//一个借贷池合约contract SimplePool {uintpublic totalDebt;  //代表债务uintpublic lastAccrueInterestTime;  //生成时间uintpublic loanTokenBalance; //借贷池中的代币余额    constructor() {        totalDebt = 10000e6//借了10000000000个代币        lastAccrueInterestTime = block.timestamp - 1//记录现在的时间        loanTokenBalance = 500e18//借贷池里有500W亿代币    }//合约function getCurrentReward() public view returns (uint _reward) {// 获取自上次计息以来经过的时间uint _timeDelta = block.timestamp - lastAccrueInterestTime; //// 如果时间差为0  则返回0if (_timeDelta == 0) return0;// 计算利息,随着时间不断增加//10000000000 * 1 / (365 days * 1000000000000000000        _reward = (totalDebt * _timeDelta) / (31536000  * 1e18);        _reward;    }}

主要的计算利息算法为

      _reward = (totalDebt * _timeDelta) / (365 days * 1e18);

    _timeDelta为随着时间增加的秒数,_reward是计算将将年利率转换为每秒钟的利率,并使用 1e18 调整精度。_reward(利息)正常来说会越来越大。

    所以计算利息_reward的方式是随着时间(每秒不断增加的),但是一眼可以看出分子在前期是明显小于分母的,所以利息为0会持续非常久的一段时间。

    利息存在很久时间为0就是漏洞问题所在。

    用户可以进行借款,但是在短时间内还款是无需利息的,可是实际上我们代码原本的逻辑就是从借钱的一瞬间就开始产生利息。

DeFiVulnLabs靶场全系列详解(三十五)ERC20代币不同精度导致的精度损失:四舍五入为0

上面的接待池测试代码利息年利率为100%,也就是借100块钱,在1年后需要还款200块。

03
如何修复该问题
    1、如果根据上面合约代码的逻辑的话,10s的利息,但是这样无疑在合约计算里会取整为0——10000e6 * 10s) /(31536000 *1e18)
DeFiVulnLabs靶场全系列详解(三十五)ERC20代币不同精度导致的精度损失:四舍五入为0
2、根据AI的回答应该采用以下方式来计算才是正确的
_reward = (totalDebt * _timeDelta* 1e18) / (31536000  * 1e18);
_reward = (10000000000 * 10s * 1e18) / (31,536,000 * 1e18);
DeFiVulnLabs靶场全系列详解(三十五)ERC20代币不同精度导致的精度损失:四舍五入为0

实际上这段代码的从业务逻辑角度来说,也不能说是存在漏洞,只能说是存在非预期的可能损失部分利息的bug。就算是给客户几天的时间免息,如果是预期的行为也是非常正常的。

05
感谢关注
个人语雀账号:https://www.yuque.com/iceqaq

原文始发于微信公众号(Ice ThirdSpace):DeFiVulnLabs靶场全系列详解(三十五)ERC20代币不同精度导致的精度损失:四舍五入为0

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

发表评论

匿名网友 填写信息