Writeup: FlareOn 2022: 005 - T8
1. TLDR
2. Dane wejściowe
Plik z zadaniem znajduje się tutaj. Hasło: flare.
Przedmiotem zadania były pliki PE oraz pcapng:
t8.exe
traffic.pcapng
3. Analiza pliku pcapng
W pierwszej kolejności wylistowałem wykorzystywane protokoły:
> tshark -r .\traffic.pcapng -T fields -e frame.protocols | sort | uniq
eth:ethertype:ip:tcp
eth:ethertype:ip:tcp:http:data
Następnie wylistowałem komunikację z wykorzystaniem protokołu HTTP:
> tshark -r .\traffic.pcapng -Y "http"
5 0.000725 192.168.10.15 → 13.13.37.33 HTTP 78 POST / HTTP/1.1
9 0.016663 13.13.37.33 → 192.168.10.15 HTTP 390 HTTP/1.0 200 OK
17 0.019410 192.168.10.15 → 13.13.37.33 HTTP 70 POST / HTTP/1.1
21 0.020672 13.13.37.33 → 192.168.10.15 HTTP 218 HTTP/1.0 200 OK
Wyodrębniłem dwie sekwencje komunikacji (poniżej). Odkodowanie danych z wykorzystaniem base64 nie przyniosło żadnych zrozumiałych rezultatów.
3.1. Sekwencja nr 1
Odzyskana treść żądania i odpowiedzi poniżej:
POST / HTTP/1.1
Connection: Keep-Alive
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; 11950)
Content-Length: 24
Host: flare-on.com
y.d.N.8.B.X.q.1.6.R.E.=.
HTTP/1.0 200 OK
Server: Apache On 9
Date: Tue, 14 Jun 2022 16:14:36 GMT
TdQdBRa1nxGU06dbB27E7SQ7TJ2+cd7zstLXRQcLbmh2nTvDm1p5IfT/Cu0JxShk6tHQBRWwPlo9zA1dISfslkLgGDs41WK12ibWIflqLE4Yq3OYIEnLNjwVHrjL2U4Lu3ms+HQc4nfMWXPgcOHb4fhokk93/AJd5GTuC5z+4YsmgRh1Z90yinLBKB+fmGUyagT6gon/KHmJdvAOQ8nAnl8K/0XG+8zYQbZRwgY6tHvvpfyn9OXCyuct5/cOi8KWgALvVHQWafrp8qB/JtT+t5zmnezQlp3zPL4sj2CJfcUTK5copbZCyHexVD4jJN+LezJEtrDXP1DJNg==
3.2. Sekwencja nr 2
Odzyskana treść żądania i odpowiedzi poniżej:
POST / HTTP/1.1
Connection: Keep-Alive
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; CLR)
Content-Length: 16
Host: flare-on.com
V.Y.B.U.p.Z.d.G.
HTTP/1.0 200 OK
Server: Apache On 9
Date: Tue, 14 Jun 2022 16:14:36 GMT
F1KFlZbNGuKQxrTD/ORwudM8S8kKiL5F906YlR8TKd8XrKPeDYZ0HouiBamyQf9/Ns7u3C2UEMLoCA0B8EuZp1FpwnedVjPSdZFjkieYqWzKA7up+LYe9B4dmAUM2lYkmBSqPJYT6nEg27n3X656MMOxNIHt0HsOD0d+
4. Analiza wstępna pliku PE
Zweryfikowałem typ pliku t8.exe
:
$ file t8.exe
t8.exe: PE32 executable (console) Intel 80386, for MS Windows
Jego uruchomienie jednak nie wskazywało na jakąkolwiek interakcję z użytkownikiem. Proces jednak działał cały czas…
Przeanalizowałem program narzędziem capa:
+------------------------+------------------------------------------------------------------------------------+
| md5 | fc5051e15de9ad7fbeeded225f8e7997 |
| sha1 | d68eede38c42fc26d7f0385c1f717f889fb3bcea |
| sha256 | 06144f641616519805b7cb6f215347967a9f01da50aa83aa155d5bacaffcbdf8 |
| path | t8.exe |
+------------------------+------------------------------------------------------------------------------------+
+------------------------+------------------------------------------------------------------------------------+
| ATT&CK Tactic | ATT&CK Technique |
|------------------------+------------------------------------------------------------------------------------|
| DEFENSE EVASION | Obfuscated Files or Information [T1027] |
| DISCOVERY | System Information Discovery [T1082] |
| EXECUTION | Command and Scripting Interpreter [T1059] |
| | Shared Modules [T1129] |
+------------------------+------------------------------------------------------------------------------------+
+-----------------------------+-------------------------------------------------------------------------------+
| MBC Objective | MBC Behavior |
|-----------------------------+-------------------------------------------------------------------------------|
| COMMUNICATION | HTTP Communication::Create Request [C0002.012] |
| | HTTP Communication::Get Response [C0002.017] |
| | HTTP Communication::WinHTTP [C0002.008] |
| CRYPTOGRAPHY | Encrypt Data::RC4 [C0027.009] |
| | Encryption Key::RC4 KSA [C0028.002] |
| | Generate Pseudo-random Sequence::RC4 PRGA [C0021.004] |
| DATA | Check String [C0019] |
| | Encoding::Base64 [C0026.001] |
| | Encoding::XOR [C0026.002] |
| | Non-Cryptographic Hash::FNV [C0030.005] |
| DEFENSE EVASION | Obfuscated Files or Information::Encoding-Standard Algorithm [E1027.m02] |
| FILE SYSTEM | Write File [C0052] |
| MEMORY | Allocate Memory [C0007] |
| OPERATING SYSTEM | Environment Variable::Set Variable [C0034.001] |
| PROCESS | Allocate Thread Local Storage [C0040] |
| | Set Thread Local Storage Value [C0041] |
| | Terminate Process [C0018] |
+-----------------------------+-------------------------------------------------------------------------------+
+------------------------------------------------------+------------------------------------------------------+
| CAPABILITY | NAMESPACE |
|------------------------------------------------------+------------------------------------------------------|
| initialize WinHTTP library | communication/http |
| prepare HTTP request | communication/http/client |
| receive HTTP response | communication/http/client |
| encode data using Base64 (2 matches) | data-manipulation/encoding/base64 |
| reference Base64 string | data-manipulation/encoding/base64 |
| encode data using XOR (2 matches) | data-manipulation/encoding/xor |
| encrypt data using RC4 KSA | data-manipulation/encryption/rc4 |
| encrypt data using RC4 PRGA | data-manipulation/encryption/rc4 |
| hash data using fnv | data-manipulation/hashing/fnv |
| hash data with MD5 | data-manipulation/hashing/md5 |
| contain a resource (.rsrc) section | executable/pe/section/rsrc |
| accept command line arguments | host-interaction/cli |
| query environment variable | host-interaction/environment-variable |
| set environment variable | host-interaction/environment-variable |
| write file (5 matches) | host-interaction/file-system/write |
| print debug messages (2 matches) | host-interaction/log/debug/write-event |
| allocate thread local storage (2 matches) | host-interaction/process |
| get thread local storage value (2 matches) | host-interaction/process |
| set thread local storage value (2 matches) | host-interaction/process |
| allocate RWX memory | host-interaction/process/inject |
| terminate process (3 matches) | host-interaction/process/terminate |
| terminate process via fastfail (7 matches) | host-interaction/process/terminate |
| access PEB ldr_data | linking/runtime-linking |
| link function at runtime (2 matches) | linking/runtime-linking |
| link many functions at runtime | linking/runtime-linking |
| parse PE header (4 matches) | load-code/pe |
+------------------------------------------------------+------------------------------------------------------+
Spodziewałem się zatem uruchomienia shellcode’u, interakcji sieciowej, szyfrowania, skrótu MD5 oraz base64
Załadowałem program do środowiska IDA
5. Analiza kodu
Na początku analizy zauważyłem wywołanie pętli z funkcją Sleep, Była to jedna z pierwszych aktywności po uruchomieniu funkcji main.
Wykonałem patch instrukcji skoku warunkowego jz:
na instrukcję skoku bezwarunkowego jmp:
Następnie… zauważyłem zaimportowanie funkcji KERNEL32.OutputDebugStringW
.
Po uruchomieniu programu, zanim jego działanie zostało zakończone, program faktycznie logował… nazwy funkcji.
Zauważyłem następnie szereg importowanych funkcji związanymi z nawiązywaniem połączeń http:
Ujawniłem jednocześnie podejrzany fragment kodu, który uruchamiał kod z nowozaalkowanego obszaru pamięci:
Przygotowałem zatem plik konfiguracyjny do narzędzia tiny_tracer:
kernel32;LoadLibraryW;1
kernel32;LoadLibraryA;1
kernel32;GetProcAddress;2
advapi32;RegQueryValueW;3
kernel32;CreateFileW;6
kernel32;CreateProcessInternalW;12
winhttp;WinHttpQueryDataAvailable;2
winhttp;WinHttpReceiveResponse;2
winhttp;WinHttpOpen;5
winhttp;WinHttpReadData;4
winhttp;WinHttpOpenRequest;7
winhttp;WinHttpCloseHandle;3
winhttp;WinHttpSendRequest;7
winhttp;WinHttpConnect;4
urlmon;ObtainUserAgentString;3
Uzbrojony w zdobytą wiedzę przystąpiłem do analizy statycznej kodu. Ustaliłem w ten sposób następujące fakty:
- Program budował ciąg znaków, który wykorzystywany był do generowania klucza
- Ciąg znaków miał zawsze 8 znaków: stały początek “F09” a następnie 5 cyfr (które prawdopodobnie był wybierane w sposób pseudolosowy)
- Wygenerowane “ziarno” (po zakodowaniu z wykorzystaniem UTF-16LE) stanowiło wejście dla algorytmu MD5.
- Wynik funkcji skrótu (po zakodowaniu z wykorzystaniem UTF-16LE) stanowił klucz, który służył do zaszyfrowania ciągu “ahoy” (zakodowanego z wykorzystaniem UTF-16LE)
- Szyfrogram zakodowany z wykorzystaniem base64 był przesyłany na serwer HTTP.
- Otrzymana odpowiedź była zakodowana w base64 i zaszyfrowana tym samym kluczem.
6. Odzyskanie klucza
Postanowiłem zatem opracować kod, który wyznaczyłby prawidłowe ziarno i klucz. Wystarczyło przeszukać brutalnie przestrzeń wygenerowaną przez 5 cyfr:
import itertools
import string
import malduck
from pprint import pprint
expected_b64 = b"ydN8BXq16RE="
known_seed="FO913390"
seed_prefix = "FO9"
plaintext = "ahoy".encode("UTF-16LE")
for c in itertools.product(string.digits, repeat=len(known_seed)-len(seed_prefix)):
seed_cand = seed_prefix +''.join(c)
seed_cand_w = seed_cand.encode("UTF-16LE")
seed_cand_hash = malduck.md5(seed_cand_w).hex()
key = seed_cand_hash.encode("UTF-16LE")
ciphertext = malduck.rc4(key,plaintext)
curr_b64 = malduck.base64.encode(ciphertext)
if curr_b64 == expected_b64:
break
print(f"seed_cand: {seed_cand}")
print(f"seed_cand_hash: {seed_cand_hash}")
print(f"key: {key}")
print(f"curr_b64: {curr_b64}")
Po ułamku sekundy zobaczyłem odpowiedź:
seed_cand: FO911950
seed_cand_hash: a5c6993299429aa7b900211d4a279848
key: b'a\x005\x00c\x006\x009\x009\x003\x002\x009\x009\x004\x002\x009\x00a\x00a\x007\x00b\x009\x000\x000\x002\x001\x001\x00d\x004\x00a\x002\x007\x009\x008\x004\x008\x00'
curr_b64: b'ydN8BXq16RE='
Postanowiłem następnie odszyfrować wiadomość odzyskaną z pliku pcapng:
message_1_ciphertext = "TdQdBRa1nxGU06dbB27E7SQ7TJ2+cd7zstLXRQcLbmh2nTvDm1p5IfT/Cu0JxShk6tHQBRWwPlo9zA1dISfslkLgGDs41WK12ibWIflqLE4Yq3OYIEnLNjwVHrjL2U4Lu3ms+HQc4nfMWXPgcOHb4fhokk93/AJd5GTuC5z+4YsmgRh1Z90yinLBKB+fmGUyagT6gon/KHmJdvAOQ8nAnl8K/0XG+8zYQbZRwgY6tHvvpfyn9OXCyuct5/cOi8KWgALvVHQWafrp8qB/JtT+t5zmnezQlp3zPL4sj2CJfcUTK5copbZCyHexVD4jJN+LezJEtrDXP1DJNg=="
message_1_plaintext = malduck.rc4(key, malduck.base64.decode(message_1_ciphertext))
Niestety, na standardowym wyjściu nie zauważyłem niczego szczególnego, co realnie w tamtym momencie pozwoliłoby odczytać flagę.
7. Odczytanie flagi.
Uruchomiłem następnie program i pod kontrolą debuggera zmieniłem w pamięci ciąg znaków stanowiących podstawę do wyznaczenia klucza. Następnie przygotowałem podstawiony serwer http. W tym celu wykorzystałem fakedns oraz kod prostego serwera http, który generował odpowiedź z szyfrogramem:
import BaseHTTPServer
class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def do_POST(self):
print("Responding on POST")
content = "TdQdBRa1nxGU06dbB27E7SQ7TJ2+cd7zstLXRQcLbmh2nTvDm1p5IfT/Cu0JxShk6tHQBRWwPlo9zA1dISfslkLgGDs41WK12ibWIflqLE4Yq3OYIEnLNjwVHrjL2U4Lu3ms+HQc4nfMWXPgcOHb4fhokk93/AJd5GTuC5z+4YsmgRh1Z90yinLBKB+fmGUyagT6gon/KHmJdvAOQ8nAnl8K/0XG+8zYQbZRwgY6tHvvpfyn9OXCyuct5/cOi8KWgALvVHQWafrp8qB/JtT+t5zmnezQlp3zPL4sj2CJfcUTK5copbZCyHexVD4jJN+LezJEtrDXP1DJNg=="
self.send_response(200)
self.send_header("Content-Length", str(len(content)))
self.end_headers()
self.wfile.write(content)
if __name__ == '__main__':
serverAddress = ('', 80)
server = BaseHTTPServer.HTTPServer(serverAddress, RequestHandler)
server.serve_forever()
Przechodząc przez kolejne funkcje programu i podmieniając jeszcze kilkukrotnie ciąg znaków będący “ziarnem”, zobaczyłem w pamięci prefix flagi:
Zatem flaga to:
i_s33_you_m00n@flare-on.com