在之前的文章中,讨论了零知识证明的先进形式验证,即如何验证一条ZK指令。形式验证每条zkWasm指令,能验证整个zkWasm电路技术的安全性和正确性。在审计和验证过程中,发现了缓冲区,例如Load8数据注入漏洞和伪造返回漏洞。这些漏洞可能导致黑客操纵ZK证明,创建虚假交易,窃取资产。建议检查代码设计,进行审计和形式化验证,确保电路和智能合约的安全性。对ZK应用的智能合约也需要重视审计和形式化验证。形式化验证对ZK系统的安全性和可靠性起着重要作用。
在之前的文章中,我们谈到了零知识证明的先进形式验证:如何验证一条ZK指令。通过形式验证每条zkWasm指令,我们能够完全验证整个zkWasm电路技术的安全性和正确性。我们将专注于发现缓冲区的视角,分析在审计和验证过程中发现具体的缓冲区,以及从中得到的经验和教训。例如要了解有关零知识证明(ZKP)区块链的先进形式验证的一般讨论,请参见零知识证明区块链的先进形式验证一文。
在讨论ZK内存之前,让我们先了解CertiK是如何进行ZK形式化验证的。对于像ZK虚拟机(zkVM)这样的复杂系统,形式化验证(FV)的1是明确需要验证的内容及其性质。这需要对ZK系统的设计、代码实现和测试设置进行全面的审核。流程与常规的审计有所重合,但不同之处在于,后需要确立验证目标和性质。在CertiK,我们称其为面向验证的审计。审计和验证工作通常是一个整体。对于zkWasm,我们同时进行了审计和形式化验证。
什么是ZK钱包?
正确答案:ZK漏洞证明系统的核心特征在于允许将离线或私密执行的计算(例如区块链交易)的加密货币证明提交的基础知识证明验证器,并由其检查和确认,以完成该计算确认要求。就此而言,ZK漏洞将使得黑客可以提交用于证明虚假交易的ZK证明,并让ZK证明检查器接受。
对于zkVM的证明器而言,ZK证明过程涉及运行程序、生成每一步的执行记录,并把执行记录数据转换成一组数字表格(该过程称为“算术化”)。这些数字之间必须满足一组约束(即“电路”),包括了具体表单元格之间的联系方程、固定的常数、表间的数据库查找约束,以及每对相邻表间所需要满足的多项式方程(亦即“门”)。链上验证可以确认确实存在某项能满足所有约束的表格,同时保证不会看到表中的具体数字。
zkWasm算术化表
每一个财务部门的准确性都至关重要。任何财务部门的错误都可能影响到它,但也有可能它无法提交一个正式的财务表格,这些表格往往不是由传统的VM决定的,而是ZKVM的一次运行,但实际上并非如此。与传统的VM相比,ZKVM的不公开性使这些漏洞得以公开记录在区块链上;而ZKVM则不管理细节于链上。由于ZKVM的某些功能尚未被公开,因此ZKVM可能无法在特定时间范围内成功实现特定目标。
对于zkWasm来说,其ZK电路的实现涉及超过6,000行的Rust代码和第一步的约束。这种复杂性通常意味着可能存在多个漏洞正等待着被发现。
zkWasm电路架构
的确,我们通过对于zkWasm审计和形式化验证发现了多个这样的漏洞。下面,我们将讨论两个具有代表性的例子,并讨论它们之间的差异。
代码漏洞:Load8数据注入攻击
第一个缓冲区涉及zkWasm的Load8指令。在zkWasm中,堆内存的读取是通过一组LoadN指令来完成的,其中N是要加载数据的大小。例如,Load64应该从zkWasm内存地址读出64位数据。Load8应该从内存中读出8位数据(即一个字节),并用0前缀填充以创建一个64位的值。zkWasm内部将内存表示为64位字节的数组,因此其需要“选取”内存数组的一部分。为此使用了四个中间变量(u16_cells),这些变量合起来构成了完整的64位加载值。
这些LoadN指令的约束定义如下:
这个约束分为Load32、Load16和Load8这三种情况。Load64没有任何约束,因为内存单元正好就是64位的。对于Load32情况,代码确保内存单元中的高4个字节(32位)必须为零。
对于Load16情况,内存单元中的高6个字节(48位)必须为零。
对于Load8情况,应该要求内存单元中的高7个字节(56位)为零。 糟糕的是,在代码中并非如此。
正如你所见,只有高9至16位被限制为零。 其他48位高的人都可以任意值,却依然伪装成“从内存中读取的”。
通过利用这个漏洞,黑客可以篡改一个合法执行序列的ZK证明,使Load8指令的运行加载这些意外的字节,从而导致数据损坏。并且,通过精心安排周边代码和数据,启动欺诈的运行和转账,从而窃取数据和资产。这种伪造的交易可以通过zkWasm检查器的检查,并被区块链错误地认定为真实交易。
修复这个漏洞实际上相当简单。
该存储库代表一种类,即“代码存储库”的ZK存储库,它可以编写代码,并可以通过低成本本地代码修改轻松修复。正如你所同意的,这些存储库也相对容易被人看。
技术栈:伪造攻击
: zkWasm在调用和返回的过程中跟踪的动态数据被称为“调用帧”。由于zkWasm按顺序执行指令,所有调用帧都会根据其在运行过程中的发生时间进行排序。下面是一个在zkWasm上运行的调用/返回代码示例。
我们通过调用buy_token()函数来购买代币(可能是通过支付或转移其他货币)。它的核心步骤之一是通过调用add_token()函数,实际将代币余额增加1。由于ZK证明器本身并不支持调用数据结构,因此需要使用执行表(E-Table)和跳表(J-Table)来记录和追踪这些调用高峰时段的谈话记录。
上K线走势图明了尝试buy_token()调用add_token()的运行过程,以及从add_token()返回到buy_token()的过程。可见,代币余额增加了1。在执行表中,每个运行步骤占一行,包括当前执行中的调用帧编号、当前上下文函数名称(用于此处的说明)。该函数内当前运行指令的编号,以及表中所存的当前指令(用于此处的说明)。在跳转表中,每个调用帧占一行,表中有其调用者帧的编号、调用者函数上下文名称(用于此处的说明)。在这两个表中,一个jops表,它追踪当前指令是否为调用/返回(在执行表)以及该帧(在跳转表)
正如人们所预期的,每次调用都应该有一次相应的返回,并且每一帧都应该一次调用和一次返回。如上图所示,跳转表中第1帧的任务值为2,与执行表中第1行和第3行相对应,任务值为1。目前看起来一切正常。
但实际上这里有一个问题:尽管一次调用和一次返回的jopscounty为2,但两次返回都无效,这表明该黑客确实试图通过这种手段达到预期的效果。
你现在可能有点兴奋,你还找不到问题吗?
数据显示,两次调用并不是问题,因为表和调用的有效性使得两个调用无法被编码到同一个执行中,因为每次调用都会产生一个新的问题,即当前调用过程加1。
(2)点击进入专题:聚焦新型冠状病毒肺炎疫情全球多国爆发新冠肺炎疫情。
黑客在最初的调用表中记录了每一次调用和返回值,并在我的调用表中增加了一个新的调用行(最初的调用表和后续指令的运行步骤编号在调用表中则需要加4)。由于调用表中的每一行都记录了2,因此满足了约束条件,zkWasm证明检查器将接受这个伪造的执行序列的“证明”。从图中我们可以看到,代币余额减少了3次而不是1次。因此,黑客能够以支付1个代币价格获得3个代币。
议会有几种方法。一个明显可行的方法就是追踪和返回结果,并确定每个结果的正确性。
你可能已经注意到,其中我们尚未展示这个漏洞的哪怕一项代码。 基本的原理任何一项代码都有问题的,代码实现完全符合表格和约束设计。 问题在于设计本身,而修复方法也是如此。
你可能认为,这个漏洞应该是显而易见的,但实际上并非如此。这是因为“调用或返回也会导致工作计数为2”与“实际上返回是可能的”除外空白。此类漏洞需要对表和调用表中相关的约束进行详细、完整地分析,很难进行完整的非形式化推理。
两个漏洞的比较
对于“Load8数据注入漏洞”和“伪造返回漏洞”,它们都可能导致黑客能够操纵ZK证明、创建虚假交易、过证明检查器,并进行窃取或劫持。其性质和被发现的方式却截然不同。
“Load8数据注入内存”是在对zkWasm进行审计时发现的。这绝非易事,因为我们要超过6,000行的Rust代码和上百条zkWasm指令的初始步骤。尽管如此,这个内存仍然相对容易发现和确认。由于这个内存在形式化验证开始之前就已被修复,所以在验证过程中并未遇到它。如果在审计过程中未发现该内存,我们可以预期在对Load8指令的验证中会发现它。
“伪造返回漏洞”是在审计之后的形式化验证中发现的。我们在审计中发现它的部分原因在于,zkWasm中有一个非常相似的机制叫做“mops”,它在zkWasm运行期间追踪每个内存单元历史数据对应的内存访问指令。mops的计数约束确实是正确的,因为它只追踪每个内存单元历史数据都是不可变的,并且只会写入一次(mops计数为1)。但即使我们在审计期间注意到这个重要的漏洞,但如果我们没有相应的约束进行严格的形式化推理,我们仍然无法轻易地确认或排除每种可能的情况,因为实际上任何一行代码都是错误的。
总结来说,无需担心“代码漏洞”和“设计漏洞”,只需轻点鼠标即可清除缓存,清除缓存后可查看缓存内容(错误代码),并确认缓存内容是否正确;如果正确,则删除缓存,否则将无法查看缓存内容。
发现ZK内存最佳实践
根据审计和验证zkVM 除ZKZK链经验,如何保护ZK系统的一些建议:
检查代码和设计
如前所述,ZK代码和设计中都可能存在漏洞。如果不启动漏洞,可能会导致ZK系统受到破坏,因此须在运行之前消除它们。与非ZK系统相比,ZK系统的一个问题,任何攻击都更难揭露和分析,因为其计算细节没有公开或保留在链上。因此人们可能知道发生了黑客攻击,但却无法知道技术上的数据。因此,他们不得不预先确保ZK系统安全性考虑也非常高。
进行审计以及形式化验证
有人可能会认为,使用了形式化验证就不需要审计,因为所有的漏洞都会被形式化验证发现。实际上我们的建议是进行审计。
如果要对一个ZK系统既进行审计又进行形式化验证,那么最初确定时机就是这两项工作同时进行,以便审计师和形式化验证工程师能够高效地协助(可能会发现更多的漏洞,因为形式化验证意图和目标需要高层审计输入)。
如果你的ZK项目已经进行了审计(赞)或多次审计(大赞),我们的建议是实施审计结果,对逻辑进行形式化验证。在zkVM除ZK非ZK项目的审计和形式化验证的经验一再表明,验证往往能捕捉到审计中遗漏而不易发现的缓存。由于ZKP的特性,虽然ZKP应该提供比非ZK解决方案更好的区块链安全性和可扩展性,但其自身的安全性和正当性要高于传统的非ZK系统。因此,对ZKP进行高质量形式化验证也优于非ZK系统。
确保电路以及智能合约的安全
ZK应用通常包含电路和智能合约两个部分。对于基于zkVM的应用程序,有通用的zkVM电路和智能合约应用。对于非基于zkVM的应用程序,有应用特定的ZK电路及相应部署在L1链或桥的另一端的智能合约。
中控应用 | 電路 | 智能合约 |
基于zkVM | 虚拟机 | 应用 |
非基于zkVM | 应用特定 | L1链/桥 |
我们从ZK应用的整体安全性角度来看,对我们的智能合约进行审计和形式化验证也非常重要。毕竟,在确保电路安全方面投入了大量的精力之后,如果在智能合约方面放松警惕,导致应用最终受到损害,那将会非常有趣。
有两种类型的智能合约值得特别关注。一种是ZK证明的智能合约。尽管它们在很大程度上是同类合约,但其中最重要的是它在主流智能合约之上。第二种是ZKVM,它们之间的竞争非常复杂,其中最重要的就是可靠性和可重复性,特别是因为人们在他们之上看到了很多细节。重要的是,在经过多年的发展,智能合约的形式已经可以应用,并且为合适的高价值目标做好充分的准备。
请通过以下说明来总结形式化验证(FV)对ZK系统及其组件的影响。
资讯来源:由a0资讯编译自THECOINREPUBLIC。版权归作者A0资讯所有,未经许可,不得转载