Writeup: Cyber Apocalypse CTF 2022 - Reflection
Last week we played the Cyber Apocalypse CTF 2022 - Intergalactic Chase with my team. This article serves as a writeup for the Reflection forensic challenge.
Pretty fun challenge and relevant to the previous articles on this blog. If you haven’t already, go take a look at them (PE format and especially Reflective loading). Enjoy!
The actual writeup
Introduction
As a quick introduction to the challenge, we get this few lines of text:
You and Miyuki have succeeded in dis-empowering Draeger’s army in every possible way. Stopped their fuel-supply plan, arrested their ransomware gang, prevented massive phishing campaigns and understood their tactics and techniques in depth. Now it is the time for the final blow. The final preparations are completed. Everyone is in their stations waiting for the signal. This mission can only be successful if you use the element of surprise. Thus, the signal must remain a secret until the end of the operation. During some last-minute checks you notice some weird behaviour in Miyuki’s PC. You must find out if someone managed to gain access to her PC before it’s too late. If so, the signal must change. Time is limited and there is no room for errors.
We download the challenge and start poking around.
$ ls -lah memory.raw
-rwxrwxrwx 1 skrix skrix 1.0G Apr 20 13:06 memory.raw
$ file memory.raw
memory.raw: data
It appears to be a memory dump. Let’s get cracking.
Analysis
To perform the analysis, I’ll be using the well known Volatility3. Clearly the best tool out there atm to perform memory analysis.
Let’s begin to gather information about the dump. As the challenge was called Reflection, I assumed the challenge was about Reflective loading (I wonder, where I could read more about that particular subject? cough cough..).
As we might be dealing about reflective loading in this challenge, I suspected the image to be a Windows memory dump. And in fact it is:
$ python3 vol.py -f ../memory.raw windows.info.Info
Volatility 3 Framework 2.2.0
Progress: 100.00 PDB scanning finished
Variable Value
Kernel Base 0x8281c000
DTB 0x185000
Is64Bit False
IsPAE True
layer_name 0 WindowsIntelPAE
memory_layer 1 FileLayer
KdDebuggerDataBlock 0x82947c68
NTBuildLab 7601.18939.x86fre.win7sp1_gdr.15
CSDVersion 1
KdVersionBlock 0x82947c40
Major/Minor 15.7601
MachineType 332
KeNumberProcessors 1
SystemTime 2022-04-20 11:06:10
NtSystemRoot C:\Windows
NtProductType NtProductWinNt
NtMajorVersion 6
NtMinorVersion 1
PE MajorOperatingSystemVersion 6
PE MinorOperatingSystemVersion 1
PE Machine 332
PE TimeDateStamp Wed Jul 22 16:46:25 2015
Where to now chief?
Well, according to my first huntch we are looking for an injected PE or DLL file somewhere. My first guess was to run malfind to see if anything stood out.
(The outputs of Volatility3’s commands can get fairly long, most of them in this article are truncated.)
According to the Volatilty3’s documentation:
The malfind command helps find hidden or injected code/DLLs in user mode memory, based on characteristics such as VAD tag and page permissions.
Scrolling through the results of malfind, some of the data immediately stands out as weird. Why?
python3 vol.py -f ../memory.raw windows.malfind.Malfind
[...]
3244 notepad.exe 0xb0000 0xb3fff VadS PAGE_EXECUTE_READWRITE 4 1 Disabled
00 00 90 00 03 00 00 00 ........
04 00 00 00 ff ff 00 00 ........
b8 00 00 00 00 00 00 00 ........
40 00 00 00 00 00 00 00 @.......
00 00 00 00 00 00 00 00 ........
00 00 00 00 00 00 00 00 ........
00 00 00 00 00 00 00 00 ........
00 00 00 00 d0 00 00 00 ........
0xb0000: add byte ptr [eax], al
0xb0002: nop
0xb0003: add byte ptr [ebx], al
0xb0005: add byte ptr [eax], al
0xb0007: add byte ptr [eax + eax], al
0xb000a: add byte ptr [eax], al
3244 notepad.exe 0x170000 0x170fff VadS PAGE_EXECUTE_READWRITE
53 89 e3 83 e4 f0 b9 00 S.......
00 0b 00 ba 01 00 00 00 ........
b8 00 00 00 00 50 52 51 .....PRQ
b8 50 12 0b 00 ff d0 89 .P......
dc 5b c3 00 00 00 00 00 .[......
00 00 00 00 00 00 00 00 ........
00 00 00 00 00 00 00 00 ........
00 00 00 00 00 00 00 00 ........
0x170000: push ebx
0x170001: mov ebx, esp
0x170003: and esp, 0xfffffff0
0x170006: mov ecx, 0xb0000
0x17000b: mov edx, 1
0x170010: mov eax, 0
0x170015: push eax
0x170016: push edx
0x170017: push ecx
0x170018: mov eax, 0xb1250
0x17001d: call eax
0x17001f: mov esp, ebx
0x170021: pop ebx
0x170022: ret
0x170023: add byte ptr [eax], al
[...]
Well, we have two memory ranges in notepad.exe that are flagged PAGE_EXECUTE_READWRITE. This is quite unusual. Having a memory space flagged as such could indicate injection in the process.
But that is not all.
If we take a look the disassembled asm code for the 0x170000 range. A few things stand out:
[...]
0x170000: push ebx
0x170001: mov ebx, esp
[...]
0x17001f: mov esp, ebx
0x170021: pop ebx
0x170022: ret
[...]
These few lines sure look like an asm function prologue and epilogue to me.
In assembly language programming, the function prologue is a few lines of code at the beginning of a function, which prepare the stack and registers for use within the function. Similarly, the function epilogue appears at the end of the function, and restores the stack and registers to the state they were in before the function was called.
We might be looking at some sort of malicous code here.
Another thing should have drawn your attention.
[...]
0x170018: mov eax, 0xb1250
0x17001d: call eax
[...]
The value 0xb1250 is moved into EAX and is called. Interesting. If you recall the malfind output, there is another memory section (0xb0000-0xb3fff) that is also PAGE_EXECUTE_READWRITE.
Let’s dump both sections using VadInfo (we could also have used malfind to acheive that):
python3 vol.py -f ../memory.raw windows.vadinfo.VadInfo --pid 3244 --dump --address 0xb0000
python3 vol.py -f ../memory.raw windows.vadinfo.VadInfo --pid 3244 --dump --address 0x170000
According to the function we just analysed, something tells me that what we are looking for is in the memory section @0xb0000.
$ file pid.3244.vad.0xb0000-0xb3fff.dmp
pid.3244.vad.0xb0000-0xb3fff.dmp: data
Nothing using the file command.
Quick hexdump to check what this file is:
$ hexdump -C pid.3244.vad.0xb0000-0xb3fff.dmp | head
00000000 00 00 90 00 03 00 00 00 04 00 00 00 ff ff 00 00 |................|
00000010 b8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 |........@.......|
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000030 00 00 00 00 00 00 00 00 00 00 00 00 d0 00 00 00 |................|
00000040 0e 1f ba 0e 00 b4 09 cd 21 b8 01 4c cd 21 54 68 |........!..L.!Th|
00000050 69 73 20 70 72 6f 67 72 61 6d 20 63 61 6e 6e 6f |is program canno|
00000060 74 20 62 65 20 72 75 6e 20 69 6e 20 44 4f 53 20 |t be run in DOS |
00000070 6d 6f 64 65 2e 0d 0d 0a 24 00 00 00 00 00 00 00 |mode....$.......|
00000080 81 2d 03 c5 c5 4c 6d 96 c5 4c 6d 96 c5 4c 6d 96 |.-...Lm..Lm..Lm.|
00000090 d1 27 6c 97 c6 4c 6d 96 c5 4c 6c 96 c7 4c 6d 96 |.'l..Lm..Ll..Lm.|
There it is. This section seems to contain a binary file, but the magic bytes (0x4D5A - MZ) of the file have been stomped (meaning overwritten for obfuscation purposes).
That is also why the file output did not detect the binary file!
Before extracting the file and throwing it at Ghidra, I usually try out commands such as strings or hexdump to see what I can quickly gather before moving on to heavier analysis.
Looking at the hexdump output, we observe this section:
$ hexdump -vC pid.3244.vad.0xb0000-0xb3fff.dmp
[...]
00001000 55 8b ec 83 ec 78 c6 45 88 70 c6 45 89 6f c6 45 |U....x.E.p.E.o.E|
00001010 8a 77 c6 45 8b 65 c6 45 8c 72 c6 45 8d 73 c6 45 |.w.E.e.E.r.E.s.E|
00001020 8e 68 c6 45 8f 65 c6 45 90 6c c6 45 91 6c c6 45 |.h.E.e.E.l.E.l.E|
00001030 92 20 c6 45 93 2d c6 45 94 65 c6 45 95 70 c6 45 |. .E.-.E.e.E.p.E|
00001040 96 20 c6 45 97 62 c6 45 98 79 c6 45 99 70 c6 45 |. .E.b.E.y.E.p.E|
00001050 9a 61 c6 45 9b 73 c6 45 9c 73 c6 45 9d 20 c6 45 |.a.E.s.E.s.E. .E|
00001060 9e 2d c6 45 9f 65 c6 45 a0 6e c6 45 a1 63 c6 45 |.-.E.e.E.n.E.c.E|
00001070 a2 20 c6 45 a3 5a c6 45 a4 51 c6 45 a5 42 c6 45 |. .E.Z.E.Q.E.B.E|
00001080 a6 6a c6 45 a7 41 c6 45 a8 47 c6 45 a9 67 c6 45 |.j.E.A.E.G.E.g.E|
00001090 aa 41 c6 45 ab 62 c6 45 ac 77 c6 45 ad 41 c6 45 |.A.E.b.E.w.E.A.E|
000010a0 ae 67 c6 45 af 41 c6 45 b0 45 c6 45 b1 67 c6 45 |.g.E.A.E.E.E.g.E|
000010b0 b2 41 c6 45 b3 56 c6 45 b4 41 c6 45 b5 42 c6 45 |.A.E.V.E.A.E.B.E|
000010c0 b6 43 c6 45 b7 41 c6 45 b8 48 c6 45 b9 73 c6 45 |.C.E.A.E.H.E.s.E|
000010d0 ba 41 c6 45 bb 5a c6 45 bc 41 c6 45 bd 42 c6 45 |.A.E.Z.E.A.E.B.E|
000010e0 be 73 c6 45 bf 41 c6 45 c0 47 c6 45 c1 77 c6 45 |.s.E.A.E.G.E.w.E|
000010f0 c2 41 c6 45 c3 63 c6 45 c4 77 c6 45 c5 42 c6 45 |.A.E.c.E.w.E.B.E|
00001100 c6 66 c6 45 c7 41 c6 45 c8 47 c6 45 c9 4d c6 45 |.f.E.A.E.G.E.M.E|
00001110 ca 41 c6 45 cb 4e c6 45 cc 41 c6 45 cd 42 c6 45 |.A.E.N.E.A.E.B.E|
00001120 ce 75 c6 45 cf 41 c6 45 d0 46 c6 45 d1 38 c6 45 |.u.E.A.E.F.E.8.E|
00001130 d2 41 c6 45 d3 59 c6 45 d4 67 c6 45 d5 41 c6 45 |.A.E.Y.E.g.E.A.E|
00001140 d6 7a c6 45 d7 41 c6 45 d8 46 c6 45 d9 38 c6 45 |.z.E.A.E.F.E.8.E|
00001150 da 41 c6 45 db 61 c6 45 dc 41 c6 45 dd 41 c6 45 |.A.E.a.E.A.E.A.E|
00001160 de 30 c6 45 df 41 c6 45 e0 48 c6 45 e1 49 c6 45 |.0.E.A.E.H.E.I.E|
00001170 e2 41 c6 45 e3 5a c6 45 e4 41 c6 45 e5 42 c6 45 |.A.E.Z.E.A.E.B.E|
00001180 e6 66 c6 45 e7 41 c6 45 e8 48 c6 45 e9 51 c6 45 |.f.E.A.E.H.E.Q.E|
00001190 ea 41 c6 45 eb 4d c6 45 ec 41 c6 45 ed 42 c6 45 |.A.E.M.E.A.E.B.E|
000011a0 ee 66 c6 45 ef 41 c6 45 f0 47 c6 45 f1 59 c6 45 |.f.E.A.E.G.E.Y.E|
000011b0 f2 41 c6 45 f3 4d c6 45 f4 51 c6 45 f5 42 c6 45 |.A.E.M.E.Q.E.B.E|
000011c0 f6 75 c6 45 f7 41 c6 45 f8 47 c6 45 f9 51 c6 45 |.u.E.A.E.G.E.Q.E|
000011d0 fa 41 c6 45 fb 66 c6 45 fc 51 c6 45 fd 41 c6 45 |.A.E.f.E.Q.E.A.E|
000011e0 fe 3d 6a 00 8d 45 88 50 ff 15 00 20 0b 00 8b e5 |.=j..E.P... ....|
[...]
This resembles base64 in a wierd way. We can also see a byte sequence (“0xc6 0x45”) repeated in the quite a few times. If you know your x86 opcodes, you might know that 0xc6 is a mov instruction.
I extracted the hexadecimal values and headed on to https://defuse.ca/online-x86-assembler.htm to disassemble it.
I love this website because it automagically know what to do with you input.
In the disassembled result, we observe a very large ASCII array instanciation:
3: 83 ec 78 sub esp,0x78
6: c6 45 88 70 mov BYTE PTR [ebp-0x78],0x70
a: c6 45 89 6f mov BYTE PTR [ebp-0x77],0x6f
e: c6 45 8a 77 mov BYTE PTR [ebp-0x76],0x77
12: c6 45 8b 65 mov BYTE PTR [ebp-0x75],0x65
16: c6 45 8c 72 mov BYTE PTR [ebp-0x74],0x72
1a: c6 45 8d 73 mov BYTE PTR [ebp-0x73],0x73
1e: c6 45 8e 68 mov BYTE PTR [ebp-0x72],0x68
22: c6 45 8f 65 mov BYTE PTR [ebp-0x71],0x65
26: c6 45 90 6c mov BYTE PTR [ebp-0x70],0x6c
2a: c6 45 91 6c mov BYTE PTR [ebp-0x6f],0x6c
[...]
1ba: c6 45 f5 42 mov BYTE PTR [ebp-0xb],0x42
1be: c6 45 f6 75 mov BYTE PTR [ebp-0xa],0x75
1c2: c6 45 f7 41 mov BYTE PTR [ebp-0x9],0x41
1c6: c6 45 f8 47 mov BYTE PTR [ebp-0x8],0x47
1ca: c6 45 f9 51 mov BYTE PTR [ebp-0x7],0x51
1ce: c6 45 fa 41 mov BYTE PTR [ebp-0x6],0x41
1d2: c6 45 fb 66 mov BYTE PTR [ebp-0x5],0x66
1d6: c6 45 fc 51 mov BYTE PTR [ebp-0x4],0x51
1da: c6 45 fd 41 mov BYTE PTR [ebp-0x3],0x41
1de: c6 45 fe 3d mov BYTE PTR [ebp-0x2],0x3d
Let’s grab the ASCII values and head on to CyberChef:
powershell -ep bypass -enc ZQBjAGgAbwAgAEgAVABCAHsAZABsAGwAcwBfAGMANABuAF8AYgAzAF8AaAA0AHIAZABfAHQAMABfAGYAMQBuAGQAfQA=
A PowerShell encoded command, almost there!
Let’s run that again in Cyberchef:
And there it is boyz, the flag for the challenge.
HTB{dlls_c4n_b3_h4rd_t0_f1nd}
Conclusion
In a real forensics investigation, we would have spent more time looking at processes, network information and other artifacts to determine what really happened on this device. However in a CTF scenario, our goal is to find the flag as fast as possible to continue on with other challenges.
It was a very fun challenge to solve and I was eager to share my solution. Thanks for reading.
Byeeeeeeeeeeeeee.
Useful Links
https://github.com/volatilityfoundation/volatility3
http://ref.x86asm.net/coder32.html
https://defuse.ca/online-x86-assembler.htm
https://gchq.github.io/CyberChef/