返回论坛

智能合约安全审计:揭秘“移花接木”式恶意代码隐藏手法

查找币 学术研究 安全研究 Web3安全 区块链安全

查找币安全研究院

钱包恢复评估 | 链上取证分析 | Web3 事件响应
以合法授权、证据保全、隐私保护和可复核流程为前提,不要求用户在线提交完整私钥或助记词。

查看研究院 研究报告中心
## 背景概述 在上一期的技术分享中,我们深入剖析了利用 `tx.origin` 进行钓鱼攻击的手法。今天,查找币安全团队将带大家探索一种更为隐蔽的攻击方式——在合约部署阶段隐藏恶意代码。这种手法利用开发者对合约地址的信任盲区,通过“移花接木”的方式将恶意逻辑隐藏在看似无关的合约中,极具欺骗性。 ## 攻击原理与技术细节 ### 核心逻辑 攻击者通过部署一个“中间合约”(如 `MoneyMaker`),在构造函数中传入一个恶意合约地址(而非受害者预期的合法合约地址)。受害者由于未核验部署交易,盲目信任了攻击者提供的合约地址,最终导致资金被盗。 **关键点:** - 受害者预期:合约 A 调用合约 B(合法逻辑) - 实际执行:合约 A 调用合约 C(恶意逻辑) ### 技术实现示例 以下是一个典型的攻击代码示例,展示了攻击者如何通过隐藏的恶意合约窃取资金。 ```solidity // 看似合法的资金倍增合约 contract MoneyMaker { Vault vault; constructor(address _vault) { vault = Vault(payable(_vault)); } function makeMoney(address recipient) public payable { require(msg.value >= 1, "You are so poor!"); uint256 amount = msg.value * 2; (bool success, ) = address(vault).call{value: msg.value, gas: 2300}(""); require(success, "Send failed"); vault.transfer(recipient, amount); } } // 受害者预期的合法金库合约 contract Vault { address private maker; address private owner; uint256 transferGasLimit; constructor() payable { owner = msg.sender; transferGasLimit = 2300; } modifier OnlyMaker() { require(msg.sender == maker, "Not MoneyMaker contract!"); _; } modifier OnlyOwner() { require(msg.sender == owner, "Not owner!"); _; } function setMacker(address _maker) public OnlyOwner { maker = _maker; } function transfer(address recipient, uint256 amount) external OnlyMaker { require(amount <= address(this).balance, "Game Over~"); (bool success, ) = recipient.call{value: amount, gas: transferGasLimit}(""); require(success, "Send failed"); } function withrow() public OnlyOwner { (bool success, ) = owner.call{value: address(this).balance, gas: transferGasLimit}(""); require(success, "Send failed"); } receive() external payable {} fallback() external payable {} } // 隐藏的恶意合约(攻击者部署) contract Hack { event taunt(string message); address private evil; constructor(address _evil) { evil = _evil; } modifier OnlyEvil() { require(msg.sender == evil, "What are you doing?"); _; } function transfer() public payable { emit taunt("Haha, your ether is mine!"); } function withrow() public OnlyEvil { // 窃取资金逻辑 } } ``` ### 攻击流程详解 1. **部署阶段**:攻击者 Evil 部署 `Vault` 合约,并存入 100 ETH。随后,Evil 部署 `Hack` 合约,并将自己的地址传入构造函数。 2. **欺骗阶段**:Evil 向受害者 Bob 展示 `MoneyMaker` 合约的源码,声称该合约会调用 `Vault` 合约实现资金倍增。Bob 验证了 `MoneyMaker` 和 `Vault` 的源码,未发现恶意代码。 3. **部署陷阱**:Evil 部署 `MoneyMaker` 合约时,在构造函数中传入的是 `Hack` 合约的地址,而非 `Vault` 合约地址。Bob 未检查这笔部署交易,盲目信任了 Evil。 4. **资金窃取**: - Bob 调用 `MoneyMaker.makeMoney()` 并转入 20 ETH。 - 实际执行路径:`MoneyMaker.makeMoney()` → `Hack.transfer()` → 触发事件 `"Haha, your ether is mine!"` - Bob 未收到任何回报,20 ETH 被锁定在 `MoneyMaker` 合约中。 - Evil 调用 `Vault.withrow()` 提取 100 ETH,再通过 `Hack.withrow()` 窃取 Bob 的 20 ETH。 ## 攻击核心要素 - **信任盲区**:受害者仅验证了合约源码,未核验部署交易中的实际参数。 - **隐藏逻辑**:恶意代码被部署在独立的 `Hack` 合约中,而非直接暴露在 `MoneyMaker` 中。 - **不可逆性**:一旦部署完成,攻击路径无法更改,资金损失不可挽回。 ## 防御策略与审计建议 ### 1. 交易验证是唯一真理 在以太坊的黑暗森林中,**不要相信任何人的口头承诺或展示的源码**。每笔交易都记录在链上,只有亲自验证部署交易中的实际参数,才能确认合约的真实行为。 ### 2. 审计检查清单 - **构造函数参数检查**:审计时需重点检查合约构造函数中传入的地址是否与预期一致,是否存在被替换的风险。 - **多合约交互审计**:当合约涉及多个外部合约调用时,需完整追踪调用链,确保每个环节的合约地址都经过验证。 - **部署交易溯源**:建议使用区块浏览器(如 Etherscan)查看部署交易,确认 `input data` 中的参数是否与源码一致。 ### 3. 用户安全实践 - **拒绝盲信**:即使项目方提供了完整的开源代码,仍需通过链上交易验证实际部署参数。 - **使用安全工具**:可借助自动化审计工具检查合约间的调用关系,识别潜在的地址替换风险。 - **小额测试**:在参与任何资金倍增或高收益项目前,先进行小额测试交易,观察实际执行路径。 ## 总结 “移花接木”式攻击手法利用了开发者和用户对合约源码的信任,通过隐藏恶意代码实现了资金窃取。在区块链安全审计中,**地址验证**和**交易溯源**是防范此类攻击的关键。希望本期的技术分享能帮助大家提升对合约安全的理解,在实际开发与审计中避免陷入类似的陷阱。 > **注**:本文参考自《Solidity by Example》,并结合查找币安全团队的实际审计经验进行深度解析。 --- **本文由查找币安全团队整理发布**
在论坛中查看和回复