Writeup: FlareOn 2022: 005 - T8
1. TLDR
2. Input data
The challenge file is here. Password: flare.
The subjects of the task were PE and pcapng files:
t8.exe
traffic.pcapng
3. Analysis of the pcapng file
I first listed the protocols used:
> tshark -r .\traffic.pcapng -T fields -e frame.protocols | sort | uniq
eth:ethertype:ip:tcp
eth:ethertype:ip:tcp:http:data
I then listed communications using the HTTP protocol:
> 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
I extracted two communication sequences (below). Decoding the data using base64 yielded no intelligible results.
3.1. Sequence #1
Retrieved content of the request and response below:
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. Sequence # 2
Retrieved content of the request and response below:
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. Initial analysis of the PE file
I verified the file type t8.exe
:
$ file t8.exe
t8.exe: PE32 executable (console) Intel 80386, for MS Windows
Its startup, however, did not indicate any interaction with the user. The process, however, was running all the time….
I analyzed the program with the capa tool:
+------------------------+------------------------------------------------------------------------------------+
| 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 |
+------------------------------------------------------+------------------------------------------------------+
So I expected to run shellcode, network interaction, encryption, MD5 hashing, and base64
I loaded the program into the IDA environment
5. Code analysis
At the beginning of the analysis I noticed a loop call with the Sleep function, This was one of the first activities after the main function was started.
I made a patch of the jz conditional jump statement:
per jmp unconditional jump instruction:
Then … I noticed an import of the KERNEL32.OutputDebugStringW function. After running the program, before its operation was completed, the program actually logged the function names.
I then noticed a number of imported functions related to establishing http connections:
At the same time, I disclosed a suspicious piece of code that was running code from a newly allocated memory area:
So I prepared a configuration file for the tiny_tracer tool:
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
Armed with the knowledge I gained, I proceeded with a static analysis of the code. In doing so, I determined the following facts:
- the program was building a string that was used to generate the key
- the string always had 8 characters: a fixed beginning “F09” followed by 5 digits (which were probably chosen pseudo-randomly)
- the generated “seed” (after encoding using UTF-16LE) was the input for the MD5 algorithm.
- the result of the hash function (after encoding using UTF-16LE) was the key, which was used to encrypt the “ahoy” string (encoded using UTF-16LE)
- the ciphertext encoded using base64 was sent to the HTTP server.
- the received response was base64 encoded and encrypted with the same key.
6. Key recovery
So I decided to develop a code that would determine the correct seed and key. A brute-force search of the space generated by the 5 digits was sufficient:
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}")
After a split second, I saw the answer:
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='
I then decided to decrypt the message recovered from the pcapng file:
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))
Unfortunately, I didn’t notice anything special on the standard output, which realistically at that moment would have allowed to read the flag.
7. Reading the flag
I then ran the program and, under the control of the debugger, changed the string in memory that forms the basis for determining the key. Next, I prepared the substituted http server. To do this, I used fakedns and the code for a simple http server that generated a response with the ciphertext:
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()
Going through the program’s functions and substituting the string that is the “seed” a few more times, I saw a flag prefix in memory:
Thus, the flag is:
i_s33_you_m00n@flare-on.com