主页 > imtoken官方最新版 > 【链安全】ATN代币CUSTOM_CALL漏洞

【链安全】ATN代币CUSTOM_CALL漏洞

imtoken官方最新版 2023-08-24 05:11:08

sitemytokencap.com 以太以太坊价格_sitejianshu.com 以太坊以太经典_以太坊漏洞

本文结论:ERC223和ERC827部分实现代码引入任意函数调用缺陷,可能给使用该部分代码的合约带来安全漏洞。 如需实现上述规范接口,请仔细查看实现代码。 这种合约本身允许用户在call()的任意地址上自定义任意函数的设计,这是非常危险的。 攻击者可以很容易地使用当前合约的身份进行任何操作,例如窃取 Token 或绕过权限检查。

影响范围:截至目前,已检测到部署在以太坊上的受影响ERC20合约数量:146个

最新更新:

CUSTOM_CALL 滥用事件的审查和分析

2018年6月20日,AI科技网(ATN)和一家安全公司披露了一起针对ATN智能合约的攻击事件。 2018年5月11日,黑客利用ATN Token合约的漏洞,将自己的地址设置为owner,并增发了1100万个ATN获利。 ATN技术团队很快发现了问题,定位了攻击方式,并完成了对合约的升级修复[1]。 黑客利用ERC223合约可以传递的特性,自定义接听调用函数和ds-auth权限验证。 ERC223合约调用此自定义函数时,合约调用自己的函数,导致内部权限控制失效。

随后,百度安全的《隐形人真的很忙》也在先知安全大会上分享了主题为“以太坊智能合约调用注入攻击”的内容[2]。 该漏洞源于一个普遍的做法:调用一个合约函数后,可以再次调用另一个合约的任意函数,这个任意函数可以由合约调用发起方指定。 然而,ATN的合约漏洞恰恰暴露了这种常见做法非常危险的一面:合约调用者可能利用该功能绕过权限检查,或者以合约身份对其他合约发起攻击等。

有安全风险代码链接:

ATN事件漏洞分析

ERC223 是 Dexaran 于 2017 年 3 月 5 日提出的 Token 标准草案[3],用于完善 ERC20,解决其无法处理发送给合约自身的 Token 问题。 ERC20有两套代币转账机制,一套是直接调用transfer()函数,一套是调用approve() + transferFrom()先授权再转账。 当转账对象为智能合约时,此时必须使用第二种方法,否则转入该合约地址的Token将永远不会再转出。

以下代码是 ERC223 草案中的正确示例。 调用transfer()函数时,合约会判断目标地址是否为合约。 如果是合约,则调用目标合约的tokenFallback()方法,实现合约对转入的Token的处理。 . 此代码不会遭受 CUSTOM_CALL 滥用。

以太坊漏洞_sitemytokencap.com 以太以太坊价格_sitejianshu.com 以太坊以太经典

图片

ERC223合约是ERC20合约的超集,旨在取代ERC20合约,成为新的Token合约标准。 然而,自提出以来一年多时间并未得到广泛接受,只有少数项目采纳了该提案。

以下是错误的ERC223实现代码,不幸被ATN Token采用。 允许用户传入任何自定义_custom_fallback来调用目标_to地址上的任何方法!

以太坊漏洞_sitemytokencap.com 以太以太坊价格_sitejianshu.com 以太坊以太经典

以太坊漏洞_sitemytokencap.com 以太以太坊价格_sitejianshu.com 以太坊以太经典

图片

ATN的漏洞分析报告称,其Token合约参考了ERC223标准[4]的推荐实现。 经过我们的调查,我们发现它确实类似于 Dexaran [5] 维护的 ERC223-token-standard 中 Recommended 分支的 transfer() 方法的实现:

以太坊漏洞_sitemytokencap.com 以太以太坊价格_sitejianshu.com 以太坊以太经典

图片

这其实是非常危险的行为! ConsenSys 维护的《以太坊智能合约-最佳安全开发指南》已经明确建议,应尽可能避免外部调用合约。 但是在这次攻击中,黑客传入的_custom_fallback是setOwner(address),传入的目标地址_to恰好是ATN合约本身,间接调用了ATN的setOwner(address)方法,使得msg.sender变成了ATN Token 合约本身通过了 ds-auth 库的 isAuthorized() 身份验证检查。

EVM 在读取参数时不校验参数个数。 在上面的例子中,黑客调用了setOwner(address)函数,EVM只会读取最左边的_from参数。 因此,在使用底层call()方法传递参数时,如果参数个数与功能要求不一致,则不会报错。 黑客很容易仔细构造所需的攻击参数。

