BND Reversing Challenge – Level 1

Das BND (Bundesnachrichtendienst) aus Deutschland hat eine Reversing Challenge mit drei Levels veröffentlicht. Ziel ist es, passende Talente zu finden:

Um Ihnen die Möglichkeit zu geben uns von Ihrer Qualifikation zu überzeugen, haben wir drei Aufgaben vorbereitet, die gestaffelt nach unterschiedlichen Schwierigkeitsgraden Ihre Analysefähigkeiten in diesem Bereich testen.

Ich habe zwar überhaupt keine Absicht für den BND in Deutschland zu Arbeiten, aber die Challenge habe ich mir trotz allem mal angeschaut und möchte hier mal kurz meine Lösung zum Level 1 vorstellen:

In Level 1 zielt das Spielbeispiel auf eine Ransomware ab. Hier bekommt man ein PE „evil.exe“ und ein Bild „Urlaubsphoto.png.crypt“. Um nun nicht einfach im blauen fischen zu müssen, und eine mögliche Verschlüsselung des Bildes zu erraten, schaut man sich zuerst mal die evil.exe an. Mit einem kurzen „strings“ auf das PE bekommt auch bereits erste hilfreiche Infos zur Art der Erstellung:

Synchronized
competition2.Form1.resources
competition2.Properties.Resources.resources
3System.Resources.Tools.StronglyTypedResourceBuilder
4.0.0.0
KMicrosoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator
11.0.0.0
.NETFramework,Version=v4.5
FrameworkDisplayName
.NET Framework 4.5
competition2
Copyright 
  2016

Hier wird klar, dass es sich um eine .NET Applikation mit Visual Studio handeln muss. Also kann man sich super mit einem C#/.NET-Decompiler ans Werk machen. Bei mir war das z.B. ILSpy:

Selection_008

Wenn man den Code nun so durchschaut, so sieht man die folgende „Hauptmethode“:

private void button1_Click(object sender, EventArgs e)
{
	string text = this.textBox2.Text.Trim();
	if (text.Length < 3)
	{
		MessageBox.Show("Erst Code eingeben!");
		return;
	}
	if (!this.callCthulhu(text))
	{
		MessageBox.Show("Ohne gültigen Code geht hier nix weiter.");
		return;
	}
	OpenFileDialog openFileDialog = new OpenFileDialog();
	openFileDialog.Filter = "Crypted files (*.crypt)|*.crypt";
	if (openFileDialog.ShowDialog() == DialogResult.OK)
	{
		try
		{
			byte[] chi = File.ReadAllBytes(openFileDialog.FileName);
			byte[] bytes = this.invertGravity(chi);
			File.WriteAllBytes(openFileDialog.FileName.Substring(0, openFileDialog.FileName.Length - 6), bytes);
			MessageBox.Show("Okay, ist entschlüsselt.");
		}
		catch (Exception ex)
		{
			MessageBox.Show("Fehler! Und Du bist schuld: " + ex.Message);
		}
	}
}

Hier sieht man, dass erst der Input geprüft wird und dann eine Methode „callCthulhu“ aufgerufen wird, welcher die Überprüfung macht. Wenn alles stimmt, so kommt die Methode „invertGravity“ zum Einsatz, welche die Entschlüsselung zu handeln scheint.

Beginnen wir doch mal bei der Überprüfung:

private bool callCthulhu(string serial)
{
	bool result;
	try
	{
		TcpClient tcpClient = new TcpClient("127.0.0.1", 1337);
		byte[] bytes = Encoding.ASCII.GetBytes(serial);
		NetworkStream stream = tcpClient.GetStream();
		stream.Write(bytes, 0, bytes.Length);
		byte[] array = new byte[1024];
		string text = string.Empty;
		int count = stream.Read(array, 0, array.Length);
		text = Encoding.ASCII.GetString(array, 0, count);
		stream.Close();
		tcpClient.Close();
		bool flag = text.Trim().Equals(this.installLinux());
		result = flag;
	}
	catch (Exception ex)
	{
		MessageBox.Show("Kann mich nicht zu meinem Server verbinden.\nFehlermeldung: " + ex.Message);
		result = false;
	}
	return result;
}

