返回文章库

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

查找币 学术研究 安全研究 Web3安全 区块链安全
智能合约安全审计:揭秘“移花接木”式恶意代码隐藏手法

背景概述

在上一期的技术分享中,我们深入剖析了利用 tx.origin 进行钓鱼攻击的手法。今天,查找币安全团队将带大家探索一种更为隐蔽的攻击方式——在合约部署阶段隐藏恶意代码。这种手法利用开发者对合约地址的信任盲区,通过“移花接木”的方式将恶意逻辑隐藏在看似无关的合约中,极具欺骗性。

攻击原理与技术细节

核心逻辑

攻击者通过部署一个“中间合约”(如 MoneyMaker),在构造函数中传入一个恶意合约地址(而非受害者预期的合法合约地址)。受害者由于未核验部署交易,盲目信任了攻击者提供的合约地址,最终导致资金被盗。

关键点:

技术实现示例

以下是一个典型的攻击代码示例,展示了攻击者如何通过隐藏的恶意合约窃取资金。

// 看似合法的资金倍增合约
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。

攻击核心要素

防御策略与审计建议

1. 交易验证是唯一真理

在以太坊的黑暗森林中,不要相信任何人的口头承诺或展示的源码。每笔交易都记录在链上,只有亲自验证部署交易中的实际参数,才能确认合约的真实行为。

2. 审计检查清单

3. 用户安全实践

总结

“移花接木”式攻击手法利用了开发者和用户对合约源码的信任,通过隐藏恶意代码实现了资金窃取。在区块链安全审计中,地址验证和交易溯源是防范此类攻击的关键。希望本期的技术分享能帮助大家提升对合约安全的理解,在实际开发与审计中避免陷入类似的陷阱。

注:本文参考自《Solidity by Example》,并结合查找币安全团队的实际审计经验进行深度解析。


本文由查找币安全团队整理发布

前往社区查看和回复