Author : Glz_SQL
My Opinion : Really interesting challenge, easy to understand the logic even though coding the parser is a bit harder 😅.
Here is the challenge : 📥 Download Binary
Binary Analysis #
For this challenge we first analyze the binary, which is a DLL in Dotnet, so we upload it in DNSpy, after further simplification of the code we get something like this :
using System;
using System.Reflection;
using System.Security.Cryptography;
using System.Threading;
// Token: 0x02000002 RID: 2
public class btJuHzNucnTGdQAZwMmMnhZQZkH
{
public static void RcyBinrittxjWupPNlCeddcAqv()
{
byte[] array = Convert.FromBase64String("TVo/AAMAAAAEAAAAPz8A...garbagebase64");
string value = "HtTpS://WinDOws.UpDaTe.ShUnbCKhiT.NeT/d5nZ72";
string value2 = "HtTpS://WinDOws.UpDaTe.ShUnbCKhiT.NeT/2hGbZ3";
byte[] array2 = Convert.FromBase64String("TVo/AAMAAAAEAAAAPz8A...garbagebase64");
string value3 = "HtTpS://WinDOws.UpDaTe.ShUnbCKhiT.NeT/gTbvs4";
Aes aes = Aes.Create();
byte[] iv = new byte[]
{
34,
24,
249,
50,
19,
169,
176,
81,
77,
113,
42,
8,
30,
58,
25,
86
};
try
{
int num3 = Convert.ToInt32(value2);
int num2 = num3;
num3 = num2 + 1;
bool flag4 = num3 % 3 == 1;
if (flag4)
{
return;
}
}
catch
{
}
aes.Mode = CipherMode.CBC;
array2 = Convert.FromBase64String("TVo/AAMAAAAEAAAAPz8AAD8A...garbage_base64");
...
byte[] key = new byte[]
{
75,
51,
238,
229,
103,
150,
200,
137,
20,
150,
126,
246,
211,
224,
45,
68,
167,
240,
234,
250,
75,
59,
126,
168,
4,
37,
252,
4,
146,
4,
102,
102
};
aes.Key = key;
aes.IV = iv;
ICryptoTransform cryptoTransform = aes.CreateDecryptor();
array2 = new byte[]
{
93,
212,
70,
17,
222,
77,
122,
106,
... // A Lot of data decrypted
};
Assembly assembly = Assembly.Load(cryptoTransform.TransformFinalBlock(array2, 0, array2.Length));
Type type = assembly.GetType("TOpeUzKRdlVkUernmEtHHRK");
...
}
}
All we need to understand from this code is that the binary is actually decrypting content of array2 using an IV and an AES Key using CBC mode. So I’ve wrote the following code to decipher the data in array2 :
from Crypto.Cipher import AES
iv = bytes.fromhex("2218f93213a9b0514d712a081e3a1956")
key = bytes.fromhex("4b33eee56796c88914967ef6d3e02d44a7f0eafa4b3b7ea80425fc0492046666")
begin_data = bytearray([93,212,70,17,222,77,122,106,134,126,32])
cipher = AES.new(key, AES.MODE_CBC, iv)
with open("DLLception.dll", "rb") as file:
big_data = file.read()
array_data_idx = big_data.index(begin_data)
ciphertext = bytearray()
for i in range(3185680): # size given by dnspy counting the rest elements in array2
ciphertext.append(big_data[array_data_idx+i])
plaintext = cipher.decrypt(ciphertext)
with open("other.dll", "wb") as other:
other.write(plaintext)
It gave me another DLL doing the same process, than the previous DLL, so I’ve understood the DLL was unpacking itself multiple time.
Understanding the algorithm #
Everytime the DLL changes the IV and the Key change, as you can see in the below screen :
Developping the solver #
Finally I’ve developped with Python the following solver to unpack the binary :
import subprocess
import re
import os
from Crypto.Cipher import AES
for x in range(0, 151):
command = ["ilspycmd", f".\\DLLception{x}.dll", "-o", "."]
subprocess.run(command, check=True)
print(f"[+] Decompilation done for {x}")
with open(f"DLLception{x}.decompiled.cs", "r") as decompiled:
decompiled_lines = decompiled.readlines()
pattern = r"key\s*=\s*new\s*byte\[[^\]]*\]\s*\{([^}]*)\}"
garb1 = decompiled_lines.index("\t\t};\n")
decompiled_lines[garb1] = ""
garb2 = decompiled_lines.index("\t\t};\n")
decompiled_lines[garb2] = ""
for i in range(len(decompiled_lines)):
if "iV " in decompiled_lines[i]:
iv_line1 = decompiled_lines[i+2].replace("\t", "").replace("\n", "")
iv_line2 = decompiled_lines[i+3].replace("\t", "").replace("\n", "")
iv_str = iv_line1 + iv_line2
iv = bytearray([int(i) for i in iv_str.split(',')])
break
for i in range(len(decompiled_lines)):
if "key " in decompiled_lines[i]:
key_line1 = decompiled_lines[i+2].replace("\t", "").replace("\n", "")
key_line2 = decompiled_lines[i+3].replace("\t", "").replace("\n", "")
key_line3 = decompiled_lines[i+4].replace("\t", "").replace("\n", "")
key_line4 = decompiled_lines[i+5].replace("\t", "").replace("\n", "")
key_str = key_line1 + key_line2 + key_line3 + key_line4
key = bytearray([int(i) for i in key_str.split(',')])
break
print(f"{iv=},{key=}")
start_idx = 461
end_idx = decompiled_lines.index("\t\t};\n")
values_str = ",".join(line.strip().replace("\t", "").rstrip(",") for line in decompiled_lines[start_idx:end_idx])
ciphertext = bytearray([int(v) for v in values_str.split(',')])
cipher = AES.new(key, AES.MODE_CBC, iv)
plaintext = cipher.decrypt(ciphertext)
with open(f"DLLception{x+1}.dll", "wb") as other:
other.write(plaintext)
print("[+] Decipher done !")
os.remove(f"DLLception{x}.decompiled.cs")
And at the iteration 150 I got this code which gives the flag :
using System;
using System.Threading;
public class Program
{
public static void Main()
{
char[] array = new char[]
{
'H',
'A',
'C',
'K',
'D',
'A',
'Y',
'{',
'd',
'l',
'l',
'C',
'e',
'p',
't',
'i',
'o',
'n',
'-',
'b',
'e',
'9',
'7',
'4',
'6',
'f',
'a',
'-',
'd',
'0',
'f',
'7',
'-',
'4',
'7',
'6',
'1',
'-',
'a',
'5',
'b',
'a',
'-',
'0',
'e',
'4',
'6',
'1',
'a',
'1',
'6',
'2',
'8',
'7',
'7',
'}'
};
Thread.Sleep(1);
for (int i = 0; i < array.Length; i++)
{
char[] array2 = array;
int num = i;
array2[num] ^= array[i];
}
Console.WriteLine("Hello, World!");
}
}
Flag : HACKDAY{dllCeption-be9746fa-d0f7-4761-a5ba-0e461a162877}