Hier scheint man zu versuchen, sich auf 127.0.0.1:1337 zu verbinden, was natürlich nicht funktionieren wird, da die Applikation selbst nirgends einen Server startet. Ist aber auch gar nicht nötig, wir können das ja auch einfach auslassen 🙂

in Frage 1 müssen wir beantworten, was der Server nach erfolgreicher Verschlüsselung zurück gibt. Dies führt uns zu der Methode „installLinux“:

private string installLinux()
{
	string text = "The iron bank will have its due";
	return string.Concat(new string[]
	{
		text.Substring(0, 1).Replace('q', 'm').Replace('T', 'A').Replace('e', 'd').Replace('v', 's').Replace('r', 'L').Replace('i', 'a'),
		text.Substring(3, 1),
		text.Substring(13, 1).Replace('i', 'k').Replace('A', 'k').Replace('n', 'q').Replace(' ', 'L').Replace('y', 'd').Replace('v', 'f').Replace('m', 'a').Replace('m', 'e'),
		text.Substring(10, 1),
		text.Substring(7, 1),
		text.Substring(7, 1),
		text.Substring(4, 1),
		text.Substring(26, 1),
		text.Substring(25, 1),
		text.Substring(2, 1),
		text.Substring(5, 1),
		text.Substring(3, 1),
		text.Substring(10, 1),
		text.Substring(16, 1),
		text.Substring(14, 1),
		text.Substring(10, 1),
		text.Substring(5, 1).Replace('c', 'l').Replace('m', 'v').Replace('d', 'o').Replace('k', 'd').Replace('r', 'y').Replace('u', 'A').Replace('g', 'm'),
		text.Substring(26, 1),
		text.Substring(3, 1),
		text.Substring(13, 1).Replace('u', 's').Replace('q', 'A').Replace('k', 'a').Replace('f', 'g').Replace(' ', 'p').Replace('A', 'o').Replace('A', 'r').Replace('m', 'b'),
		text.Substring(10, 1),
		text.Substring(4, 1).Replace('g', 'k').Replace('h', 'p').Replace('i', 'y').Replace('f', 'x').Replace('t', 'g').Replace('j', ' ').Replace(' ', 'b'),
		text.Substring(26, 1),
		text.Substring(3, 1),
		text.Substring(1, 1),
		text.Substring(4, 1),
		text.Substring(26, 1),
		text.Substring(3, 1),
		text.Substring(28, 1),
		text.Substring(2, 1),
		text.Substring(9, 1),
		text.Substring(25, 1),
		text.Substring(26, 1)
	});
}

Ist ja schon fast zu einfach: Einfach die Methode herauskopieren und ausführen:

A Lannister always pays his depts

Super, nun wollen wir aber noch unsere Datei entschlüsselt haben, also schauen wir uns die Entschlüsselungsmethode an:

private byte[] invertGravity(byte[] chi)
{
	if (chi.Length % 2 != 0)
	{
		throw new Exception("Verschlüsselte Datei hat falsche Länge oder so.");
	}
	byte[] array = new byte[chi.Length / 2];
	for (int i = 0; i < array.Length; i++)
	{
		array[i] = Math.Min(chi[i * 2], chi[i * 2 + 1]);
	}
	return array;
}

Natürlich kann man einfach in C# auch diese Methode auf die Datei anwenden, doch das war mir irgendwie zu langweilig… Ausserdem bin ich gerade unter Linux unterwegs und wollte die Gelegenheit nutzen, das neu veröffentlichte Powershell unter Linux auszuprobieren. Also habe ich mich daran gemacht, die Statements in Powershell zu übersetzen.
Zuerst brauchen wir ein Byte-Array mit dem verschlüsselten Bild und dessen Länge:

[byte[]]$bytes = Get-Content ./Urlaubsphoto.png.crypt -Encoding Byte
echo $bytes.Length

Nun wissen wir, dass Array ist 101706 Bytes lang, also erstellen wir ein neues Array mit der Hälfte der Grösse:

$array = New-Object Byte[] 50853

Nun wenden wir die Decrypt-Funktion an und schreiben alles in ein Bild raus:

for ($i=0; $i -lt $array.Length; $i++) { 
	$array[$i] = [math]::min($bytes[$i*2], $bytes[$i*2+1])
}
[io.file]::WriteAllBytes('out.png', $array)

Et voilà, raus kommt das gewünschte Bild:

out

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.