Writeup: FlareOn 2022: 005 - T8

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 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:

loop-jz-before-patch

na instrukcję skoku bezwarunkowego jmp:

loop-jz-after-patch

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.

debugview

Zauważyłem następnie szereg importowanych funkcji związanymi z nawiązywaniem połączeń http:

http-actions

Ujawniłem jednocześnie podejrzany fragment kodu, który uruchamiał kod z nowozaalkowanego obszaru pamięci:

shellcode-run

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:

  1. Program budował ciąg znaków, który wykorzystywany był do generowania klucza
  2. 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)
  3. Wygenerowane “ziarno” (po zakodowaniu z wykorzystaniem UTF-16LE) stanowiło wejście dla algorytmu MD5.
  4. 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)
  5. Szyfrogram zakodowany z wykorzystaniem base64 był przesyłany na serwer HTTP.
  6. 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:

shellcode-run

Zatem flaga to:

i_s33_you_m00n@flare-on.com