-
-
智能合约-整形溢出漏洞 笔记
-
发表于: 2020-4-29 11:42 21273
-
在商城换的那本《智能合约安全分析和审计指南》的笔记
整型溢出漏洞
用书上的例子来介绍一下:
一个小朋友,他可以数着手指运算十以内的运算,比如 1+1=2,他可以用两个手指算出来,但是如果你问他 5+6 等于多少,他数完十个手指之后发现手指不够用了,就会把手指扳回来,说:结果为 1,对于小朋友来说,这个问题就超纲“溢出”了
在 solidity 中,当一个整型变量高于或者低于他所能承受的范围时,就会发生溢出,导致一些不可预期的情况出现。例如,当用户转账金额超过系统预设的最大值时,只要用户金额大于零,用户就可以直接将巨额的代币转走
代码片段
function batchTransfer(address[] _receivers, uint256 _value) public whenNotPaused returns (bool){ uint cnt = _receivers.length; uint 256 amount = uint256(cnt) * _value; //出现了成发运算,且amount缺少溢出判断,因此存在溢出的可能 require(cnt > 0 && cnt <=20); require(_value >0 && balances[msg.sender] >= amount); //存在通过将amount溢出为0或极小值来绕过余额判断的可能 balances[msg.sender] = balances[msg.sender].sub(amount); for(uint i = 0; i < cnt; i++){ balances[_receivers[i]] = balances[_receivers[i]].add(_value); Transfer(msg.sender, _receivers[i], _value); } return true; }
这个函数的作用是:让用户同时向多个人转账。第一个参数 _receivers 为 Address 数组类型,代表接收者地址,也就是可以向一整个数组的人转账。第二个参数 _value 为转账金额。
这个函数的逻辑是:
获得数组的成员数(cnt),计算一共转多少钱(amount)
成员数要大于 0 且小于 20 ,然后转账的数值要大于 0 且要小于拥有的金额数才能继续
漏洞分析
uint 256 amount = uint256(cnt) * _value;
在上下文中,没有对 amount 进行溢出判断,如果攻击者将 amount 溢出为 0 或者其他很小的值就能绕过用于对账户余额的判断
require(_value >0 && balances[msg.sender] >= amount);
在代码中可以看到 转账的金额能够被 cnt 和 _value 所控制,所以我们可以操纵这俩数值,来达到目的
具体步骤如下:
- 创建两个地址,用于接收溢出转账
- 调用 batchTransfer() 函数,将 _receivers 设置为 uint256 的最大值 / 2 + 1
- 这样,当计算 amount = uint256 的最大值 + 1,就超过了 uint256 的最大范围,成功溢出为 0
奇数 115792089237316195423570985008687907853269984665640564039457584007913129639935 是 uint256 的最大值,如果把他除以二(刚好需要向下取整)然后加一,把这个作为 _value 的值,这样,再乘以一个 cnt 的值,得到的就刚好溢出
代码执行后,两个地址都会得到 _value 个 Token,也就是这两个账号会凭空增加 57896044618658097711785492504343953926634992332820282019728792003956564819968 个 Token(也就是 _value 值)
代码调试
去 这里 看一下
代码复制出来,然后拿到 remix IDE 里面去编译一下
然后在 Run 里面,选择 Environment 为 JavaScript VM,然后选择 BecToken 合约 点击 Deplay 进行部署
记录一下账户的地址:
1:0xca35b7d915458ef540ade6068dfe2f44e8fa733c(主账户)
2:0x14723a09acff6d2a60dcdf7aa4aff308fddc160c
3:0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db
_receivers:
["0x14723a09acff6d2a60dcdf7aa4aff308fddc160c","0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db"]
_value:
57896044618658097711785492504343953926634992332820282019728792003956564819968
然后点击 batchTransfer 的 transact
再用 balanceOf 看一下账户余额是不是变化了
一开始主账户的金额:
其他账户(以第二个为例)
转账之后第二个帐户的金额
再来看看第一个账户的金额,还是这样,这就说明我们复现成功了
规避整型溢出:SafeMath库
目前 solidity 还没有解决此问题,所以只能由各个合约自行完成整型溢出的判断
在任何时候,都不要在代码中直接使用 +、-、*、/ 来进行数学运算,而应使用 SafeMath 库
在 SafeMath 库中每个函数开头都用 语句进行了判断,对所有函数都进行了防溢出判断,可以有效地杜绝整型溢出问题
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
赞赏
- 对某款智能手表的分析与攻击 6503
- [原创][车联网安全]使用STM32开发板实战汽车UDS诊断 15016
- [分享]binwalk路径穿越导致RCE(CVE-2022-4510) 9984
- [原创]Hack-A-Sat 2020预选赛 beckley 13586
- [原创]一个BLE智能手环的分析 31555