Write-up-camed-Reverse-Me
Description
Serie of 7 challenges in .Net, available here: http://crackmes.de/users/camed/reverse_me/
Reverse Me! 1
Crackme
The crackme looks like this:
Analysis
This crackme is an easy one. Start by opening Reverse Me! 1.exe in dnSpy adn go to the Entry Point. The code is as follows:
using System;
using System.Windows.Forms;
namespace Reverse_Me__1
{
// Token: 0x02000003 RID: 3
internal static class Program
{
// Token: 0x06000007 RID: 7 RVA: 0x000023DD File Offset: 0x000005DD
[STAThread]
private static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
In line 15, Form1 is called. Let's click on the item in dnSpy to see the code:
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
namespace Reverse_Me__1
{
// Token: 0x02000002 RID: 2
public class Form1 : Form
{
// Token: 0x06000001 RID: 1 RVA: 0x00002050 File Offset: 0x00000250
public Form1()
{
this.InitializeComponent();
}
// Token: 0x06000003 RID: 3 RVA: 0x0000206C File Offset: 0x0000026C
private void button1_Click(object sender, EventArgs e)
{
string a = this.textBox1.Text.ToString();
string b = "f4h9b";
if (a == b)
{
MessageBox.Show("Correct! You passed first challange!", "Congratulations!");
Application.Exit();
}
else
{
MessageBox.Show("Incorrect password | Enter Correct Password!", "Wrong Password");
}
}
// ...[SNIP]...
}
}
Solution
In line 21, the user input is saved to a. In line 22, the expected password (f4h9b) is saved to b. Starting from line 23, there is a test that compares both strings. If they match, you are done!
Reverse Me! 2
Crackme
The crackme looks like this:
Analysis
Open the crackme in dnSpy and go to the Entry Point. On line 15, Form1 is called:
using System;
using System.Windows.Forms;
namespace Reverse_Me__2
{
// Token: 0x02000004 RID: 4
internal static class Program
{
// Token: 0x06000009 RID: 9 RVA: 0x0000247D File Offset: 0x0000067D
[STAThread]
private static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
Below is an extract of Form1:
private void button1_Click(object sender, EventArgs e)
{
this.buff = this.textBox1.Text;
if (this.buff == this.string1)
{
MessageBox.Show("Great, you passed second level!", "Great Job!");
Application.Exit();
}
else
{
MessageBox.Show("Wrong Password!", "Wrong Password!");
}
}
On line 21, the user input is saved to this.buff and on line 22, it is compared to this.string1. If both strings match, you win.
Some lines later, you will see the following code that is loaded when the form is loaded.
private void Form1_Load(object sender, EventArgs e)
{
string str = "damn_password123";
Shift shift = new Shift();
int shift2 = -3;
this.string1 = shift.Cshift(str, shift2).ToString();
}
You can see that the string damn_password123 is used as parameter to the Cshift function. The second parameter (-3) is the offset as we will see later. Now, let's see what this function looks like.
using System;
namespace Reverse_Me__2
{
// Token: 0x02000003 RID: 3
internal class Shift
{
// Token: 0x06000007 RID: 7 RVA: 0x00002428 File Offset: 0x00000628
public string Cshift(string str, int shift)
{
string text = null;
char[] array = str.ToCharArray();
for (int i = 0; i < str.Length; i++)
{
int num = (int)array[i] + shift;
text += (char)num;
}
return text;
}
}
}
This code uses the Caesar cipher to transform a string (str) given an offset (shift).
Solution
You can easily solve this crackme with python:
>>> tmp = [] >>> for i in s: ... tmp.append(chr(ord(i)-3)) ... >>> "".join(tmp) 'a^jk\\m^pptloa./0'
Reverse Me! 3
Crackme
The crackme looks like this:
Analysis
Load the crackme in dnSpy and go to the entry point. Form1 is called at line 15:
using System;
using System.Windows.Forms;
namespace Reverse_Me__3
{
// Token: 0x02000003 RID: 3
internal static class Program
{
// Token: 0x06000008 RID: 8 RVA: 0x000023C5 File Offset: 0x000005C5
[STAThread]
private static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
Below is the code of Form1.button1_Click, the function called when the Check password button is pressed:
private void button1_Click(object sender, EventArgs e)
{
string mD = this.getMD5(this.textBox1.Text);
if (mD == "22DB96BB26EEAF15E68B828A1361C132")
{
MessageBox.Show("Correct password!", "Great job!");
}
else
{
MessageBox.Show("Incorrect password.", "Sorry");
}
}
The MD5 hash of the user input is compared to 22DB96BB26EEAF15E68B828A1361C132 and both string match, the "Correct password!" popup is displayed.
Solution
You can use https://crackstation.net/ to crack this hash:
The password is rpassd:
Reverse Me! 4
Crackme
The crackme looks like this:
Analysis
Confuser
This crackme has been protected with Confuser.
C:\_malware\de4dot-v3-1>de4dot.exe "..\Reverse Me! 4.exe" Latest version and source code: http://www.de4dot.com/ 21 deobfuscator modules loaded! Detected Confuser (not supported) (C:\_malware\Reverse Me! 4.exe) Cleaning C:\_malware\Reverse Me! 4.exe Renaming all obfuscated symbols Saving C:\_malware\Reverse Me! 4-cleaned.exe
It's bad because de4dot can't unpack it and you will have to go through a long manual process (http://www.scribd.com/doc/207710371/NET-Decrypt-Confuser-1-9-Methods) if you wish to unpack/deobfuscate it. But fortunately for us, if you notice that the crackme if establishing an Internet connection, there is a much easier way to solve it.
Intercept network traffic
For this crackme, you will need an Internet connection. Indeed, the crackme validates the serial entered by the user by gathering a file on dropbox and by checking that its content matches the user input.
As the traffic makes uses of an encrypted connection to dropbox, I've used Fiddler:
Here is the request:
GET https://dl.dropboxusercontent.com/s/wchdf0w1reyr39x/n36nax.txt HTTP/1.1 Host: dl.dropboxusercontent.com Connection: Keep-Alive
And here is the intercepted response:
HTTP/1.1 200 OK Server: nginx Date: Sat, 13 Feb 2016 19:49:08 GMT Content-Type: text/plain; charset=utf-8 Content-Length: 7 Connection: keep-alive content-disposition: inline; filename="n36nax.txt"; filename*=UTF-8n36nax.txt set-cookie: uc_session=bcyy3pDxCozlbgzXMGCgMJ7eOtglWr3l4JZhPyxANCzVYZ35HLrNeDwypHOdyoCt; Domain=dropboxusercontent.com; httponly; Path=/; secure accept-ranges: bytes etag: 4n x-dropbox-request-id: e5139bd570e734693177072620c30461 pragma: public cache-control: max-age=0 X-Server-Response-Time: 174 X-Robots-Tag: noindex, nofollow, noimageindex 9ac7bvd
As you can see, this crackme gathers a file named n36nax.txt on dropbox and reads its content (9ac7bvd).
Solution
Reverse Me! 5
Crackme
The crackme looks like this:
If you click on one of the Check password button, the other button is disable and no message appears.
Analysis
Open the crackme in dnSpy and go to the Entry Point. At line 15, Form1 is called.
using System;
using System.Windows.Forms;
namespace Reverse_Me__5
{
// Token: 0x02000003 RID: 3
internal static class Program
{
// Token: 0x06000008 RID: 8 RVA: 0x00002521 File Offset: 0x00000721
[STAThread]
private static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
Below is an extract of Form1 showing the buttons onlick methods:
private void button1_Click(object sender, EventArgs e)
{
this.button1.Enabled = false;
this.button2.Enabled = true;
if (this.button1.Enabled)
{
MessageBox.Show("Password is 78asC");
if (this.textBox1.Text == "78asC")
{
MessageBox.Show("Great! You passed challange!");
}
else
{
MessageBox.Show("Wait, what?");
}
}
}
// Token: 0x06000005 RID: 5 RVA: 0x000020FC File Offset: 0x000002FC
private void button2_Click(object sender, EventArgs e)
{
this.button1.Enabled = true;
this.button2.Enabled = false;
if (this.button2.Enabled)
{
MessageBox.Show("Password is 78asC");
if (this.textBox1.Text == "78asC")
{
MessageBox.Show("Great! You passed challange!");
}
else
{
MessageBox.Show("Wait, what?");
}
}
}
As you can see:
- The password is obvious: 78asC
- when the 1st Check Password button is clicked, the code disables it and enables the 2nd one
- when the 2nd Check Password button is clicked, the code disables it and enables the 1st one
In both cases, the code checks the status (enabled or disabled) of the clicked button and does nothing if it is disabled. With such a situation, we won't be able to validate the crackme as is and we have to patch it.
Patching
To patch the crackme, I've used IDA-Pro. Here is how I proceed:
- Open the crackme into IDA-Pro
- Go to Reverse_Me5.Form1_button1_Click method. Here is the code:
seg000:0040 .method private hidebysig instance void button1_Click(object sender, class [mscorlib]System.EventArgs e)
seg000:0040 {
seg000:0040 .maxstack 2
seg000:0040 .locals init (bool V0)
seg000:0040 00 nop
seg000:0041 02 ldarg.0
seg000:0042 7B 05 00 00 04 ldfld class [System.Windows.Forms]System.Windows.Forms.Button Reverse_Me__5.Form1::button1
seg000:0047 16 ldc.i4.0
seg000:0048 6F 12 00 00 0A callvirt instance void [System.Windows.Forms]System.Windows.Forms.Control::set_Enabled(bool)
seg000:004D 00 nop
seg000:004E 02 ldarg.0
seg000:004F 7B 06 00 00 04 ldfld class [System.Windows.Forms]System.Windows.Forms.Button Reverse_Me__5.Form1::button2
seg000:0054 17 ldc.i4.1
seg000:0055 6F 12 00 00 0A callvirt instance void [System.Windows.Forms]System.Windows.Forms.Control::set_Enabled(bool)
seg000:005A 00 nop
seg000:005B 02 ldarg.0
seg000:005C 7B 05 00 00 04 ldfld class [System.Windows.Forms]System.Windows.Forms.Button Reverse_Me__5.Form1::button1
seg000:0061 6F 13 00 00 0A callvirt instance bool [System.Windows.Forms]System.Windows.Forms.Control::get_Enabled()
seg000:0066 16 ldc.i4.0
seg000:0067 FE 01 ceq
seg000:0069 0A stloc.0
seg000:006A 06 ldloc.0
seg000:006B 2D 45 brtrue.s loc_B2
seg000:006D 00 nop
seg000:006E 72 01 00 00 70 ldstr aPasswordIs78as // "Password is 78asC"
Notice that the highlighted lines correspond to the instructions that disable the first Check Password button when it is clicked. Let's NOP these instructions with 0x00. If you would like to know how to do it from IDA-Pro, you can refer to this page: https://www.aldeid.com/wiki/IDA-Pro#Patching.
Below is the difference between the patched function and the unpatched one. As you can notice in the first function, the button is no longer disabled:
Solution
Once the crackme is patched, you can enter the password in the text field and click on the 1st Check Password button. Here is the success message:
Reverse Me! 6
Crackme
The crackme looks like this:
Analysis
Open the crackme in dnSpy and go to the Entry Point. Form1 is called at line 15:
using System;
using System.Windows.Forms;
namespace Reverse_Me__6
{
// Token: 0x02000003 RID: 3
internal static class Program
{
// Token: 0x06000009 RID: 9 RVA: 0x000025E5 File Offset: 0x000007E5
[STAThread]
private static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
The code for the onlick method of the Check button is as follows:
private void button1_Click(object sender, EventArgs e)
{
try
{
string str;
using (StreamReader streamReader = new StreamReader("C:/Temp/testvalue.txt"))
{
str = streamReader.ReadToEnd();
}
string str2;
using (StreamReader streamReader = new StreamReader("C:/Temp/rundll_tmp.TEMP"))
{
str2 = streamReader.ReadToEnd();
}
string str3 = Form1.doit(this.us_name);
this.duke = str + str3 + str2;
if (this.textBox1.Text == this.duke)
{
MessageBox.Show("Correct!");
}
else
{
MessageBox.Show("Wrong Password");
}
}
catch
{
}
}
You will notice that the 2 following files are accessed:
- C:/Temp/testvalue.txt
- C:/Temp/rundll_tmp.TEMP
If you are curious to know how these files are created, go to line 70 (code executed when Form1 is loaded):
private void Form1_Load(object sender, EventArgs e)
{
string contents = Form1.RandomString(4);
string contents2 = Form1.RandomString(6);
if (File.Exists("C:/Temp/testvalue.txt"))
{
File.Delete("C:/Temp/testvalue.txt");
File.Create("C:/Temp/testvalue.txt").Close();
File.WriteAllText("C:/Temp/testvalue.txt", contents);
}
else
{
File.Create("C:/Temp/testvalue.txt").Close();
File.WriteAllText("C:/Temp/testvalue.txt", contents);
}
if (File.Exists("C:/Temp/rundll_tmp.TEMP"))
{
File.Delete("C:/Temp/rundll_tmp.TEMP");
File.Create("C:/Temp/rundll_tmp.TEMP").Close();
File.WriteAllText("C:/Temp/rundll_tmp.TEMP", contents2);
}
else
{
File.Create("C:/Temp/rundll_tmp.TEMP").Close();
File.WriteAllText("C:/Temp/rundll_tmp.TEMP", contents2);
}
}
These files are both filled with random strings:
public static string RandomString(int length)
{
Random r = new Random();
return new string((from s in Enumerable.Repeat<string>("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", length)
select s[r.Next(s.Length)]).ToArray<char>());
}
Now that we know that, let's get back to button1_Click. You know that:
- str contains the content of the C:/Temp/testvalue.txt file which is randomly filled
- str2 contains the content of the C:/Temp/rundll_tmp.TEMP file which is also randomly filled
- At line 35, we see a 3rd string str3 which is the result of Form1.doit(this.us_name)
Let's see what the this.us_name string and Form1.doit functions are.
this.us_name contains the user name of the logged in user:
private string us_name = Environment.UserName.ToString();
And this string is passed to the Form1.doit function that reverses the string received as argument:
public static string doit(string s)
{
char[] array = s.ToCharArray();
Array.Reverse(array);
return new string(array);
}
Solution
In the button1_Click method, a string this.duke is created by concatenating str, str3 and str2. If the user input matches the resulting string, you are done.
this.duke = str + str3 + str2;
if (this.textBox1.Text == this.duke)
{
MessageBox.Show("Correct!");
}
In our example, here is the solution:
string | Description | Value |
---|---|---|
str | Content of C:/Temp/testvalue.txt | 2VO4 |
str3 | Username (crackme) reversed | emkcarc |
str2 | Content of C:/Temp/rundll_tmp.TEMP | 2VO4KD |
Reverse Me! 7
Crackme
The crackme looks like this:
Obviously, this one is also packed and/or obfuscated. Let's confirm with de4dot:
C:\_malware\de4dot-v3-1>de4dot.exe "\_malware\Reverse_Me!\Reverse Me! 7.exe" Latest version and source code: http://www.de4dot.com/ 21 deobfuscator modules loaded! Detected Confuser (not supported) (C:\_malware\Reverse_Me!\Reverse Me! 7.exe) Cleaning C:\_malware\Reverse_Me!\Reverse Me! 7.exe Renaming all obfuscated symbols Saving C:\_malware\Reverse_Me!\Reverse Me! 7-cleaned.exe
And... it is! Using Confuser. Unless you wish to manually go through a manual unpacking/deobfuscation of the crackme, there must be an easier way to solve it.
Analysis
As the output guesses, let's try to monitor filesystem and registry changes implied by the crackme. There are many tools to achieve it but there is one of them that you should particularly consider: Process Monitor (procmon) from the Sysinternals suite.
If you filter the output to match only process containing "reverse", you should be able to see this:
At runtime, the crackme creates 2 files in C:\Temp as well as a key in the registry key, that looks like this:
Solution
This key looks like a MD5 hash (CE0E679968CEAAE34EFFAB354EAA57D7), that we can crack with https://crackstation.net/:
We can validate the password (wazzup) in the crackme:
Welcome to Reverse Me! 7 application! Created by camed/chomikos Visit me at camedcomputing.wordpress.com +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Now, your challange is to find a hidden password using only this file and another software that record potentialy malicious behaviour. You can use for example SysInternals that can be downloaded from this link. https://download.sysinternals.com/files/SysinternalsSuite.zip File is obfuscated (same as Reverse Me! 4), so using casual Reverse Engineering software probably won't help you WARNING: Wrong command usage will close terminal TIP: Use filesystem and registry watching software Type command (type help for command list): help command list: bomb - returns sound and boom string close - exit app cd - as in cmd/powershell print|<string> - sends string and returns with the same flst|<optional_directory> - list files of current directory or in spec. one run|<file directory and name> - runs file clr - clears command prompt pass - test password pass Enter password: wazzup Well done!
Comments
Keywords: crackme dotnet camed reverse-me