浅析AMR智能合约批量转账溢出漏洞
下面要带大家了解的是浅析AMR智能合约批量转账溢出漏洞,希望能给网友您带来不错的体验。
日前,互联网爆出AMR合约存在高危安全风险的交易,通过研究发现,AMR合约存在批量转账溢出漏洞:当合约实现批量转账功能时,容易在计算通证增加量时发生溢出漏洞,BUGX.IO安全团队经过研究分析发现,同类漏洞仍在以太坊里面部分存在,以下为漏洞分析过程:「原理」/*** @dev Function is used to perform a multi-transfer operation. This could play a significant role in the Ammbr Mesh Routing protocol.** Mechanics:* Sends tokens from Sender to destinations[0..n] the amount tokens[0..n]. Both arrays* must have the same size, and must have a greater-than-zero length. Max array size is 127.** IMPORTANT: ANTIPATTERN* This function performs a loop over arrays. Unless executed in a controlled environment,* it has the potential of failing due to gas running out. This is not dangerous, yet care* must be taken to prevent quality being affected.** @param destinations An array of destinations we would be sending tokens to* @param tokens An array of tokens, sent to destinations (index is used for destination->token match)*/function multiTransfer(address[] destinations, uint[] tokens) public returns (bool success){// Two variables must match in length, and must contain elements// Plus, a maximum of 127 transfers are supportedassert(destinations.length > 0);assert(destinations.length < 128);assert(destinations.length == tokens.length);// Check total requested balanceuint8 i = 0;uint totalTokensToTransfer = 0;for (i = 0; i < destinations.length; i++){assert(tokens[i] > 0);totalTokensToTransfer += tokens[i];}// Do we have enough tokens in hand?assert (balances[msg.sender] > totalTokensToTransfer);// We have enough tokens, execute the transferbalances[msg.sender] = balances[msg.sender].sub(totalTokensToTransfer);for (i = 0; i < destinations.length; i++){// Add the token to the intended destinationbalances[destinations[i]] = balances[destinations[i]].add(tokens[i]);// Call the event...emit Transfer(msg.sender, destinations[i], tokens[i]);}return true;}`totalTokensToTransfer += tokens[i];`这一句溢出,溢出后,totalTokensToTransfer 变小了,从而绕过了`assert (balances[msg.sender] > totalTokensToTransfer);`的判断,这样就能花极少的token , 任意增加目标地址的 token 。
攻击者的攻击行为:https://etherscan.io/tx/0xd4ee42c454941fccb5d03f6155e288f28cc00473ba927ee4b19ad4e2bfc68b68可以看到这两个 tokens 值都是 uint256 最大值的一半,两个加起来刚好溢出变为 0。
「漏洞复现」1.部署AMR 合约2.因为需要攻击者token 数量大于0,所以先使用管理员账户给攻击者地址充 token。
"0x14723a09acff6d2a60dcdf7aa4aff308fddc160c",1使用`balanceOf`可以查看到:0x14723a09acff6d2a60dcdf7aa4aff308fddc160c有balances为13.使用漏洞溢出攻击这里需要两个地址,一个是攻击者,另一个为其它地址,这里设置 0 地址就行。
执行 multiTransfer 就行。
["0x14723a09acff6d2a60dcdf7aa4aff308fddc160c","0x0000000000000000000000000000000000000000"],["57896044618658097711785492504343953926634992332820282019728792003956564819968","57896044618658097711785492504343953926634992332820282019728792003956564819968"]4.查看攻击者余额可以看到攻击者余额已经变得非常大。
「修复方案」1.使用safeMath 即可解决此问题。
2.以太坊的大部分合约是以transfer 的形式进行转账,此方法也可以避免溢出问题。
function multiTransfer(address[] recipients, uint256[] amounts) public {require(recipients.length == amounts.length);for (uint i = 0; i < recipients.length; i++) {transfer(recipients[i], amounts[i]);}}「深入探索」在对以太坊上做进一步探索的时候,我们发现批量转账功能或者批量充值功能的实现主要有以下几种形式:1.合约部署时使用批量充值功能此功能在构造函数中实现,只有部署的时候能够使用,所以不可利用。
/// @title Gnosis token contract/// @author Stefan George -contract GnosisToken is StandardToken {/** Token meta data*/string constant public name = "Gnosis Token";string constant public symbol = "GNO";uint8 constant public decimals = 18;/** Public functions*//// @dev Contract constructor function sets dutch auction contract address and assigns all tokens to dutch auction./// @param dutchAuction Address of dutch auction contract./// @param owners Array of addresses receiving preassigned tokens./// @param tokens Array of preassigned token amounts.function GnosisToken(address dutchAuction, address[] owners, uint[] tokens)public{if (dutchAuction == 0)// Address should not be null.throw;totalSupply = 10000000 * 10**18;balances[dutchAuction] = 9000000 * 10**18;Transfer(0, dutchAuction, balances[dutchAuction]);uint assignedTokens = balances[dutchAuction];for (uint i=0; i<owners.length; i++) {if (owners[i] == 0)// Address should not be null.throw;balances[owners[i]] += tokens[i];Transfer(0, owners[i], tokens[i]);assignedTokens += tokens[i];}if (assignedTokens != totalSupply)throw;}}2.管理者调用批量转账功能function batchTransfer(address[] _to, uint[] _value) checkAccess("currencyOwner) returns (bool) {if (_to.length != _value.length) {Error(7, tx.origin, msg.sender);return false;}uint totalToSend = 0;for (uint8 i = 0; i < _value.length; i++) {totalToSend += _value[i];}ElcoinDb db = _db();if (db.getBalance(msg.sender) < totalToSend) {Error(8, tx.origin, msg.sender);return false;}db.withdraw(msg.sender, totalToSend, 0, 0);for (uint8 j = 0; j < _to.length; j++) {db.deposit(_to[j], _value[j], 0, 0);Transfer(msg.sender, _to[j], _value[j]);}return true;}即使有漏洞,但受到管理者权限控制,所以一般不可利用。
3.公开函数中的批量转账功能function transferMulti(address[] _to, uint256[] _value) public returns (uint256 amount){require(_to.length == _value.length);uint8 len = uint8(_to.length);for(uint8 j; j<len; j++){amount += _value[j];}require(balanceOf[msg.sender] >= amount);for(uint8 i; i<len; i++){address _toI = _to[i];uint256 _valueI = _value[i];balanceOf[_toI] += _valueI;balanceOf[msg.sender] -= _valueI;Transfer(msg.sender, _toI, _valueI);}}我们看到了不少这种形式的写法,通过`amount += _value[j];`的增加,溢出后绕过了`require(balanceOf[msg.sender] >= amount);`的检测。
「漏洞影响范围」研究此漏洞原理后,我们使用自研的审计系统"以太坊冲击波",对以太坊上的合约进行整体监测,发现了以下合约均存在此漏洞。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
关于浅析AMR智能合约批量转账溢出漏洞的信息到此就结束了,如果你还想了解更多这方面的信息,记得收藏关注本站。