Himayatic 2017 - Rev400 Writeup
Hello, today I’ll crack a .NET crackme, it was featured on Himayatic CTF, November 2nd 2017. Download link : https://drive.google.com/open?id=0B7U3AsTA9UVfRHdTY2hfQzZrQm8
Let’s start :)
First, we notice that it’s a .NET crackme, it asks for a serial, and displays "Wrong Serial ... !!!"
when we enter a random one.
We open it in a .NET decompiler (I used dnSpy
, which is a fork of ILSpy
), and we immediately locate this function :
The code looks obfuscated, we follow the N
and M
functions in the namespace A
, and we find this:
Looks like they used CryptoObfuscator
to obfuscate the code, we’ll use the popular .NET deobfuscator de4dot to clean the executable.
We open the cleaned executable in dnSpy
, the obfuscation is gone!
But it’s a wrong flag, if it were the real flag, the else part would display "Wrong Serial ... !!!"
and not "Illusion ... !!"
(of course, we still try it, who knows :p).
We don’t find anything interesting in the Form
class, so we try debugging in x64dbg
, we notice that when we open the program with x64dbg
, it spawns itself in a new process and then exits from the main process, we breakpoint on CreateProcessA
and CreateProcessW
, just to ensure that we are correct, and yes, we hit the breakpoint on CreateProcessW
, I did not notice the CREATE_SUSPENDED
state in dwCreationFlags
, I immediately went back to the decompiler, to find out where it spawns the new process, and what it does exactly.
We suspect that it uses the RunPE
technique, which in short works like this:
- It spawns itself as a new process in suspended state
- It locates the base address of the new exe.
- It uses
WriteProcessMemory
or a similar API to write every section to the new exe - It sets the context of the main thread of the new process
- It resumes the new process
We go back to dnSpy
, and we find out that we were correct, the code is in the Skins
class.
Now, to analyze the code in the new process, we have two possiblities:
- dump it while it’s running
- hook the API that writes to the new process, and intercept the data written
Let’s follow the first option, I used pd (process dump) to dump the process and all its DLLs while it was running, this way I got the final exe.
The new exe looks a lot like the original one.
More fake flags :p.
after more investigation, we find this function :
It Loads another Base64-encoded assembly, we look for T1.Text
to T4.Text
, they are initialized in the InitializeComponent
method in FZero
class, all this way:
this.T1.Text = componentResourceManager.GetString("T1.Text");
We find the strings in the resources
We concatenate them, decode as base64
, and save output as a file, it’s a DLL.
We load it in dnSpy
, and this time, we find the real check :
the method V
calls the method E
to encrypt the user input, then it calls the method VR
to check if it’s correct, VR
compares a fixed, twice-base64
-encoded string (stored encoded once in ressources) with the encrypted and twice-encoded user input, then it calls DS
if the check succeeds, we find "Wrong Serial ... !!|Yeah,, you did it :)'"
in the resources.
the method E
(which encrypts) looks like this:
It concatenates the second argument with "Himayatic_0xC001"
, then hashes the result using md5
, then it initializes a buffer of 32
characters, then it copies the md5
hash to a buffer starting at index 0
(copies 16
characters), then copies the same md5
hash to the same buffer starting at index 15
(copies 16
characters), so it overwrites the last character of the first copy, then it uses the buffer as the key to encrypt the first function parameter, it encrypts it using AES
in ECB
mode, then encodes it using base64
.
We go back to where the assembly is loaded and used, we look for references to the function GetSkin
, which returns an instance of CTFs__.Himayatic
We find it in FZero_Load
: this.o = RuntimeHelpers.GetObjectValue(this.Himayatic.getSkin());
We look for references to this.o
, and we find this :
It’s pretty straightforward to understand this code, it’s calling the method V
, something like this : V( this.FL__.Text, this.Name, true)
this.FL__.Text
is the user input, this.Name
can be found in FZero
class ( this.Name = "FZero";
).
After that, it’s pretty simple to get the flag, just a simple Ruby script:
require'base64';
require'openssl';
require'digest';
begin
enc = Base64.decode64 "FQUFl/85WMFJp5XXfJX5Xykt8WhTPcy1MD0/0+SqEsj/IdMrolb3Haaq9yiZvcuH";
e = OpenSSL::Cipher::AES.new(256,:ECB)
e.decrypt; # initialize it for decryption
md5 = Digest::MD5.digest("FZero" + "Himayatic_0xC001");
key = "\x00" * 32;
key[0,16] = md5;
key[15, 16] = md5;
e.key = key;
puts (e.update(enc)+e.final); # should output the flag
rescue Exception => e
# show the exception
p e;
p e.message;
p e.backtrace
ensure
# wait before exiting
gets
end
Himayatic{*1LLu510N__W17h0u7_C4rD5*}
Really nice challenge.