Writeup: FlareOn 2022: 006 - à la mode
1. TLDR
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:
oraz 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:
- Wystarczającą obecność wyłącznie jednego pliku
- Brak większej ilości kodu w C#, widocznego w dnSpy
- 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:
Odczytując adres powrotu ze stosu 100010DA
, wróciłem w miejsce wywołania funkcji CreateFileW
, tj. pod adres 100010D4
:
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:
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:
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