Writeup: FlareOn 2022: 006 - à la mode

Task description

1. TLDR

TLDR graph

2. Dane wejściowe

Plik z zadaniem znajduje się tutaj. Hasło: flare.

Przedmiotem zadania były pliki PE oraz txt:

HowDoesThisWork.dll
'IR chat log.txt'

3. Treść pliku txt

W pierwszej kolejności podejrzałem zawartość pliku tekstowego:

[FLARE Team]  Hey IR Team, it looks like this sample has some other binary that might 
              interact with it, do you have any other files that might be of help.
			  
[IR Team]     Nope, sorry this is all we got from the client, let us know what you got.

4. Analiza pliku jako pliku .NET Assembly

Zweryfikowałem typ pliku:

$ file HowDoesThisWork.dll
HowDoesThisWork.dll: PE32 executable (DLL) (GUI) Intel 80386 Mono/.Net assembly, for MS Windows

Widząc, że plik został przygotowany w technologii .NET, przeanalizowałem wstępnie plik wykorzystując proste narzędzia Detect-It-Easy:

die

oraz exeinfo:

exeinfo

Oprócz nietypowego entry pointa (0x181A), programy nie zwróciły istotnych informacji.

Korzystając z narzędzia CFF Explorer sprawdziłem wstępnie czy nie istnieją jakieś zasoby, o których warto pamiętać w trakcie analizy. Na tym etapie nie zidentyfikowałem takich zasobów.

Korzystając z dnSpyEx zdekompilowałem plik:

using System;
using System.IO.Pipes;
using System.Text;

namespace FlareOn
{
	// Token: 0x02000002 RID: 2
	public class Flag
	{
		public string GetFlag(string password)
		{
			Decoder decoder = Encoding.UTF8.GetDecoder();
			UTF8Encoding utf8Encoding = new UTF8Encoding();
			string text = "";
			byte[] array = new byte[64];
			char[] array2 = new char[64];
			byte[] bytes = utf8Encoding.GetBytes(password + "\0");
			using (NamedPipeClientStream namedPipeClientStream = new NamedPipeClientStream(".", "FlareOn", PipeDirection.InOut))
			{
				namedPipeClientStream.Connect();
				namedPipeClientStream.ReadMode = PipeTransmissionMode.Message;
				namedPipeClientStream.Write(bytes, 0, Math.Min(bytes.Length, 64));
				int num = namedPipeClientStream.Read(array, 0, array.Length);
				int chars = decoder.GetChars(array, 0, num, array2, 0);
				text += new string(array2, 0, chars);
			}
			return text;
		}
	}
}

Niestety, to był cały kod napisany w C#…

Z kodu w C# wiedziałem, że nawiązywane jest połączenie do Named Pipe FlareOn. Nie wiedziałem natomiast co go tworzy… Uruchomiłem zatem próbkę w x32dbg z myślą, że może coś przeoczyłem. Być może ktoś wykonał patch pliku .NET Assembly kodem x86? To by tłumaczyło:

  1. Wystarczającą obecność wyłącznie jednego pliku
  2. Brak większej ilości kodu w C#, widocznego w dnSpy
  3. Informację o nietypowym entry point’cie.

5. Analiza pliku jako pliku PE

Uruchomienie pliku dll można było zrealizować np. w wyniku wywołania pliku rundll32.exe. Z uwagi na 32-bitową dll należało wskazać również 32-bitową wersję rundll32.exe. Plik posiadał wyłącznie Entry Point bez dodatkowych eksportów. Uruchomienie zatem mogło wyglądać jak poniżej:

>C:\Windows\SysWOW64\rundll32.exe ".\HowDoesThisWork.dll,0"

Po załadowaniu pliku do środowiska IDA, przez chwilę byłem przytłoczony liczbą funkcji. Uznałem, że większość z nich pochodzi z narzutu kodu wygenerowanego w trakcie kompilacji kodu napisanego w C#. Jeżeli plik powinien tworzyć Named Pipe, to powinien był wywołać funkcję CreateFileW. Załadowałem zatem plik do x32dbg i podałem polecenie dodające breakpoint do tej funkcji:

bp CreateFileW

Po uruchomieniu pliku, po kilkunastokrotnym zatrzymaniu się na breakpoint’cie, uchwyciłem moment tworzenia pliku:

x32dbg-CreateFileW-pipe

Odczytując adres powrotu ze stosu 100010DA , wróciłem w miejsce wywołania funkcji CreateFileW, tj. pod adres 100010D4:

x32dbg-call-CreateFileW-pipe

Znając miejsce tworzenia Named Pipe w kodzie programu, wróciłem do środowiska IDA i przeszedłem pod ten sam adres.

Kilka instrukcji wyżej ujawniłem funkcję dekodującą ciągi znaków poprzez xor 0x17:

ida-mw_load_string

W ten sposób ujawniłem ładowane i wykorzystywane funkcje:

CloseHandle
ConnectNamedPipe
CreateNamedPipeA
CreateThread
DisconnectNamedPipe
FlushFileBuffers
GetLastError
GetProcessHeap
lstrcmpA
ReadFile
WriteFile

Kontynuując analizę, trafiłem na funkcję mw_process_buffer , która przetwarzała bufor danych z NamedPipe:

ida-mw_process_buffer

Analiza funkcji mw_process_buffer pozwoliła ujawnić fakt, że hasło i flaga zostały zaszyfrowane z wykorzystaniem algorytmu rc4:

key = 55 8B EC 83 EC 20 EB FE 
password = 3E 39 51 FB A2 11 F7 B9 2C
flag = E1 60 A1 18 93 2E 96 AD 73 BB 4A 92 DE 18 0A AA  41 74 AD C0 1D 9F 3F 19 FF 2B 02 DB D1 CD 1A 

6. Odczytanie flagi

Wystarczyło zatem odszyfrować hasło i flagę. W tym celu napisałem kod w Pythonie:

import malduck

key = b'\x55\x8B\xEC\x83\xEC\x20\xEB\xFE'
password = b'\x3E\x39\x51\xFB\xA2\x11\xF7\xB9\x2C'
flag = b'\xE1\x60\xA1\x18\x93\x2E\x96\xAD\x73\xBB\x4A\x92\xDE\x18\x0A\xAA\x41\x74\xAD\xC0\x1D\x9F\x3F\x19\xFF\x2B\x02\xDB\xD1\xCD\x1A'

ciphertext = password+flag
plaintext = malduck.rc4(key, ciphertext).decode('latin1')
print(plaintext)
MyV0ic3! M1x3d_M0dE_4_l1f3@flare-on.com

Zatem hasło to:

MyV0ic3!

… natomiast flaga to:

M1x3d_M0dE_4_l1f3@flare-on.com