非首发-Whitechain 跨链桥漏洞挖掘复盘:从源码分析到 PoC 复现,以及被判定 N/A 的反思
一句话结论:一次白帽跨链桥审计里,源码分析确凿地揪出了两个真实逻辑缺陷——提现路径缺少重放防护、以及把字节串地址强制截断成较短整数导致资金被永久锁进零地址;本地也完整复现并通过。但提交被判「不适用」并扣分,原因不在技术对错,而在赏金边界:触发这两个缺陷的提现函数带着中继器角色权限锁,属于「特权信任区」、外部普通攻击者无法直接触达,按规则归入范围之外。这篇复盘真正的价值,是「代码缺陷」与「有效漏洞」之间的这条界线,以及跨链桥应当怎么补。本文保留缺陷成因与审计方法论,不复述可直接照抄的攻击脚本。
排版说明:正文按「定调 / 两个缺陷成因 / 为何判不适用 / 工程化沉淀 / 心态与教训 / 防御视角」组织;引号统一用直角引号,合约标识符、类型名与角色名(bytes32、uint160、onlyRole、ChainId)按原样书写、不含转义符,攻击载荷只描述其原理类别、不给成品代码。
一、定调:绿色的两条通过,和一个「不适用」
本地复现跑通、测试全绿之后,很容易把它当成一次丰收。随后的「不适用」判定与扣分,恰好逼出一个更值得记的问题:一个确实存在、也确实能被复现的缺陷,为什么不算「有效漏洞」?答案分两层——缺陷是真的,但它落在赏金规则划定的信任边界之内。把这两层分开,是这次复盘的核心。
二、缺陷成因一:提现路径缺少重放防护
跨链桥通常成对出现两个入口:一个把资产锁定、发起跨链意图(存款侧),一个在收到源链的签名确认后于目标链释放资产(提现侧)。这次审计发现,存款函数用了一个「已用哈希」集合来防止同一笔跨链意图被二次兑换,而与之对称的提现函数完全没有这道防护。
这是一类典型缺陷:安全防护只做了「一半对称」。凡是「凭一个可重放的凭证(哈希、签名、消息)即可领取资产」的路径,都必须回答同一个问题——这个凭证被用过之后,还能不能再被用一次?存款侧想到了,提现侧漏了,就意味着同一笔释放指令理论上可被重复执行。它属于「缺少一次性消费校验」这一族,与协议消息是否跨链无关,是任何幂等敏感的资金出口都该守的通用不变量。
三、缺陷成因二:地址类型强制截断,资金锁进零地址
第二个缺陷是类型处理不当。提现逻辑把一个三十二字节的地址字段强制转换成一百六十位的整数——这本身是常见的「字节串到定长地址」转换,但危险在于它不加校验。攻击者可以手工构造一个「高位有值、低位全零」的异常字节串:它既能通过合约入口那句「非零即可」的前置检查,又会在截断到一百六十位时低位被丢弃、结果恰好落到全零地址。资产于是被打进零地址、永久锁定、不可找回。
这里的教训不是「别做类型转换」,而是「转换之后必须做完整性校验」:截断是否可能丢失信息、一个非零输入是否可能截断成零、地址是否落在不可用地址空间。把「非零」当作「有效」是脆弱的——校验的应当是转换后的完整语义,而不是转换前的粗略存在性。
四、为什么被判「不适用」:特权信任区与外部攻击面
判定不是「无效」,而是「不适用」,根因在赏金规则本身。在 Web3 审计的常见规则里,带权限修饰(如仅限中继器角色、仅限管理员)的函数属于「特权信任区」:即使其内部逻辑确有校验缺失,只要外部普通攻击者无法直接触达、必须借由特权账号才能触发,就统一归入范围之外——「特权账号被攻破」被当作假设前提之外的事件,不作为有效漏洞计。
这次两个缺陷所在的提现函数恰好挂着中继器角色锁,于是尽管缺陷真实、复现通过,仍被认定偏离了奖励规则。这不是审核在否定代码逻辑,而是在执行「攻击面必须外部可达」这条边界。对审计者而言,它划出一条清晰的资源分配线:判断一个缺陷「值不值得报」,第一步不是看它能否被触发,而是看谁能触发它——是任意外部账户,还是只有被信任的角色。
五、工程化沉淀:本地复现要过的三道坎(讲原理、不给成品)
把两个缺陷在本地跑通,本身是一次有价值的工程训练,值得沉淀的是三类通用能力,而非具体载荷。其一,类型与编码对齐:跨链结构体里地址、哈希等字段在合约侧是定长字节串,客户端必须按 ABI 编码规则补齐到合约要求的宽度,否则调用在编码阶段就失败。其二,构造「能通过前置检查、却暴露内部缺陷」的边界输入:其价值在于用最小反例证明「校验粒度不对」这一判断,本质是边界值测试在链上环境的应用。其三,避免硬编码、按运行环境自适应:链标识要动态获取,以保证脚本在不同分叉或测试网络上都能可复现地跑通。三点合起来,是「从源码结论到可验证证据」这条链路的工程化能力。
六、心态与教训:不适用不等于无效
平台判定「不适用」,只代表这次发现偏离了奖励规则,并不否定代码逻辑本身——本地实打实的通过结果就是证据。虽未获赏金,但完整跑通了从源码分析到本地复现的全链路,这套可复现、可验证的能力才是真正带走的东西。给未来审计的两条务实结论:重心应更多放在「无需权限即可触达的公共函数」上,那里才是赏金规则认定的外部攻击面;对确有缺陷、却落在特权区的发现,正确的用法是作为「纵深防御缺口」写进加固建议,而不是当作可计赏的漏洞提交。
七、防御视角:跨链桥该怎么补这两个洞
从被审计方的加固看,两个缺陷都指向可模板化的检查项。对称性检查:任何「一侧有防护、另一侧应对称」的资金路径都要逐一核对——存款有重放防护,提现也必须有;锁定与释放、发起与回执,成对入口的安全不变量必须成对实现。转换完整性检查:所有定长类型之间的强制转换,都要问「截断会不会把有效值变成无效值、把非零变成零」,并在转换后校验结果的完整语义(地址长度、是否可支付地址、是否零地址),而非只在转换前查一个「非零」。攻击面与权限边界检查:把「哪些入口外部可自由调用、哪些仅特权角色可达」显式建档,对特权入口同样做内部校验加固——因为「角色被攻破」虽不在赏金范围内,却是真实事件,最小权限与自身校验不应以「调用者可信」为唯一前提。把这三类检查钉进代码审查清单与测试用例(用例要写成「重放必须被拒」「截断成零地址必须被拒」这类否定断言),比事后逐条发现这两个缺陷更省力,也更经得起审计规则的边界检验。