滥用 CUSTOM_CALL 的危险

回到 _custom_fallback 接口的实现。 我们认为,作为通用的Token标准接口设计,设计者必须尽可能考虑整个系统生态的安全性,尽可能避免因使用不当引入的风险。 如果上述_custom_fallback接口设计被广泛采用,以后类似的安全问题会更多。 一个好的界面设计应该是简洁、易用和明确的。 作者提案中的tokenFallback()接口完全可以解决它原本打算解决的ERC20问题。 但是,在实现中引入自定义_custom_fallback,很容易误导开发者而被滥用。

sitemytokencap.com 以太以太坊价格_sitejianshu.com 以太坊以太经典_以太坊漏洞

图片

通常当我们调用ERC20的approve()函数给出智能合约地址时,对方是收不到相关通知无法进行下一步操作的。 通常的做法是使用接收者调用(receiverCall)来解决无法监听的问题。 上面的代码是一个实现方法。 不幸的是,这段代码存在严重的 CUSTOM_CALL 滥用漏洞。 调用approveAndCall()函数后,会执行_spender上的其他用户自定义方法,进行receiver的后续操作。

敲下面的黑板!

【危害】:这种合约本身允许用户在call()的任意地址自定义任意函数的设计,非常危险。 攻击者可以很容易地借用当前合约的身份来执行任何操作。

这通常会导致两种危险的后果:

sitemytokencap.com 以太以太坊价格_以太坊漏洞_sitejianshu.com 以太坊以太经典

后果一示例:假设有缺陷的Token合约A在自己的账户中有各种Token B、C、D等,攻击者只需要设置_spender为他要窃取的目标Token(比如B的地址) ,然后构造它用于调用transfer(address,uint256)的_data作为合约A方便的转移合约A中的各种token。上面代码中_spender != address(this)的验证只能保护A Token。

如果管理各种Token的智能合约也允许自定义call(),那么合约上的各种Token将非常危险。

后果二:比如在ATN安全事件中,黑客也利用该漏洞利用ATN合约的身份绕过了ds-auth的权限控制。

后果3示例:假设有缺陷的Token合约A被用户X授权(Approve)管理10000个Token B,那么黑客也利用该漏洞调用transferFrom()函数窃取Token B。

ERC223提案实现与接口定义不一致

进一步调查后发现,ERC223提案的文本接口描述中并没有提及参数_custom_fallback的引入和使用。

以下是提案指定的接口:

以太坊漏洞_sitemytokencap.com 以太以太坊价格_sitejianshu.com 以太坊以太经典

图片

可以看出_custom_fallback参数没有出现在两个transfer()接口定义中。

我们来看看提案的文字说明:

如果接收方是合约,ERC223 代币合约将尝试调用接收方合约上的 tokenFallback 函数。 如果 receiver contract 上没有 tokenFallback 函数,交易将失败。 tokenFallback 函数类似于以太交易的回退函数。 交易。

本段的中心思想是,如果Token转账的目标对象是ERC223合约以太坊漏洞,则尝试调用其tokenFallback()函数,如果目标对象没有tokenFallback()函数,则让交易失败. tokenFallback() 在这里的作用类似于以太币转账中的默认回退函数。

显然,ERC223提案的初衷很明确,就是约定一个tokenFallback()接口作为处理转账代币的代币合约标准。 ERC223提案主分支的代码实现不存在_custom_fallback的问题。 作者推荐的Recommended分支中的代码增加了引入_custom_fallback的transfer()实现,但是没有风险提示。

以太坊漏洞_sitemytokencap.com 以太以太坊价格_sitejianshu.com 以太坊以太经典

ERC223代码实现的其他问题

其实,ERC223 Recommended分支代码实现还有其他问题。

call()在处理bytes变量时会在evm层面产生bug,导致数据不一致[6]。

sitemytokencap.com 以太以太坊价格_sitejianshu.com 以太坊以太经典_以太坊漏洞

event处理indexed bytes变量,特殊情况下也会报错[7]。

由此可以推断,ERC223的Recomm*分支代码不成熟,不推荐使用。

EVM参数传递机制

下面我们来解释一下EVM中函数调用和参数传递的机制,从而理解这个安全风险。以下面的合约为例

sitemytokencap.com 以太以太坊价格_sitejianshu.com 以太坊以太经典_以太坊漏洞

图片

