[Reverse] 3DS - Ransomware


Ransomware - 464 Points
WARNING! DON’T EXECUTE THIS SAMPLE IN YOUR OWN PERSONAL MACHINE!!!

We have a malware to analyse, right at the beginning(_start function) I found an encrypted string with xor, reversing it wasn’t very hard as you can see bellow:



The actual function in the ida was this one:



Rewriting this function into python we ended up with this:

1
2
3
4
5
6
7
8
9
10
11
12
import struct

def sub_4005D0(al, ecx, edx):
c = ''
for i in range(ecx):
c += chr(ord(edx[i]) ^ ord(al))
return c

first = struct.pack ("16B", *[
0xea,0xe8,0xe4,0xe8,0xef,0xdb,0xe1,0xeb,0xe6,0xe0,0xa9,
0xea,0xe8,0xe4,0xe8,0xef])
print sub_4005D0(al='\x87',ecx=0x11-1, edx=first)

By running the script we can see that the encrypted string was the name of the file we received from the challenge which was encrypted:

1
2
$ python writeup_mocoh.py 
mocoh\flag.moco

One good thing to do while reversing is to rename the function and string names to a more readable names, so if they are reused we can immediately recognize them :


Now continuing analysing the contents of sub_40023B we have more strings to decrypt:



Updating this python script with this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import struct
def sub_4005D0(al, ecx, edx):
c = ''
for i in range(ecx):
c += chr(ord(edx[i]) ^ ord(al))
return c

first = struct.pack ("16B", *[
0xea,0xe8,0xe4,0xe8,0xef,0xdb,0xe1,0xeb,0xe6,0xe0,0xa9,
0xea,0xe8,0xe4,0xe8,0xef])
print sub_4005D0(al='\x87',ecx=0x11-1, edx=first)
a = sub_4005D0(al='\x54',ecx=9-1, edx=struct.pack ("8B", *[
0x27,0x31,0x21,0x26,0x21,0x36,0x21,0x37]))
b = sub_4005D0(al='\x80',ecx=9-1, edx=struct.pack ("8B", *[
0xe1,0xee,0xf4,0xe1,0xf3,0xf3,0xe5,0xe5]))
c = sub_4005D0(al='\x14',ecx=9-1, edx=struct.pack ("8B", *[
0x61,0x77,0x7b,0x78,0x7b,0x77,0x75,0x62]))
d = sub_4005D0(al='\x32',ecx=0xa-1, edx=struct.pack ("9B", *[
0x53,0x5c,0x53,0x55,0x53,0x5b,0x5d,0x5e,0x53]))

print a
print b
print c
print d

By running we can see it’s some words in Portuguese and they don’t look really useful at all:

1
2
3
4
5
6
7
$ python mocoh.py
mocoh\flag.mocoh
mocoh\flag.mocoh
seurubuc
antassee
ucolocav
anagaiola

After renaming the variable names in IDA ended up in this final part of the program:



As you can see above every string we decrypted is being concatenated into a place in memory at dword_4012c0, and at the end in sub_4005DA the flag file name is being pushed in to the stack since this will be a argument to that function so this may be the one that was used to encrypted the flag.mocoh file!



Now by checking this function I found it too hard to reverse sub_400684, without running the malware itself (Couldn’t do it since it executable wasn’t compatible with my Windows 10 VM), so after failing to infect myself and debugging it with IDA, I checked that sub_4006D8 of them was using xor again to encrypt that file:



Now you may be asking if I didn’t reverse sub_400684 how the hell I did get the keys to decrypt the flag file? well I wasn’t really expecting this to happen but at some point I knew I could know the first 4 bytes of the key because we know the first 4 bytes of the plaintext which is part of the flag format 3DS{, I wanted to check before reversing the rest, the first 4 bytes of the key, I did this by brute-forcing byte by byte:

1
2
3
4
5
6
7
flag_cipher = open('flag.mocoh', 'r').read()
plain = '3DS{'
for i in range(0x4):
for x in xrange(1,0xff):
if ord(plain[i]) == ord(flag_cipher[i]) ^ x:
print x
break

And surprisingly to me the first 4 bytes of the keys was always 175

1
2
3
4
5
$ python test.py 
175
175
175
175

And then I asked myself what if the key is always the same? this couldn’t be a coincidence:

1
2
3
4
5
flag_cipher = open('flag.mocoh', 'r').read()
flag = ''
for i,c in enumerate(flag_cipher):
flag += chr(ord(c) ^ 175)
print flag

Running it we could see that this was the case…

1
2
$ python test.py 
3DS{4sS3mbly_r0cks!!}

And in the end I realized that I lost a lot of time reversing the binary, we didn’t even needed to reverse anything, if we made a guess that it was xor we could just tried and check that it was using always the same 1 byte key to encrypt…