Elysium: Context-Aware Bytecode-Level Patching to Automatically Heal Vulnerable Smart Contracts
本文由ETH Zurich的Postdoc Fellow,Christof Ferreira Torres发表于RAID 22’。Dr. Torres在博士期间专注区块链研究,在智能合约安全 DeFi等领域发表过多篇顶级会议论文。
Intro
截至2022年三月,Ethereum上已提供源码的合约大约占152,996/49,183,523。而由于智能合约之间可以互相调用,所以闭源合约也可能会影响开源合约。
智能合约安全领域已经有大量的工作,包括使用可升级的模式来构建合约,设计专门检测和拦截有害交易的合约,也有在部署前自动检查合约漏洞的工作,模型验证,模糊测试等等等。
不过尽管工作有很多,很多已经被研究过的问题依然在高价值合约中出现,因此合约的自动检查和自动修补依然是非常重要的方向。
现有合约修补工作的主要问题:
1.只支持几种缺陷。2.用了硬编码的模板。3.增加了额外的部署和运行时开销。
本文提出了一种方法可以自动生成上下文相关的patch。首先对每一个合约进行分析,包括整数类型推断,空闲储存空间推断,来足够理解智能合约的上下文,进而可以定制高效的patch。分析和patching均在字节码层面,字节码重写通常比源代码重写更高效一些,能让codesize和gas消耗都更低一些。
本文使用了混合的方法来平衡模板方法的可用性和语义方法的有效性。
模板方法只是在固定指令序列中插入补丁,而不会考虑整个程序的语义,语义方法会在保留原语义的情况下修改程序。
合约的开发者可以重复使用现有的补丁模板或写新模板,而不必关注空闲状态变量,整数类型等。
本文的模板包含了占位符,可以自动地替换成和合约相关的信息,而且由本文方法可以很容易地和新工具兼容。
Methodology
Fig2 中展示了一个重入攻击的例子,a代码的5行可以被重复调用,而用户的余额此时还未更新。patch增加了锁变量,使得b代码中第八行处不能多次被调用。
Fig3 展示了一个tx.origin错误使用的例子,这里应该检查函数调用的直接发送者sender,而不是整个交易的发起者origin。修复为替换指令。
Fig4 是一个自我析构合约,但是kill函数没有only owner的保证,典型的访问权限问题。这里的修复即增加对sender的检查。
Fig5 展示了一个整数溢出问题,程序逻辑为 把uint32 amount加在余额tokens数组的某一项上。修复为增加bounds的检查。
Fig6 是一个未处理的异常,在a代码第七行没有检查send的返回情况,就把claimed置为true。修补为增加require对send返回值的检查。
Design and Implementation
主要系统设计为以下四个步骤:
bug localization, context inference, patch generation, bytecode rewriting
•缺陷定位bug localization
这一步中,作者没有实现自己的漏洞检测工具,而是使用了现有的检测方案并结合到了Elysium中,具体使用以下三个工具:整数:Osiris;重入:Oyente;其他缺陷:Mythril。
•上下文推断context inference
这里包括整数类型推断和空闲储存空间推断。
整数类型包括字长和符号。要正确地处理整数patch需要源代码信息,而这些直接信息在编译后会丢失,但是可以通过字节码的行为来推断这些值的大小和是否有符号。比如从bitmask 0xFFFFFFFF就可获知,上一个压入栈的是32-bit的无符号数。如果上一个压入栈的是有符号数,那么编译器会引入一条SIGNEXTEND指令来补全2的补码。
空闲储存推断主要用于需要增加储存变量时。状态值储存在256-bit的连续编址空间中,其中大部分简单类型都是从0地址开始连续分布的,除了映射和动态数组,动态数组是用hash值来索引。同时,如果有多个连续的小于256bit的元素会被打包存储在一块内。这些写入和读出都可以通过SSTORE和SLOAD指令和上下文来推断。
•补丁生成patch generation
为了支持多种漏洞的修补,作者构造了一种DSL来描述和生成补丁,具体结构为:
将删除的序列;需插入的序列;插入模式和构造器标志。
插入模式决定了指令应该放在bug位置前或后,构造器标志指定了删除和插入是否发生在部署代码中。
DSL的设计中包含了EVM字节码的助记符和几个自定义的关键词:
free_storage_location
integer_bounds
PUSH_jump_loc_{x}
JUMPDEST_jump_loc_{x}
这些新增的关键词可以获取当前空闲储存位置;获取bug位置的整数类型;在模板内部跳转。
•二进制重写bytecode rewriting
本文使用了shadow address的方法。
首先复制一份所有指令在CFG中的原有地址。然后,扫描整个CFG找到和bug相关的地址。之后,修改基本块。完成后,更新比bug地址更大的shadow address。最后修改jump指令和对应JUMPDEST的关联。
Evaluation
Setup
所有对比的项目:
使用的CVE:
Results
RQ1 Effectiveness
RQ2 Correctness
正确性主要依赖于CFG还原和推断的准确性。
CFG还原:作者使用了Etherscan上的源代码数据集,EVM CFG Builder总体上恢复成功率96%,作者修改后成功率提升到了98%。
储存推断:这里对比了Solidity compiler的储存空间分配来推断准确性。实验中使用的100个合约都推断成功了。
RQ3 Costs
这里分析两部分cost:deployment and transaction
原文始发于微信公众号(COMPASS Lab):【论文分享】智能合约修补:Elysium