首先理解EVM传参机制:在调用函数时,如果目标函数有参数,一般情况下我们需要根据ABI指定的参数类型构造输入。比如当transfer(address to, uint256 value )调用transfer(),以太坊使用函数签名的哈希值的前4个字节作为函数选择器,计算sha3(transfer(address, uint256))得到0xA9059CBB,然后连接到Address,256位补全为

sitemytokencap.com 以太以太坊价格_sitejianshu.com 以太坊以太经典_以太坊漏洞

图片

紧接着进行value拼接,256位补全也是

sitemytokencap.com 以太以太坊价格_sitejianshu.com 以太坊以太经典_以太坊漏洞

sitemytokencap.com 以太以太坊价格_sitejianshu.com 以太坊以太经典_以太坊漏洞

图片

最后得到完整的调用数据:

以太坊漏洞_sitemytokencap.com 以太以太坊价格_sitejianshu.com 以太坊以太经典

图片

将这个calldata附加到交易中,发送到目标智能合约地址,实现函数调用。 当以太坊节点收到交易时以太坊漏洞,它会将调用数据和智能合约的字节码加载到 EVM 中。 字节码是在编译时生成的,也就是说参数的处理也在编译时就已经固定下来了。 我们从以太坊黄皮书可以看出:

以太坊漏洞_sitemytokencap.com 以太以太坊价格_sitejianshu.com 以太坊以太经典

图片

现在我们来看一下实际编译的字节码是如何分离transfer、address、value这三个参数的,观察下面的字节码片段:

以太坊漏洞_sitemytokencap.com 以太以太坊价格_sitejianshu.com 以太坊以太经典

图片

我们可以看到,在字节码中,为了动态数组,只会判断calldata是否小于某个最小长度,而不会检查参数是否过长。 编译器会生成一系列带有数学运算的CALLDATALOAD来分隔函数所需的参数。 首先计算目标函数调用:

CALLDATALOAD 指令将交易中的calldata(0xa9059cbb0000000000000000000000003f5ce5fbfe3e9af3971dd833d26ba9b5c936f0be000000000000000000000000000000000000000000000000000000e8d4a51000)加载到栈中,然后使用除法运算,将数据前256位除以0x 100000000000000000000000000000000000000000000000000000000, 得到0xA9059CBB,以此类推,每个参数都会用类似的方法分离出来。 But when参数太多,字节码和EVM不会处理,可以直接忽略,所以这个特性主要来自于编译器。 利用这个特性,黑客可以很容易地为CUSTOM_CALL构造攻击参数。

ERC827代码实现存在安全隐患

以太坊漏洞_sitemytokencap.com 以太以太坊价格_sitejianshu.com 以太坊以太经典

类似的 ERC827 Token 提案也存在同样的问题[8]。 以下代码来自openzeppelin-solidity的ERC827问题代码实现:

以太坊漏洞_sitemytokencap.com 以太以太坊价格_sitejianshu.com 以太坊以太经典

图片

transferAndCall()中的传递函数完成后,代码会调用_to地址上的任意函数,参数由调用者任意指定。 由于这个函数检查了_to != address(this),代码结合ds-auth库后不会产生绕过权限检查的安全漏洞(后果2),但可能会引入前面提到的后果1和后果3,即可以任意控制问题合约拥有或管理的Token(即以该合约为跳板攻击其他合约)。

此外,许多 ERC20 Token 都添加了自定义数据的类似 call() 实现。 这种允许在任何地址对任何函数进行自定义 call() 的设计非常危险。 在特殊情况下,攻击者甚至可以窃取合约中的各种Token,绕过合约本身的权限控制。

ERC20、ERC721中“接收通知调用”的正确代码实现

在正确的代码实现中,“接收通知调用”的处理应该将被通知函数的签名硬编码为固定值,避免被攻击者任意指定的任何可能性。 下面举两个例子来说明通知调用的正确写法:

声明Receiver函数,通过声明的函数调用接收到的通知

例如,在以太坊官网(ethereum.org)维护的ERC20代码中,关于通知调用的代码片段:

以太坊漏洞_sitemytokencap.com 以太以太坊价格_sitejianshu.com 以太坊以太经典

图片

调用通知采用正常的函数调用方式。

通过Receiver函数的签名常量接收通知调用

以下一段正确的实现代码来自Consensys维护的Token-Factory项目

以太坊漏洞_sitemytokencap.com 以太以太坊价格_sitejianshu.com 以太坊以太经典

图片

以下是正确实现“接收通知调用”的代码实现库