首页 时政热点 科技头条 智能AI 安全攻防 数码硬件 开发者生态 归档 标签 关于

赏金踩坑实录:因“非标代币不在白名单”被拒收的白链桥假充值漏洞分析

摘要

前言(踩坑经验分享) 最近在对白链(Whitechain)的桥接协议(bridge-contracts)进行代码审计时,我发现了一个逻辑非常严密的 Critical 级别漏洞(假充值漏洞)。理论上,攻击者可以利用该漏洞以零成本掏空目标链的流动性池。 我用 Foundry 编写了完美的 PoC 并在本地跑通了全流程,随后提交至 HackenProof 平台。但遗憾的是,这份报告最终被官方以“Out ...

000 Foundry PoC false amount 漏洞点二 Deposit 漏洞点一 _executeTokenTransferFrom 函数中
2026-11-12 1 阅读 约10分钟阅读 先知社区
分享:
字号:
前言(踩坑经验分享) 最近在对白链(Whitechain)的桥接协议(bridge-contracts)进行代码审计时,我发现了一个逻辑非常严密的 Critical 级别漏洞(假充值漏洞)。理论上,攻击者可以利用该漏洞以零成本掏空目标链的流动性池。 我用 Foundry 编写了完美的 PoC 并在本地跑通了全流程,随后提交至 HackenProof 平台。但遗憾的是,这份报告最终被官方以“Out of Scope(超出审计范围)”为由关闭了。 这次“踩坑”经历非常典型。在 Web3 赏金平台,除了比拼技术实力,读懂项目方的“威胁模型(Threat Model)”同样重要。借此机会,我写下这篇复盘文章,一来分享漏洞挖掘的技术细节与 Foundry PoC,二来总结一下在 Bug Bounty 挖掘中,如何让自己的报告更契合平台的规则,希望能给各位提供一些参考经验。 一、 漏洞详情与代码分析 该漏洞存在于目标项目的 Bridge.sol 合约中,具体位于存款(Deposit)的流程内。这个攻击之所以能够成立,是因为合约中同时存在两个致命的设计缺陷,它们组合在一起形成了一条完美的攻击链。 漏洞点一:绕过 SafeERC20 且忽略返回值 在桥接合约的 _executeTokenTransferFrom 函数中,处理代币转账的逻辑存在严重问题。当函数参数 useTransfer 为 true 时,合约显式地绕过了引入的安全库 SafeERC20Upgradeable,而是直接使用了底层的原生调用进行代币转移,伪代码如下: IERC20Upgradeable(token).transferFrom(from, to, amount); 致命点在于:这个原生调用完全忽略了布尔返回值。在以太坊的 ERC-20 标准中,transferFrom 成功通常返回 true,失败返回 false。由于合约没有使用 require() 去捕获这个返回值(即没有检查是否转账成功),一旦遇到转账失败(返回 false)或只转账了部分金额(Fee-on-transfer 扣税代币)的情况,合约的执行不仅不会回滚(revert),还会继续往下走,仿佛什么都没发生一样。 漏洞点二:缺失余额差值校验(盲目信任用户输入) 紧接着,在上层的 bridgeTokens 函数中,用户发起跨链存款请求。逻辑是:先调用刚刚提到的 _executeTokenTransferFrom 尝试把钱转进桥接合约,然后根据用户传入的 amount 参数,发出一个 Deposit 跨链事件,通知目标链的中继器(Relayer)给用户放款。 致命点在于:合约在调用转账之后,没有去计算实际收到多少代币(例如使用 balanceAfter - balanceBefore 来校验)。它直接盲目地信任了用户输入的 amount 参数来记账和发事件。也就是说,只要代码不报错,不管你实际转没转进来钱,合约都以为你转进来了。 完整的攻击链路 结合上述两个漏洞点,攻击者可以实施极具破坏性的跨链假充值攻击: 攻击者部署或利用一个非标准代币(例如转账永远静默失败返回 false 的代币,或者带有高额转账手续费的代币)。 攻击者调用桥接合约的 bridgeTokens,声称要存入 1,000,000 枚该代币。 桥接合约执行转账,由于代币是静默失败的,桥接合约实际上收到了 0 枚代币。 但因为【漏洞点一】,忽略返回值,代码继续执行;因为【漏洞点二】,合约没有校验余额,直接根据入参发出了 1,000,000 枚代币的跨链存款事件。 目标链的 Relayer 监听并接收到事件后,将释放 1,000,000 枚真实代币给攻击者。攻击者完成零成本套现,导致目标链流动性池被直接抽干。 现实世界中的可利用性 这不是一个仅仅依靠“管理员恶意注册假币”才能触发的边缘场景,它实质上是一个代币兼容性问题,会影响主流的合法代币: • 静默失败代币:例如老版本的 USDT,在某些极端网络或合约状态下,它失败时是返回 false 而不是直接回滚的。这会导致一笔失败的存款被系统误认为成功。 • 转账扣费代币(Fee-on-Transfer):如果某个代币设置了 10% 的交易税,用户尝试跨链存入 1000 枚,实际上只有 900 枚到达桥接合约。但由于【漏洞点二】缺失了余额校验,桥接合约依然会给用户记上 1000 枚的跨链额度。这意味着跨链池子里的流动性每被操作一次,就会流失 10% 的差额。 二、 PoC 复现(Foundry 实战) 为了验证漏洞的真实性,我使用 Foundry 编写了 PoC,模拟了一个 FalseToken(静默失败代币)以及极简的桥接逻辑。 核心合约代码 // 模拟真实世界中有缺陷的代币(静默失败:不报错,只返回 false) contract FalseToken { mapping(address => uint256) public balanceOf; // 核心作弊逻辑:转账时永远失败,返回 false,也不扣钱 function transferFrom(address from, address to, uint256 amount) external returns (bool) { return false; } } // 模拟白链桥漏洞的极简靶机 contract BridgeTarget { function bridgeTokens(address token, uint256 amount) external { // 直接调用 transferFrom,并且【不检查返回值】,也不检查余额差值 (bool success, ) = token.call( abi.encodeWithSignature("transferFrom(address,address,uint256)", msg.sender, address(this), amount) ); // 致命漏洞:这里没有 require(success),也没有 balanceAfter - balanceBefore 校验! // 合约以为存款成功,直接发出跨链事件 emit DepositSuccess(msg.sender, token, amount); } event DepositSuccess(address indexed user, address indexed token, uint256 amount); } 2. 攻击测试脚本 import {Test, console} from "forge-std/Test.sol"; import {FalseToken, BridgeTarget} from "src/FakeDeposit.sol"; contract FakeDepositTest is Test { FalseToken public fakeToken; BridgeTarget public bridge; address public attacker; function setUp() public { fakeToken = new F
这篇文章对您有帮助吗?

订阅66必读

每日精选科技资讯,直达你的邮箱