Write-up-camed-Reverse-Me

From aldeid
Jump to navigation Jump to search

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'
Note
The python output shows a double backslash to actually escape the second one. The password only contains one.

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:

  1. Open the crackme into IDA-Pro
  2. 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.

Note
You can apply the same patch for the second button though it won't be required to valide the crackme.

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