Thanks drome for sharing his knowledge and skills! He completed all 10 challenges and this series of writeups is done by him :)
Details | Links |
---|---|
Official Challenge Site | https://flare-on.com/ |
Official Challenge Announcement | https://www.fireeye.com/blog/threat-research/2021/08/announcing-the-eighth-annual-flare-on-challenge.html |
Official Solutions | https://www.mandiant.com/resources/flare-on-8-challenge-solutions |
Official Challenge Binaries | http://flare-on.com/files/Flare-On8_Challenges.zip |
07_spel
Pro-tip: start disassembling this one then take a nice long break, you’ve earned it kid.
7-zip password:flare
The file given is a 64-bit executable with the following properties
arch x86
baddr 0x140000000
binsz 4376064
bintype pe
bits 64
canary false
retguard false
class PE32+
cmp.csum 0x004375cd
compiled Tue Jul 27 01:22:41 2021
crypto false
endian little
havecode true
hdr.csum 0x00000000
laddr 0x0
lang c
linenum false
lsyms false
machine AMD 64
nx true
os windows
overlay false
cc ms
pic true
relocs false
signed false
sanitize false
static false
stripped false
subsys Windows GUI
va true
Running it produces an alert with the following error message, An error occurred. Please close the application and try again.
Running in IDA, trace where it went before the message appeared, goes to 0x7FF7ED092CB0
which has too many variable declarations (because it uses a huge portion of the stack) to view properly as a function so we hide the first node. This function essentially uses CDialog::DoModal
to create the modal dialog with the message, then it uses GetProcAddress
to get VirtualAllocExNuma
, then it starts writing a huge chunk of shellcode at rsp+2F0h
which goes all the way until rsp+0x2F01C
, then it allocates an area of memory in the heap to write the shellcode to, then it jumps into the shellcode (instruction at 0x7FF7ED20972F
)
Shellcode
To make analysis easier, instead of analyzing the extracted shellcode binary, we make the heap segment containing it a loader segment, then reanalyze, then take a memory snapshot.
With the shellcode segment being at address 0x1C616490000
, the function quickly jumps into sub_1C616490040
. This function loads a PE file embedded in the shellcode.
Used pecheck.py to carve out the PE file from the shellcode.
> ./pecheck.py -l P .\shellcode.bin
1: 0x00000b28 DLL 64-bit 0x0002ed27 aee298f73b1b9ef48e125dfc649fa2ba 0x0002ed2c (EOF) b'' b''
2: 0x00015a18 DLL 64-bit 0x0002d417 60dbeb2094bb0b5a439b9e2f3ecd06b5 0x0002ed2c (EOF) b'' b'ldr.dll'
From pecheck.py,
The first column is the position of the embedded PE file, the fourth column is the end of the embedded PE file without overlay, and the sixth column is the end with overlay.
The fifth column is the hash of the embedded PE file without overlay.
DLL 1
The first extracted DLL has the following properties
arch x86
baddr 0x180000000
binsz 188927
bintype pe
bits 64
canary false
retguard false
class PE32+
cmp.csum 0x0003e06d
compiled Tue Jul 27 00:48:18 2021
crypto false
endian little
havecode true
hdr.csum 0x00000000
laddr 0x0
lang c
linenum false
lsyms false
machine AMD 64
nx true
os windows
overlay true
cc ms
pic true
relocs false
signed false
sanitize false
static false
stripped false
subsys Windows CUI
va true
On brief analysis, looks like this sample manually loads the second embedded DLL then calls it’s export Start
, then terminates.
DLL 2
arch x86
baddr 0x180000000
binsz 96768
bintype pe
bits 64
canary false
retguard false
class PE32+
cmp.csum 0x0001be44
compiled Tue Jul 27 00:39:26 2021
crypto false
endian little
havecode true
hdr.csum 0x00000000
laddr 0x0
lang c
linenum false
lsyms false
machine AMD 64
nx true
os windows
overlay false
cc ms
pic true
relocs false
signed false
sanitize false
static false
stripped false
subsys Windows CUI
va true
Analysis base address of this DLL is at 0x7FFDFF9E0000
.
Start
calls sub_7FFDFF9E1990
.
Afterwards it calls sub_7FFDFF9E12C0
. sub_7FFDFF9E12C0
converts a weird looking number like 0x1A10BD8B
into a function like LoadResource
. We will call it the hash-function converter, because it finds a function in kernel32 that has a name whose hash matches the number, using this hash algorithm
|
|
We hence use the following script which will help us later on to find the function corresponding to a given hash using static analysis:
|
|
sub_7FFDFF9E1990
used the hash-function converter to get VirtualAlloc
, then creates a buffer of size 480.
Afterwards it calls sub_7FFDFF9E1A40
. sub_7FFDFF9E1A40
is a function that has 2 parts, depending on the second argument. The first time it is called, the second argument is 1
, so it loads the resource named PNG
and places a pointer to it in the 480-byte buffer. The offsets and corresponding value meanings are documented below in Object Struct
After that, sub_7FFDFF9E1990
calls sub_7FFDFF9E2E60
which essentially checks if the process name is Spell.EXE
.
After that it calls SleepEx
then calls sub_7FFDFF9E1A40
this time with the argument as 8
or 2
, depending on whether the process is named Spell.EXE
(being so gives 2
).
The second time it’s run, if the argument is 2
, then it calls sub_7FFDFF9E1F80
which is a network function, repeatedly calling sub_7FFDFF9E2070
which contacts inactive.flare-on.com:888
. It sends a single byte @
, receive response, and does some checks
- If the response is
exe
, it goes intosub_7FFDFF9E2410
which sends#
, thenrecv
512 bytes of shellcode and runs it. - If it’s
run
, it would go tosub_7FFDFF9E2590
which sends an&
, then againrecv
512 bytes of shellcode and run it. - If it’s
flare-on.com
, it returns 1 and setpObj+408
to be the socket.
After calling the network function, sub_7FFDFF9E1A40
calls sub_7FFDFF9E2A20
to write to the registry key HKCU\Software\Microsoft\Spell
with the name as a string 1
and value as the string flare-on.com
xored with the bytes at 0x7FFDFF9F5170
.
After that it calls sub_7FFDFF9E2F70
with uses the BCrypt
library to do AES decryption, using the string "d41d8cd98f00b204e9800998ecf8427e"
as secret to generate the symmetric key, uses bytes at 0x7FFDFF9F5140
as IV (which are just a bunch of 0x80
bytes), and decrypts 32 bytes of the PNG file starting at offset 95. After decrypting that we get the string l3rlcps_7r_vb33eehskc3
. (l3rlcps_7r_vb33eehskc3@flare-on.com
doesn’t work but it was worth a try).
Then, it calls sub_7FFDFF9E2730
which uses a strange switch table to xor some reordered version of our l3rlcps_7r_vb33eehskc3
string with bytes at 0x7FFDFF9F5180
and then 0x7FFDFF9F5160
. Then it calls sub_7FFDFF9E2A20
again to write to the registry key HKCU\Software\Microsoft\Spell
with the name as a string 0
and value as our reordered string.
In essence, HKCU\Software\Microsoft\Spell
has two values, 0
which is the first part of the flag (the part before and including the @
sign) xored with the bytes at 0x7FFDFF9F5180
and then 0x7FFDFF9F5160
, and 1
which is the second part (the flare-on.com
) xored with the bytes at 0x7FFDFF9F5170
.
Object struct
The object is 480 bytes
0: (12 bytes) Date string in MM-dd-yyyy format
24: (pointer) PNG resource
32: PNG resource size
36: 2
40: (260 bytes) Module file name
301: (9 bytes) "inactive"
336: (BOOL) isWow64Process - False
340: Number of failed network tries
384: 3
392: (pointer 32 bytes) "flare-on.com"
408: Socket
416: (pointer 33 bytes) "d41d8cd98f00b204e9800998ecf8427e\x00"
424: (24 bytes) "l3rlcps_7r_vb33eehskc3"
448: (32 bytes) recv buf from "inactive.flare-on.com:888"
Solution
At least two methods are possible, the first one was what we did initially, and the second doesn’t involve writing code.
Method 1: Reordering the flag string
We can change the code in the switch statement sub_7FFDFF9E2730
to get our flag
|
|
Method 2: Running and patching
- Rename the binary
Spell.EXE
(case sensitive). - Set the
hosts
to pointinactive.flare-on.com
to the localhost, and runnc -l 888
. - Run the binary, breakpoint at
kernelbase_SleepEx
, run (and close the dialog) until you hit the breakpoint, then changeRCX
to0
and step out of the call. - Binary search for the bytes
C3 C1 A8 06 C2 96 33
, then choose the one that doesn’t come from the stack, address doesn’t start with1800
, and ends with60
, then go there and zero out the entire region from60
to90
. - Run it, recv the
@
on nc, then send backflare-on.com
, then the program will exit. - Check
HKCU\SOFTWARE\Microsoft\Spell
. The two parts of the flag will be there.
Flag
b3s7_sp3llcheck3r_ev3r@flare-on.com