以太坊智能合约call注入攻击

编程入门 行业动态 更新时间:2024-10-21 18:43:58

<a href=https://www.elefans.com/category/jswz/34/1768575.html style=以太坊智能合约call注入攻击"/>

以太坊智能合约call注入攻击

这是我在先知安全大会上分享议题中的一部分内容。主要介绍了利用对call调用处理不当,配合一定的应用场景的一种攻击手段。

0x00 基础知识

以太坊中跨合约调用是指的合约调用另外一个合约方法的方式。为了好理解整个调用的过程,我们可以简单将调用发起方合约当做传统web世界的浏览器,被调用的合约看作webserver,而调用的msg则是http数据,EVM底层通过ABI规范来解码参数,获取方法选择器,然后执行对应的合约代码。

 

当然,实际上智能合约的执行一般在打包交易或者验证交易的时候发生,上面的比喻只是方便理解。

在solidity语言中,我们可以通过call方法来实现对某个合约或者本地合约的某个方法进行调用。

调用的方式大致如下:

 

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

如上所述,可以通过传递参数的方式,将方法选择器、参数进行传递,也可以直接传入一个字节数组,当然要自己去构造msg.data的结构。

Solidity编程中,一般跨合约调用执行方都会使用msg.sender全局变量来获取调用方的以太坊地址,从而进行一些逻辑判断等。

比如在ERC20标准中的transfer方法的实现中,就是使用msg.sender来作为扣款方:

function transfer(address _to, uint256_value) returns (bool success) {….balances[msg.sender]-= _value;
balances[_to] += _value;
….
}

0x01 攻击模型

Call方法注入漏洞,顾名思义就是外界可以直接控制合约中的call方法调用的参数,按照注入位置可以分为以下三个场景:

1. 参数列表可控<address>.call(bytes4 selection, arg1, arg2, ...)
2. 方法选择器可控<address>.call(bytes4selection, arg1, arg2, ...)
3. Bytes可控<address>.call(bytesdata)<address>.call(msg.data)

简单举个例子,比如存在一个合约B,代码如下:

 

contract B{function info(bytes data){this.call(data) ;}function secret() public{require(this ==msg.sender);// secret operations}
}

其中有info和secret方法,secret方法中判断必须是合约自身调用才能执行。然而这里的info方法中有个call的调用,并且外界可以直接控制call调用的字节数组,因此如果外界精心构造一个data,这个data的方法选择器指定为secret方法,那么外部用户就可以以合约身份调用到这个secret方法,这样就会造成一定的风险。

0x02 具体场景

这里举两种实际的攻击场景:

(1) bytes注入

 

 

 

在合约代码中,有个approveAndCallcode方法,这个方法中允许调用_spender合约的某些方法或者传递一些数据,通过引入了_spender.call来完成这个功能。

如果外界调用中指定_spender为合约自身的地址,就可以以合约的身份去调用合约中的某些方法。比如如果我们使用合约的身份去调用transfer方法:

 

 

 

只需要自己去构造bytes即可,比如把transfer的_to参数指定为我们自己的账户地址。这样其实就可以直接把合约账户中的代币全部转到自己的账户中,因为通过call注入,在transfer方法看来,msg.sender其实就是合约自己的地址。

(2) 方法选择器注入

比如这里有个logAndCall方法:

function logAndCall(address _to, uint _value, bytes data, string_fallback){…..assert(_to.call(bytes4(keccak256(_fallback)),msg.sender, _value, _data)) ;……
}

这里我们对_fallback参数可控,也就是说我们可以指定调用_to地址的任何方法,但是后面跟了三个参数,分别是msg.sender,_value_data,类型分别为address,uint256以及bytes。那么我们是不是只能调用参数类型必须为这三个的方法呢?当然不是。这里涉及到EVM在处理calldata的一个特性。

 

 

 

比如Sample1合约中有个test方法,这个方法中有三个参数,都是uint256类型的。而Sample2通过call调用了Sample1的test方法,这里传入了5个参数,同样是可以调用成功的。这是因为EVM在获取参数的时候没有参数个数校验的过程,因此取到前三个参数1,2,3之后,就把4,5给截断掉了,在编译和运行阶段都不会报错。

利用这个特性,我们其实有很多攻击面,比如我们可以通过logAndCall中的call注入来调用approve方法:

 

 

 

这里的approve方法有两个参数,而且类型为address和uint256,所以我们是可以调用成功的。这样就可以将合约账户中的代币授权给我们自己的账户了。

0x03 深远的问题

ERC223标准是为了解决ERC20中对智能合约账户进行转币场景缺失的问题,可以看作是ERC20标准的升级版。但是在很多ERC223标准的实现代码中就带入了call注入的问题:

 

此外,很多合约在判断权限的时候会将合约自身的地址也纳入到白名单中:

 

 

0x04 防护手段

针对本文提到的这个风险,作为开发者来说,需要对ERC223的实现进行排查,不要引入call注入问题,如果非要执行回调,则可以指定方法选择器字符串,避免使用直接注入bytes的形式来进行call调用。对于一些敏感操作或者权限判断函数,则不要轻易将合约自身的账户地址作为可信的地址。

更多推荐

以太坊智能合约call注入攻击

本文发布于:2024-02-14 09:28:52,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1762824.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:以太   合约   智能   call

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!