[Reverse] CyBRICS CTF Quals 2019 - Matreshka

Matreshka 50

Description:
Matreshka (Reverse, Easy, 50 pts) Author: Khanov Artur (awengar)
Matreshka hides flag. Open it
https://cybrics.net/files/matreshka.zip

Decompiling java

After unzipping the file we find 2 files one is a .class and an encrypted file. First thing that I did was to decompile the .class file I used this website to do it http://www.decompiler.com

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
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;

class Code2 {
public static byte[] decode(byte[] var0, String var1) throws Exception {
SecretKeyFactory var2 = SecretKeyFactory.getInstance("DES");
byte[] var3 = var1.getBytes();
DESKeySpec var4 = new DESKeySpec(var3);
SecretKey var5 = var2.generateSecret(var4);
Cipher var6 = Cipher.getInstance("DES");
var6.init(2, var5);
byte[] var7 = var6.doFinal(var0);
return var7;
}

public static byte[] encode(byte[] var0, String var1) throws Exception {
SecretKeyFactory var2 = SecretKeyFactory.getInstance("DES");
byte[] var3 = var1.getBytes();
DESKeySpec var4 = new DESKeySpec(var3);
SecretKey var5 = var2.generateSecret(var4);
Cipher var6 = Cipher.getInstance("DES");
var6.init(1, var5);
byte[] var7 = var6.doFinal(var0);
return var7;
}

public static void main(String[] var0) throws Exception {
String var1 = "matreha!";
byte[] var2 = encode(System.getProperty("user.name").getBytes(), var1);
byte[] var3 = new byte[]{76, -99, 37, 75, -68, 10, -52, 10, -5, 9, 92, 1, 99, -94, 105, -18};

for(int var4 = 0; var4 < var3.length; ++var4) {
if (var3[var4] != var2[var4]) {
System.out.println("No");
return;
}
}

File var9 = new File("data.bin");
FileInputStream var5 = new FileInputStream(var9);
byte[] var6 = new byte[(int)var9.length()];
var5.read(var6);
var5.close();
byte[] var7 = decode(var6, System.getProperty("user.name"));
FileOutputStream var8 = new FileOutputStream("stage2.bin");
var8.write(var7, 0, var7.length);
var8.flush();
var8.close();
}
}

Interpreting the java file

Now that we have some java code I started to analyse the decode and encode functions and we can easily see that the algorithm used for the encryption was DES, the encode function encrypts an array of bytes with a key string, the decode function obviously does the opposite.

1
2
3
4
5
6
7
8
9
10
public static byte[] encode(byte[] var0, String var1) throws Exception {
SecretKeyFactory var2 = SecretKeyFactory.getInstance("DES");
byte[] var3 = var1.getBytes();
DESKeySpec var4 = new DESKeySpec(var3);
SecretKey var5 = var2.generateSecret(var4);
Cipher var6 = Cipher.getInstance("DES");
var6.init(1, var5);
byte[] var7 = var6.doFinal(var0);
return var7;
}

By looking at the main function we can see the key used to encrypt the file was an username from the machine of the creator of the challenge, we don’t know this username but we do know the result of the encryption of this username and the key used:

1
2
3
4
5
6
7
8
9
10
String var1 = "matreha!";
byte[] var2 = encode(System.getProperty("user.name").getBytes(), var1);
byte[] var3 = new byte[]{76, -99, 37, 75, -68, 10, -52, 10, -5, 9, 92, 1, 99, -94, 105, -18};

for(int var4 = 0; var4 < var3.length; ++var4) {
if (var3[var4] != var2[var4]) {
System.out.println("No");
return;
}
}

We can easily reverse this since we have the key matreha! we can just use the decode function to decrypt the username and use it to decrypt the stage2.bin, I did small modifications to the java file and ended up 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
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
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;

