Reversing: Tear or Dear
Tear Or Dear is an easy reversing challenge on Hack The Box
. The description of the challenge is as follows:
Find the username and password and put them in the flag in the format: HTB{username:password}.
Warning: It can produce false positives.
The warning is somewhat ominous as a challenge that has multiple win-paths can be daunting.
First thing to do after downloading the zip archive and extracting it with 7z
is to run file
on the binary.
TearORDear.exe: PE32 executable (GUI) Intel 80386 Mono/.Net assembly, for MS Windows
Great, it is a Mono/.Net
assembly. Running it on Kali (which some Mono binaries can) results in a very nice error message:
Unhandled Exception:
System.IO.FileNotFoundException: Could not load file or assembly 'System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' or one of its dependencies.
File name: 'System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'
[ERROR] FATAL UNHANDLED EXCEPTION: System.IO.FileNotFoundException: Could not load file or assembly 'System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' or one of its dependencies.
File name: 'System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'
The binary requires the System.Windows.Forms
library. We could try running it under Wine
, but the toolchain to work with these type of binaries is better on Windows, so lets load it up on a Commando VM
(Github) or Flare VM
(Github) machine. I happen to have a Commando VM
handy, so I will use that.
The tool that I turn to when working with .Net
assemblies is dnSpy
(Github). The tool is not on Commando VM
by default, so a quick choco install dnspy
in an elevated command shell will get it installed.
Start dnSpy
and add the TearORDear.exe
to it. The application will start to decompile it and show it on the left hand side of the screen.
Navigate through the treestructure until you find Program
. This is the main entry point of the application.
using System;
using System.Windows.Forms;
namespace TearORDear
{
// Token: 0x02000003 RID: 3
internal static class Program
{
// Token: 0x06000014 RID: 20 RVA: 0x0000335D File Offset: 0x0000155D
[STAThread]
private static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new LoginForm());
}
}
}
From the code it is clear that the Program
quickly hands over control to LoginForm
. Scanning through that code it is clear that it is the place to be. It has lots of encrypt and check functions. The function that starts everything seems to be button1_Click
, which is a quite standard practice when dealing with forms. The code for this function is listed below.
private void button1_Click(object sender, EventArgs e) {
this.label_Result.Text = "";
this.kapa(sender, e);
this.pep = 0;
this.aa = this.Multiply(this.encrypted1(this.textBox_user.Text).Substring(0, 5), -1);
this.aa = this.aa.Remove(this.aa.Length - 1);
string s = this.Multiply(this.oura, -9);
if (this.username == this.o && this.check1(s))
{
MessageBox.Show("Correct!");
return;
}
this.label_Result.Text = "WRONG CREDENTIALS! Try Again...";
}
As the description said, the goal is to find a username/password combination. The cool thing of dnSpy
is that it has a debugger. Set a breakpoint on line 298
, which is the if
statement in the above code and run it. Just right click the line and choose Add breakpoint
.
If dnSpy
throws an error when starting the debugger, as in the screenshot below, restart using dnspy-x86
. This will load up the 32 bit version of dnspy
When you have set the breakpoint and hit Start
the main program should show up.
The program has a field for username and one for password. As a first go enter username
for the Username and password
for the Password field. This will hopefully make it clear in the debugger what is expected of the input.
When the breakpoint is hit dnSpy
will show a list of local variables. The code active in the breakpoint is reproduced below. Apparently the this.username
is compared to this.o
and then a check is done on the value of s
.
if (this.username == this.o && this.check1(s))
{
MessageBox.Show("Correct!");
return;
}
In order to see what is happening I like to add the most interesting variables as Watches
. This means dnSpy
keeps the variables in a separate window and any change can be directly seen. As you might’ve seen, the this
object is quite large and thus changes are very hard to see.
Lookup the this.username
field in the locals treeview, right click it and click Add watch
. Do the same for this.o
, this.oura
and this.aa
(as it is used just before the if statement). The value of s
is the last one to be watched, it is not part of this
, but is listed separately. The Watch 1
list looks like this now:
Immediately several things stand out; this.username
now contains the value of Password
and is compared to the value of this.o
which is ‘roiw!@#
’. Hit F11
to step into the expression; the next expression will be the failed case.
Restart the debugger session (hit the circle with the arrow in it) and add username
for username and roiw!@#
as the password and hit the login button again.
The Watch 1
now shows that when hitting the breakpoint the value of this.username
and this.o
are the same. The first part of the if statement will now result in a true
condition, so when hitting F11
, Step Into, the next step should be to go to this.check1(s)
.
Looking at the code it will go and perform an array operation and then call this.check2()
using the result of the array operation. Hit F10
twice to see the result of the array calls.
The locals show that the value of s2
at the end of the check1(s)
function is roiw
, which is part of our current password as well. Hit F11
to jump into the next check2()
function.
Use F10
to once again jump over the array operations until the return
statement. Again the value from the array is roiw
. Use F11
to jump into check3()
. Repeat this process to jump into check4()
and again to jump into check()
.
The operation of check()
is different from the other check functions. The return statement sheds a lot of light on the usage of this.aa
from our entry function.
return this.textBox_user.Text == this.aa && array[0] == array[22];
As the value is already in the watch list, examine it to see what the program wants to have filled in as the username.
At this point it is clear that the username should be piph
and the password is roiw!@#
. Restart the debugger and enter them to give it a try. A messagebox will pop up to say the values are correct.
Head on over to hack the box to enter the flag as per the instructions from the challenge description.