本报告由GCSA Web3 Labs ZkSec发布:任意外部调用漏洞——合约将调用目标与数据完全交给调用者填写——是链上授权资金面临的一类核心风险。本文基于归档节点数据,重放21亿笔历史交易,扫描出超过一百个存在相关风险的合约,涉及多个代码族。文章系统介绍检测方法(历史交易重放、调用参数溯源与变异验证),分析漏洞构成与以太坊/BSC链间差异,并以已知攻击与扫描检出案例进行交叉验证,最终给出防御建议。
1.1任意外部调用漏洞
存在任意外部调用漏洞的合约,提供一个公开入口:调用的目标地址和数据由任何提交者填写,合约以自己的身份执行。正常合约不是这样——要调用谁、传什么数据,写在代码里或限定在白名单内。
这种入口有时是刻意的设计取舍(批量工具、跨链桥为了通用故意开放),有时是设计缺陷(本想限制调用范围,却漏了校验)。无论是刻意开放还是校验遗漏,只要合约持有用户授权或滞留资金,缺少调用者限制的入口就可能被非预期调用者利用。
1.2一个目标限制不足的案例
BSC上的MetaAggregatorSwapContract(0xc17b234fad8e40642662f1d2a6cbd1d609e7b9b5)存在目标限制不足的任意外部调用风险。它的功能是代用户执行兑换:接收代币,调用外部聚合器完成交易。
![[MD:Title]](http://img1.mydrivers.com/img/20261027/10516787-aac5-49f7-877c-9f162dde7e76.jpg)
aggregator和swapData都来自调用者,合约没有白名单校验。非delegate模式下,合约通过CALL调用aggregator;delegate模式下,代码在SWAP_TARGET的上下文中执行。目标限制不足,可能扩大合约可调用的范围;合约名下还挂着用户为兑换授权的额度。
aggregator和swapData可控,但这本身不能推出任意token.transferFrom攻击。minAmountOut在源码中要求大于零;非delegate模式下,MetaAggregator通过CALL调用aggregator,代币看到的调用者还要由实际调用链确定。因此,本案例只有在给出完整trace、spender和资金路径后,才能判定为授权转移漏洞。
要把该入口判定为授权转移漏洞,还需列出实际spender、受害者授权和资金流;现有示例只支持滞留余额路径。两条路径应分开列出。
2.1漏洞扫描系统设计
把历史交易一笔一笔重新执行,拿到完整的调用树——每一层调用、每个参数都有记录。然后在树里找可操控的点:内部调用的某个参数(转账给谁、转多少、调用哪个合约、带什么数据),如果能一路追溯到交易顶层calldata里的原始字节,只能说明这个参数可能受外部提交者影响,还要通过变异重放确认。把这些点逐个替换后重放,验证参数是否确实可控,以及操控能否传导成真实的资金流动。
2.2扫描执行流程
一笔交易在代码里的完整执行流程见图1。
![[MD:Title]](http://img1.mydrivers.com/img/20261027/036fb7c8-fbfa-402f-aa17-591c236627a4.jpg)
2.3调用参数溯源
扫描器先检查内层参数是否能在顶层calldata中找到原始字节。找到时,只能说明该参数在当前编码路径上可由提交者影响,仍需通过变异重放确认。
扫描器做字节级匹配:参数的32字节直接出现在顶层对齐位置,或整段调用calldata作为连续字节串嵌在顶层,即登记为可由外部参数改变的内部参数并记下偏移;匹配不到时标记为“无法建立参数来源”,不据此断定该值由合约内部算出。变异时改顶层偏移,重放验证内层是否跟随。
当前实现不做语义分析,只做EVM层的字节匹配,因此开源和闭源合约都能处理。已知缺口是:合约在内存里重组calldata时,参数来源可能无法继续追踪。
2.4实现
整套系统只有一个核心设计:每笔历史交易,都在它发生前一瞬的世界状态上执行两遍(图2)。
![[MD:Title]](http://img1.mydrivers.com/img/20261027/e215ae42-0cd2-4713-93cf-9452073cadc1.jpg)
一笔交易的执行结果,取决于它之前所有交易留下的状态——区块里第5笔交易看到的余额,是前4笔执行完的余额。重放第5笔之前,先把前4笔逐笔执行到一个临时状态层(PreState)里,停在第5笔开始前。
然后在这份状态上,把这笔交易跑两遍。第一遍原样执行,记录完整过程:调用了哪些合约、转了哪些钱、改了哪些存储。第二遍换掉可操控的参数——发起人、收款人、金额——再执行,看结果有什么不同。两遍都不改这份状态,所以换不同参数可以反复试。
试完之后,把第一遍的结果写进状态层,继续处理下一笔交易。这样区块里的每一笔,都在它当年看到的那个世界上执行。
2.5排除误报
重放产出的待审核记录以万计,人工逐条看完不可能,绝大多数在复核之前就被流水线筛掉了。图1的每个✕标记一类被筛除的原因,这里展开其中几种。
地址投毒批次 钓鱼者用假币、零值或极小额转账污染地址记录。扫描器会看到多个受害者、官方代币名称和极小额转账等相似形态,因此需要结合代币代码、金额和调用批次排除。
![[MD:Title]](http://img1.mydrivers.com/img/20261027/26e7315e-14b9-41ce-8390-f55c059b369c.jpg)
实例:0x184aaf087ca6dd17eb1200dbfbee9f671bd906fc。该家族在同轮扫描中产生数百条记录,包含几十个helper,未发现官方稳定币的非零余额。
仿冒币名 假币攻击的升级——代币名字里混进西里尔字母或零宽字符,肉眼看是USDT,字节上不是。一律标记。
USDT→55 53 44 54 (ASCII)
USDТ→55 53 44 d0a2 (末位是西里尔字母Т,U+0422)
实例:0x9257cf49…f4253、0x6b1b80b4…1ca6cc,同一批地址投毒记录中转出的仿冒USDT/USDC。
EIP-7702重放限制 在这组type-4重放中,原交易由原发起人携带authorizationList执行;换成测试地址并去掉该列表后,交易回滚。这个结果只说明该重放依赖原授权状态,不能推广为所有EIP-7702交易都必须携带authorizationList。扫描器保留这类记录,再用caller_only和dual两种变体区分真实可执行路径与重放条件导致的回滚。
原始交易 type-4,携带authorizationList(账户本人签名)→执行成功
换人重放 同一calldata,发起人换成测试地址,无授权列表→整笔revert
使用相同字节码的账户组 多个地址运行同一段字节码、使用同一个发起人和归集地址,待审记录来自一组同特征账户。
//一组账户的共同特征:字节码全部指向同一个委托目标
0xef0100 + 631af7731569e10b22a0430051cfd5c8930a1656 //多个账户共用
发起人相同·归集地址相同
实例:0x001100bd…3ff16、0x043d01a9…669d,两个成员的字节码完全相同。
3.1判定条件
扫描器筛出的漏洞记录按图3依次判定。条件一、二用于确认参数可控性和模拟执行是否成功;条件三只区分资金来源:付款方是授权者时标为授权滥用,资金来自公开流动性时标为公开套利。两类记录的后续复核不同,不能直接等同于真实漏洞。
![[MD:Title]](http://img1.mydrivers.com/img/20261027/e99e5dd1-0064-4ba5-b55c-26dbe407848d.jpg)
3.2 AI审核
扫描器产出的待审核记录由AI自动复核(图4)。AI读取原始交易的调用和资金流,分别做换发起人和换收款人重放。模拟成功只证明替换参数后交易可执行;资金来源、授权关系或调用链证据不足时,记录标为“证据不足,待人工复核”,不自动判为漏洞。
![[MD:Title]](http://img1.mydrivers.com/img/20261027/43b31dbc-b9eb-419f-a22f-79215c69dc80.jpg)
AI复核结果回写到面板(图5)。图5展示单条记录的五阶段证据链、标签、备注和模拟编号;它不展示全局标签计数。
![[MD:Title]](http://img1.mydrivers.com/img/20261027/48598fe7-dfb5-4401-9433-8f731d8616db.jpg)
4.1已知攻击交易检测
Seneca Protocol Chamber用于验证扫描器检出的漏洞:该事件有公开分析,相关调用模式也出现在扫描结果中。
Seneca Protocol于2024年2月28日遭到攻击,损失达数百万美元。BlockSec、SlowMist和Rekt分别分析了这起事件。受影响合约地址为 0x65c210c5…91f06,其Chamber.sol源码已在Etherscan验证。图6、图7对应的PT-weETH重放交易为 0x6da8f7cb…e820d,调用目标为clone 0xbc83f271…7c05。0x23fcf9d4…01286 是另一笔PT-rsETH交易,不与图混用。
漏洞代码如下,省略无关部分:
![[MD:Title]](http://img1.mydrivers.com/img/20261027/17d00974-1516-4247-af70-983068c090d9.jpg)
这与第1.2节的MetaAggregatorSwapContract属于同一类外部调用风险:调用者把callee和callData编入顶层calldata,Chamber解码后直接使用。在该历史状态中,Chamber名下保留着用户为管理仓位授予的ERC-20授权,因此需要进一步核对实际spender、受害者授权对象和资金路径,才能判定为授权转移漏洞。
扫描器重放这笔交易,用call_tracer记录完整调用树。在Chamber的内部调用帧中,它定位到callee.call(callData),并确认callee和callData都能追溯到顶层datas参数。系统将它们登记为可操控点,再把顶层calldata中对应的收款人地址替换为测试地址,在同一历史状态上重放。交易执行成功,测试地址收到PT-weETH;资金来源和调用链证据另行记录。
图6展示这笔PT-weETH重放交易的Invocation Flow Diff,图7展示对应的Fund Flow Diff;两图共同呈现替换收款人前后的调用和资金差异。
![[MD:Title]](http://img1.mydrivers.com/img/20261027/5549c4f2-c3c5-4fe1-b3e1-867c72d5a786.jpg)
![[MD:Title]](http://img1.mydrivers.com/img/20261027/3cc47759-5afc-49bd-a199-64bade5b25c2.jpg)
thirdweb Pay UniversalGatewayV1(2025年,以太坊)——公开事件交叉验证
thirdweb的支付网关 0x81d57dd01a15bc1c19563693902df621500d4d2a 也出现在扫描结果中。thirdweb的公开报告确认该旧合约存在授权余额风险并已停用。
该历史实现没有可直接核对的验证源码。签名绑定、假代币地址和callData的具体关系,需要与对应区块的代理实现和交易trace对齐;当前参考源码不能替代历史实现作为结论依据。
该案例的历史调用包含代币地址、转发数据和授权余额之间的组合关系。当前材料不足以把“假代币地址绕过扣款”确定为该历史实现的根因;在补充对应区块的实现代码和完整trace前,不将其作为已确认机制。
5.1扫描器检出的风险路径
以下案例的证据类型不同:部分来自历史交易重放,部分还有源码、审计或事故报告支持。每节只对已列出的证据作判断,并区分扫描结果和外部资料。
案例状态以所引交易的区块状态或明确的查询时间为准;余额、授权和合约是否仍可调用,不从历史交易直接推导当前状态。
一、Maker Multicall2:没有调用者白名单的批量执行器
0x5ba1e12693dc8f9c48aad8770482f4739beed696
![[MD:Title]](http://img1.mydrivers.com/img/20261027/562edc8c-2c11-4626-9267-23fb305d1d3a.jpg)
target和callData全部来自调用者,合约没有调用者白名单校验,但仍检查底层调用是否成功。它是通用批量执行器,风险取决于用户是否把代币授权给了这个执行器;本文样本中的授权和资金路径应按具体交易逐笔核对。
二、THORSwap TSAggregatorGeneric:目标限制不完整
0x0ccd5dd5bcf1af77dc358d1e2f06ee880ef63c3c
![[MD:Title]](http://img1.mydrivers.com/img/20261027/340ee15f-39d9-4797-97f3-76203283064b.jpg)
当前源码只排除了TTP,没有把swapRouter限制到白名单,因此代币合约仍可作为目标。历史状态重放显示,调用者可以把swapRouter和data指向代币转账路径;是否构成授权滥用,还需同时核对spender、授权人和资金来源。
三、Symbiosis MetaRouter:跨链路由的任意calldata
0xf621fb08bbe51af70e7e0f4ea63496894166ff7f
metaRoute从网关提取用户授权的代币,并检查relayRecipient不是网关且目标地址有代码。随后,源码直接使用调用者提供的otherSideCalldata调用relayRecipient;这里没有按函数选择器建立白名单。
otherSideCalldata的字节内容没有按函数选择器做白名单校验,目标地址只受网关地址和代码存在性检查约束。扫描器检出的交易中,该参数包含USDT.transferFrom;如要认定为授权滥用,还需把交易中的spender、授权人和资金接收方逐项列出。
四、LayerZero LZMultiCall:授权错位与无签名执行入口
0x5528cf58feb8fbfce94f43b33240fffb1312bde3
![[MD:Title]](http://img1.mydrivers.com/img/20261027/3fc70fe0-e45b-4c6f-85ae-0154c853e520.jpg)
原始复核交易走的是两参数的无签名入口,不是带_signature的重载。该入口把msg.sender作为signer;只有目标是TransferDelegate时,才检查transfer的from是否等于signer。若调用列表直接把代币合约作为target并放入token.transferFrom(受害者,自己,金额),代码会走通用的_handleCall,由LZMultiCall作为spender发起转账。
LayerZero的正常流程应把授权交给TransferDelegate;如果用户把授权直接给了LZMultiCall,公开的无签名execute就可能消耗这份额度。这里的风险来自授权对象与通用转发路径的错位,不涉及带签名入口。
五、thirdweb Pay UniversalGatewayV1:证据范围
0x81d57dd01a15bc1c19563693902df621500d4d2a
该案例已在4.1作为公开事件交叉验证。公开报告确认旧合约存在授权余额风险并已停用;历史实现的签名绑定、代币地址和callData的具体关系,需要与对应区块的代理实现和交易trace对齐。本文不使用当前UniversalBridgeV1参考源码替代历史实现下定论。
六、BSC BnbSettler:BASIC任意调用消耗误授权
0x4c6f446dd88fd1be8b80d2940806002777dc12a2
这是0x Protocol Settler的BSC部署。风险入口由公开的execute和BASIC action组成,转账路径通过action进入代币合约:
![[MD:Title]](http://img1.mydrivers.com/img/20261027/ab1d296f-82a4-4e87-9edc-95401e5c7bac.jpg)
用户若直接把ERC-20授权给Settler,调用者就可以把pool填成代币合约,把data编成token.transferFrom(受害者,自己,金额)。Settler以自身身份发起调用,代币会按这份授权执行。历史重放还显示该合约存在可改变收款人的滞留余额;这两条路径应分开计。
表中按主要利用前提归类;“扫描检出的漏洞”“历史重放确认”和“公开事件交叉验证”不是同一个证据等级:
![[MD:Title]](http://img1.mydrivers.com/img/20261027/30f7c1ad-ddb6-43a5-9fc9-714388940feb.jpg)
这些案例的证据类型不同:部分有历史交易重放,部分只有源码或公开报告。扫描器能直接验证的是:在对应历史状态下,改变顶层可控参数是否仍能把资金导向测试地址;其余结论需要单独标注来源和证据范围。
5.2漏洞记录汇总
扫描器在以太坊和BSC上汇总了超过四百条漏洞记录,涉及三百多个地址。同一地址可能对应多条检测记录,地址中也包括EIP-7702委托账户。
以太坊的记录主要集中在Maker Multicall2;BSC的记录分布在更多地址,单个地址对应的记录较少。其他记录还涉及CallDataExecutor、UniswapInterfaceMulticall、SquidMulticall和0x Settler等合约。完整地址清单见附录A、B。
![[MD:Title]](http://img1.mydrivers.com/img/20261027/ad4c630c-0487-4df4-877c-cf94eaca3bf2.jpg)
5.3 Lynx:交易分析与模拟平台
Lynx是我们自研的交易分析与模拟平台,支持以太坊和BSC,接口与标准JSON-RPC兼容。本文的重放验证和资金流对比通过Lynx完成。平台支持基于归档节点历史状态的eth_call模拟,可替换发起人、修改calldata或覆盖账户余额。
Lynx通过 debug_traceCall 及相应的tracer获取调用树,记录调用目标、calldata和返回值,供扫描器追踪参数来源;界面同时展示存储变化。平台还分别生成原始交易和模拟交易的资金流图,对比资产的来源、去向和金额。第4.1节图6、图7分别展示调用流程和资金流的差异,图8展示调用树界面。
每次模拟结果按唯一编号保存,可按编号复查。
![[MD:Title]](http://img1.mydrivers.com/img/20261027/c8b73c6e-d775-4658-86ad-342bbeec8202.jpg)
结语
本文在21亿笔历史交易中扫描出超过一百个漏洞合约,涉及基础设施、跨链路由器、支付网关和工具合约等形态。其中一部分漏洞经历史状态重放确认;Seneca和thirdweb提供了独立的公开事件作为交叉验证。
在已复现的案例中,授权额度在余额变化后仍可能存在,因此监控授权存量可以补充余额事件监控。这个结论不外推到所有案例。
防御建议:
用户:用完就撤销授权,或改用支持过期签名的授权方案(如Permit2)。授权前确认spender对应的是协议预期的执行入口;如果协议通过代理或Permit2转发,还要核对实际spender、目标合约和额度。
协议开发者:任意外部调用入口应限制目标和selector/data;签名转发入口应把签名内容绑定到调用者和接收者。打补丁时审计所有调用路径;THORSwap的历史补丁只排除了一个目标,其他目标仍需检查。
行业:可将授权存量纳入持续监控,记录spender、授权人、区块状态和余额变化。本文展示的是历史重放;持续追踪不在本文范围。
附录A:以太坊漏洞记录对应地址
![[MD:Title]](http://img1.mydrivers.com/img/20261027/0aff36e9-6e3d-4194-9254-c8d60fa9d8bd.jpg)
附录B:BSC漏洞记录对应地址(部分)
![[MD:Title]](http://img1.mydrivers.com/img/20261027/765d8886-bc75-4e22-878f-1573b587ad7c.jpg)
版权声明:本报告及相关技术分析由GCSA全球网络安全联盟独家发布。如需转载请完整保留GCSA官方出处及原始链接,并不得对报告核心观点进行恶意篡改。