class Code2
{
public static byte[] decode(byte[] paramArrayOfByte, String paramString) throws Exception {
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("DES");
byte[] arrayOfByte = paramString.getBytes();
DESKeySpec dESKeySpec = new DESKeySpec(arrayOfByte);
SecretKey secretKey = secretKeyFactory.generateSecret(dESKeySpec);
Cipher cipher = Cipher.getInstance("DES");
cipher.init(2, secretKey);
return cipher.doFinal(paramArrayOfByte);
}

public static byte[] encode(byte[] paramArrayOfByte, String paramString) throws Exception {
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("DES");
byte[] arrayOfByte = paramString.getBytes();
DESKeySpec dESKeySpec = new DESKeySpec(arrayOfByte);
SecretKey secretKey = secretKeyFactory.generateSecret(dESKeySpec);
Cipher cipher = Cipher.getInstance("DES");
cipher.init(1, secretKey);
return cipher.doFinal(paramArrayOfByte);
}


public static void main(String[] paramArrayOfString) throws Exception {
String str = "matreha!";
byte[] arrayOfByte2 = { 76, -99, 37, 75, -68, 10, -52, 10, -5, 9, 92, 1, 99, -94, 105, -18 };
byte[] userName = decode(arrayOfByte2, str);
byte[] arrayOfByte1 = encode(userName, str);

for (byte b = 0; b < arrayOfByte2.length; b++) {
if (arrayOfByte2[b] != arrayOfByte1[b]) {
System.out.println("No");
return;
}
}

File file = new File("data.bin");
FileInputStream fileInputStream = new FileInputStream(file);
byte[] arrayOfByte3 = new byte[(int)file.length()];
fileInputStream.read(arrayOfByte3);
fileInputStream.close();
byte[] arrayOfByte4 = decode(arrayOfByte3, new String(userName));
FileOutputStream fileOutputStream = new FileOutputStream("stage2.bin");
fileOutputStream.write(arrayOfByte4, 0, arrayOfByte4.length);
fileOutputStream.flush();
fileOutputStream.close();
}
}

Now compiling it with javac and running with java commands we get stage2:

1
2
3
4
$ javac wtf.java 
$ java Code2
$ file stage2.bin
stage2.bin: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, Go BuildID=FBY_TepBaPVEzlo3-SXy/Zsd256T1rd3lPJ9tnxng/YgTlqVI_KVISIMJ6lZi7/tIlG0PXO43MvY1MOVlul, not stripped

Stage2 (golang binary)

Now we have a 64bit ELF, this a go binary , this one is very similar to the previous challenge but kind of harder to reverse since is go, this time instead of the system username the directory name is used to encrypt the file:

Once again after this, a verification of an encrypted folder is done before trying to decrypt the stage3 file:

After this I created a folder named abcdefghijklmnopq which has 0x11 (17 in decimal) of size, I inserted a break point at cmp dl, bl and dumped the encrypted string abcdefghijklmnopq and the original encrypted string folder name which is present in the binary.

First getting the encrypted bytes of the folder I created:

Second get the encrypted bytes of the original folder:

We have everything we need to recover the original folder name! Since is xor encryption we just need to get the bytes of the encrypted string abcdefghijklmnopq xor it with the plaintext abcdefghijklmnopq and finally xor with encrypted original name reversing the all thing, I used a python script to do this:

1
2
3
4
5
6
a = [0x59,0xCD,0xC9,0x90,0xE9,0x6E,0x9F,0x23,0x4B,0xAF,0x0B,0x33,0xCC,0x39,0x03,0xDB,0xA0]
cp =[0x53,0xdd,0xc5,0x87,0xe4,0x63,0x99,0x14,0x4f,0xa4,0x14,0x2d,0xc4,0x24,0x04,0xc0,0xb0]
key = ''
for i,c in enumerate('abcdefghijklmnopq'):
key += chr(ord(c) ^ a[i] ^ cp[i])
print key

Getting the original folder name

1
2
$ python qwd.py 
kroshka_matreshka

To dump the final file just move the binary to a folder named kroshka_matreshka

Stage 3 (python pyc file)

This stage is the easiest one we just need to decompile the .pyc with https://pypi.org/project/uncompyle6/ and we end up with this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def decode(data, key):
idx = 0
res = []
for c in data:
res.append(chr(c ^ ord(key[idx])))
idx = (idx + 1) % len(key)

return res


flag = [
40, 11, 82, 58, 93, 82, 64, 76, 6, 70, 100, 26, 7, 4, 123, 124, 127, 45, 1, 125, 107, 115, 0, 2, 31, 15]
print('Enter key to get flag:')
key = input()
if len(key) != 8:
print('Invalid len')
quit()
res = decode(flag, key)
print(''.join(res))

Once again xor encryption since we already know the first 8 bytes of the flag as “cybrics{“ if we xor this with the first bytes of the encrypted flag we will get the key it’s easy to write a python script for this:

1
2
3
4
5
6
7
c = [40, 11, 82, 58, 93, 82, 64, 76, 6, 70, 100, 26, 7, 4, 123, 124, 127, 45, 1, 125, 107, 115, 0, 2, 31, 15]
x = 0
key = ''
for i in 'cybrics{':
key += chr(ord(i) ^ c[x])
x += 1
print key

Getting the key

1
2
python roflmao.py 
Kr0H4137

The key was Kr0H4137, using it to decrypt the flag:

1
2
3
4
$ python3 result.py
Enter key to get flag:
Kr0H4137
cybrics{M4TR35HK4_15_B35T}

The flag was cybrics{M4TR35HK4_15_B35T}