Translation
This post is also available in
Simplified Chinese.
image-crackme
I tried to reverse the challenge with IDA Pro, but IDA crashed halfway through loading it, so I had to work by inference. After running image-crackme.exe several times, I found that it always generated a MeePwn.ascii containing a 160×160 block of seemingly random characters. The directory also contained a 160×160 MeePwn.jpg, without which the program would not run. I suspected a relationship between the image pixels and the output. I replaced the original with a completely white 160×160 image so every pixel was identical, then entered 0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|} (ASCII 48–126) as the key. The output had a very regular pattern:

This pattern is revealing. The input key is likely repeated to fill 160×160 positions (ABCDEF → ABCDEFABCDEF…), after which every byte is combined somehow with the corresponding pixel in MeePwn.jpg to produce the repeated output.
The prompt says the flag has the form MeePwn{…}. We can therefore try flags of different lengths and compare the number of matching symbols between the resulting MeePwn.ascii and MeePwn.ascii.bak to determine the true flag length.
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
| import os
def march():
maxlen=160
marches=0
s1=open('MeePwn.ascii').read().split('\n')
s2=open('MeePwn.ascii.bak').read().split('\n')
for i in range(maxlen):
for j in range(maxlen):
if s1[i][j]==s2[i][j]:
marches+=1
return marches
def main():
alla=160*160
maxklen=40
mars=[]
keys=[]
for klen in range(1,maxklen):
key='MeePwn{'+'?'*klen+'}'
os.system('echo %s|.\\image_crackme.exe'%key)
marches=march()
mars.append(marches)
keys.append(key)
print(mars)
print(keys)
for i in range(len(mars)):
print('%d/%d %s %d'%(mars[i],alla,keys[i],len(keys[i])))
if __name__=='__main__':
main()
'''RESULT
2880/25600 MeePwn{????????????????????????} 32
8107/25600 MeePwn{?????????????????????????} 33
2836/25600 MeePwn{??????????????????????????} 34
'''
|
With a key length of 33, the number of matches is clearly much higher than for the others (the factor 11 produces a similarly high result), so the flag is 33 characters long. Once the length is known, each character can be brute-forced independently by counting matches at all positions occupied by that character after the key is repeated.
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
| import os
def march(index,klen):
alla=25600
i=index
marches=0
s1=''.join(open('MeePwn.ascii').read().split('\n'))
s2=''.join(open('MeePwn.ascii.bak').read().split('\n'))
while i<alla:
if s1[i]==s2[i]:
marches+=1
i+=klen
return marches
def test(rawkey,index,charset):
keylen=33
keys=[]
mars=[]
for ch in charset:
key=rawkey[:index]+ch+rawkey[index+1:]
os.system('echo %s|.\\image_crackme.exe'%key)
marches=march(index,keylen)
mars.append(marches)
keys.append(key)
keychars=[]
for i in range(len(charset)):
if mars[i]>700:
keychars.append(charset[i])
print(keychars)
return keychars
def main():
keycharss=[]
rawkey='MeePwn{?????????????????????????}'
charset='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-=?!+@%*/\\'
for index in range(7,32):
keycharss.append(test(rawkey,index,charset))
#keycharss=[['g'], ['0'], ['l'], ['4'], ['n'], ['g'], ['_'], ['A'], ['s'], ['c'], ['1'], ['1'], ['A'], ['r'], ['t'], ['_'], ['1'], ['S'], ['_'], ['4'], ['w'], ['S'], ['0'], ['m'], ['e']]
flag='MeePwn{'+(''.join(map(lambda x:x[0],keycharss)))+'}'
assert(len(flag)==33)
print(flag)
if __name__=='__main__':
main()
|
Flag: MeePwn{g0l4ng_Asc11Art_1S_4wS0me}
I have to admit that there was still a large gap between me and real CTF players. I could make neither head nor tail of the other challenges.