这几天,有两个朋友问到我“Cannot charge RAM to other accounts during notify”这个错误提示,我才想起原来曾经还存在过这个问题,也许是前段时间的Bancor和“假EOS”漏洞等事件影响力太大,导致很多开发人员忽略了这个RAM更新。于是,我翻了翻笔记,重新整理了下跟大家一起分享,也算是做个记录。
合约吞噬用户RAM原理分析
这个bug的核心是require_receipt函数。目前eosio.token智能合约的转账函数transfer会调用require_receipt(to), 当to账号是一个智能合约时就会调用to的智能合约transfer方法。也就是说用户在调用系统合约(安全的代码)转账EOS的时候竟然可能会执行另外一段代码(可能是恶意代码),这段恶意代码还能得到from用户的授权,这可太危险了。这里有两个风险。
1)如果恶意代码再调用其他合约,比如执行inline action 转账EOS给其他账号,具体金额恶意代码可以任意填写。当然,事实上这个风险肯定是已经被堵上,这个就是大名鼎鼎的eosio.code权限。恶意代码要以from的授权再执行eosio.token这个其他合约,必须得到from账号的eosio.code权限。
2)恶意代码没法调用其他合约,那就只能在自己的合约里干事。但是在自己合约能干的有价值的事就只剩下ram,使用from的ram在本合约table创建大量数据。
哪些用户容易中招
到了这里,你可能会说,恰好转账到恶意合约账号的机会还是很少的。实事上确实如此,但是有些群体是很容易中招的,比如交易所和空投方。
交易所:
恶意合约用户可以主动从交易所提取EOS到恶意合约账号,这实际上会发起一个EOS转账,自然就会让交易所账号执行恶意代码,然后恶意代码消耗多少RAM就看交易所账号的RAM量了。由于一次交易的执行时间是有限的,因而一次转账消耗的RAM也是有限的,但是由于交易所的EOS转出操作零手续费或者很低的手续费,作恶者就可以重复执行转出操作来消耗大量RAM,所以交易所是中招大户。
空投方:
由于代币空投方一般会空投代币到所有账号,或者活跃账号,这样空投账号就很容易转账到恶意合约账号,自然也就中招了,然后还浑然不觉。
Bug修复方法
很显而易见的方案就是不允许在require_receipt引发的合约代码中消耗其他账号的RAM,具体代码如下:
有了这个补丁后,如果再偷RAM就会如下的错误。
正规智能合约如何适配这个改动
很多游戏或者代币交换合约都是通过require_receipt来监控EOS转账行为然后修改合约里的table数据做记录或者换币, 也就说是这一过程消耗RAM产品正常的需求。那上面改动后,这个消耗RAM的问题怎么解决呢?智能合约买单, 即将消耗RAM的payer从用户改为智能合约,比如下图
如果是一个EOS兑换代币的合约,最后一般是调用add_balance分发代币:
漏洞及Require_Receipt实操源码
https://github.com/itleaks/eos-contract/tree/master/stealram-exp
---------------------
【转载】
作者:ITleaks
原文:https://blog.csdn.net/ITleaks/article/details/82876742
|
|