之前我们发现了一个Aptos Move VM的严重漏洞,经过深入研究,我们发现了另外一个新的整数溢出漏洞,这次一的漏洞触发过程相对更有趣一点, 下面是对这个漏洞的深入分析过程,里面包含了很多Move语言本身的背景知识.通过本文讲解相信你会对move语言有更深入的理解。
众所周知,Move 语言在执行字节码之前会验证代码单元。验证代码单元的过程,分为4步。这个漏洞就出现在 reference_safety 的步骤中。
如上面代码所示, 此模块定义了用于验证过程主体的引用安全性的转移函数。其检查包括(但不限于)验证没有悬空引用、对可变引用的访问是否安全、对全局存储引用的访问是否安全。
下面是引用安全验证入口函数, 它将调用analyze_function.
在analyze_function中,函数将对每一个基本块进行验证,那么什么是基本块呢?
在代码编译领域,基本块是一个代码序列,除了入口之外没有分支指令,除了出口之外没有分支指令。
在Move 语言中, 基本块是通过遍历字节码、查找所有分支指令以及循环指令序列来确定的。以下是核心代码:
SBI和保诚科技巨头加入Sygnum董事会:金色财经报道,Sygnum董事会增加了两名高管,分别是SBI Digital Asset Holdings首席执行官Fernando Luis Vázquez Cao和塔塔汽车董事会成员、前保诚首席执行长 Al-Noor Ramji*,
Fernando Luis Vázquez Cao目前是SBI数字资产控股公司的首席执行官,该公司是在tse上市的金融集团SBI集团的一部分,在他的职权范围内拥有咨询、代币化和发行、数字交易所和数字托管业务线。
继1月份的9000万美元融资、资产管理规模达到20亿美元、全球客户超过1000家之后,Sygnum正在加快其计划,进一步扩大其安全数字平台,进入新的国际市场,并与战略投资者共同开发解决方案。(finextra)[2022/7/13 2:10:35]
接下来,我们来分享一个move ir代码基本块的例子, 如下所示, 它 3 个基本块。分别由分支指令:BrTrue,Branch,Ret确定。
参考Rust语言的思想,Move 支持两种类型的引用类型。不可变引用&(例如&T)和可变引用&mut(例如&mut T)。你可以使用不可变 (&) 引用从结构中读取数据,使用可变 (&mut)引用 修改它们。通过使用恰当的引用类型,有助于维护安全性以及识别读取模块。这样可以让读者清晰地知道此方法是更改值还是仅读取。 下面是官方Move教程中的示例:
新加坡加密交易所Coinhako获SBI、Sygnum、Azimut投资:12月7日消息, 根据今天发布的一份声明,总部位于新加坡的加密货币交易平台Coinhako获得了SBI-Sygnum-Azimut Digital Asset Opportunity基金的支持,这轮融资获得了3倍超额认购。该基金是一个风险投资基金,由日本SBI集团、瑞士数字资产银行Sygnum Bank和意大利Azimut(欧洲主要独立资产管理公司)共同管理。此次投资的具体金额未予披露。Coinhako的声明称,该公司将利用新资金向东南亚地区以及机构和高净值客户细分市场扩张。[2021/12/7 12:56:53]
在示例中,我们可以看到mut_ref_t是 t 的可变引用。
所以在Move 引用安全模块中,尝试通过以函数为单元,扫描函数中的基本块中的字节码指令验证判断所有引用操作是否合法。
下图显示了验证引用安全性的主要流程。
这里的state是AbstractState结构体, 它包含了borrow graph 和locals ,他们共同用于确保引用函数中的引用安全性。
动态 | 瑞士加密银行Sygnum获得新加坡资本市场服务许可证:瑞士加密银行Sygnum在新加坡获得资本市场服务许可证。周四,新加坡金融管理局(MAS)允许Sygnum开始运营。该银行表示,其首个新加坡产品将是一只多管理人基金。据悉,Sygnum与竞争对手SEBA同时在八月份获得了瑞士银行业牌照。SEBA表示,它正在与MAS进行谈判,但尚未正式申请许可证。表示,它正专注于从下个月开始在瑞士进行全面的现场发布,然后再转移到其他市场。(Swissinfo)[2019/10/31]
这里borrow graph是用来表示局部变量引用之间关系的图。
从上图中可以看到, 这里有一个pre state ,其包含locals 和borrow graph (L ,BG)。然后执行了basic block 生成一个post state (L’, BG’)。然后将前后的state合并以更新块状态并将该块的后置条件传播到后续块。这就像 V8 turbofan中的Sea of Nodes 思想。
下面的代码是上图对应的主循环。首先, 执行块代码(如果执行指令不成功,将返回 AnalysisError)然后尝试通过join_result是否更改,来合并pre state和post state。如果更改并且当前块本身包含一个后向的边指向自己(这意味着有一个循环)将跳回到循环的开头, 在下一轮循环仍将执行此基本块,直到post state等于pre state或因某些错误而中止。
动态 | 瑞银前首席执行官担任加密银行Sygnum董事:瑞银(UBS)前首席执行官Peter Wuffli表示,他看到了加密银行业的巨大潜力。Wuffli目前已成为瑞士加密银行Sygnum的董事,该加密银行已从瑞士金融市场监管局(FINMA)获得了银行牌照。(bitconist)[2019/9/27]
因此在引用安全模块,如何判断 join的结果是否改变?
通过上面的代码,我们可以通过判断locals和borrow关系是否发生变化来判断join结果是否发生变化。 这里的 join_ 函数用于更新本地变量和 borrow关系图 。
下面是join_ 函数代码,第 6 行是初始化一个新的 locals Map 对象。 第 9 行迭代 locals 中的所有索引,如果pre state与 post state都值为 None,则不要插入到新的 locals 映射中,如果 pre state 有值,post state为None,则需要释放 brow_graph id ,意味着这里消除该值的借用关系, 反之亦然。特别的,当pre state与 post state 两个值都存在且相同时,像第30-33行一样将它们插入到新的map中,然后在第38行合并 borrow graph。
通过上面代码,我们可以看到 self.iter_locals() 是locals变量的个数。 请注意,此局部变量不仅包括函数的真实局部变量,还包括参数。
动态 | 加密货币公司Sygnum相关负责人称赞瑞士颁发银行牌照是游戏改革者:Sygnum加密银行联合创始人称赞瑞士银行牌照的颁发是游戏改革者,可能会打开将加密货币和其他数字资产整合到既定金融部门的闸门。周一,Sygnum和SEBA获得瑞士金融监管机构颁发的临时银行和证券交易商牌照。一旦两个实体完成了一些最终常规监管障碍,它们将成为成熟的银行。 Sygnum瑞士公司首席执行官Manuel Krieger表示,“这是第一次在全球范围内授予此类牌照,所以瑞士正在发挥先锋作用。”(Zerohedge)[2019/8/29]
在这里我们已经覆盖了所有与漏洞相关的代码,你找到漏洞了吗?
如果你没有发现漏洞也没关系,下面我会详细说明漏洞触发过程。
首先在下面的代码中,如果参数长度添加局部长度大于 256。这似乎没有问题?
当函数 join_() 中是 function_view.parameters().len() 和 function_view.locals().len() 组合值大于 256,由于语句 for local in self.iter_locals()中 local 是 u8类型,此时执行此语句对造成溢出。
开发者似乎只检查了 Move Modules代码中的 locals+parameter 长度,而忽略了script。
通过上面的介绍,我们知道有一个主循环来扫描代码块,然后调用execute_block函数,之后会合并执行前后的state,当move代码中存在循环,则会跳转到代码块开始,再次执行基本块。 因此,如果我们制造一个循环代码块并利用溢出改变块的state,使 AbstractState 对象中的新的locals map与之前不同,当再次执行 execute_block 函数时,在分析basic block中字节码指令序列的时候会访问新的locals map,这时候如果指令中需要访问的索引在新的AbstractState locals map中不存在,将导致DoS。
在审核代码后,我发现在 reference safety模块中,MoveLoc/CopyLoc/FreeRef 操作码,我们可以实现这个目标。
这里让我们看一下文件路径中execute_block函数调用的copy_loc函数作为一个说明:
move/language/move-bytecode-verifier/src/reference_safety/abstract_state.rs
在第287行,代码尝试通过LocalIndex作为参数获取本地值,如果LocalIndex不存在会导致panic,想象一下当节点执行满足上述条件代码的时候,会导致整个节点崩溃。
下面是你可以在git里面重现的PoC:
commit:add615b64390ea36e377e2a575f8cb91c9466844
thread 'regression_tests::reference_analysis::PoC' panicked at 'called `Option::unwrap()` on a `None` value', language/move-bytecode-verifier/src/reference_safety/abstract_state.rs:287:39 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
我们可以看到PoC中代码块存在一个basic block,分支指令是一个无条件分支指令,每次执行最后一条指令时,branch(0) 将跳回第一条指令,因此这个代码块将多次调用 execute_block 和 join 函数。
1.在第一次执行完 execute_block 函数,当这里设置 parameters 为SignatureIndex(0),locals为SignatureIndex(0)会导致num_locals为132*2=264。 所以在执行join_函数下面这行代码之后
将会导致新的locals map长度为8.
2.在第二次执行execute_block函数时,执行move代码第一条指令copyloc(57),57是locals需要压入栈的offset,但是这次locals只有长度8,offset 57不存在,所以会导致 get(57).unwrap() 函数返回 none ,最后导致panic。
以上就是这个漏洞的来龙去脉。首先这个漏洞说明没有绝对安全的代码, Move语言在代码执行之前确实做了很好的静态校验,但是就像这个漏洞一样,可以通过溢出漏洞完全绕过之前的边界校验。再者代码审计很重要,程序员难免会疏忽。作为Move语言安全研究的领导者,我们将继续深挖Move的安全问题。第三点,对于Move语言,我们建议语言设计者在move运行时增加更多的检查代码,以防止意外情况的发生。目前move语言主要是在verify阶段进行一系列的安全检查,但我觉得这还不够,一旦验证被绕过,运行阶段没有过多的安全加固,将导致危害进一步加深,引发更严重的问题。最后,我们还发现了Move语言的另一个漏洞,后续会继续分享给大家。
参考资料
https://github.com/move-language/move
https://github.com/MystenLabs/awesome-move
https://move-language.github.io/move/
Numen Cyber
企业专栏
阅读更多
金色早8点
金色财经
去中心化金融社区
CertiK中文社区
虎嗅科技
区块律动BlockBeats
念青
深潮TechFlow
Odaily星球日报
腾讯研究院
来源:/img/2023525181643/0.jpg" />V神:想要参与ETH2.0质押可随时进行存款:刚刚,针对推特网友询问“从现在起,还想要参与ETH2.0质押的人会怎样”,V神回复称,可以随时进行存款.
1900/1/1 0:00:00编辑|Skye 在大家一股脑儿地冲向NFT爆发的时候,我们看到大量无用的代币最后都走向了项目方跑路的结局,倒也不是都割了一大把韭菜,很多只是因为没有目标或与实际价值挂钩导致实在推进不下去了.
1900/1/1 0:00:005月18日,Game Dosi正式发布,并同步开售会员服务,包括黄金和白金级NFT通行证,这些通行证可以提供提前体验新游戏等福利.
1900/1/1 0:00:0011月15日,百度旗下DuDu Lab发行的DuDu Bear NFT已完成铸造,NFT搭建于以太坊上,铸造价格为0.02ETH,发售价格为0.05ETH,共发行1万个,总价值约为455万人民币,目前已售罄.
1900/1/1 0:00:00近日,英伟达(NVIDIA)交出了一份并不太亮眼的 2022 财年第三财季报告,在连续两个季度的下滑之后,英伟达的三季度依旧不太乐观,单纯显卡业务的收入,同比和环比均出现两位数下滑.
1900/1/1 0:00:00原文作者:Lao Bai今天来说说 Layer2 的 OP 与 ZK 之争问题 短期 OP,长期 ZK? 这句话因为 V 神说过,所以被很多人认为是「金科玉律」,然而实际情况要复杂的多.
1900/1/1 0:00:00