SiberSiaga 2023: Vacine
Well done, you've successfully reached the Final stage of SiberSiaga CTF. To obtain the flag, try reversing this challenge.
Last updated
Well done, you've successfully reached the Final stage of SiberSiaga CTF. To obtain the flag, try reversing this challenge.
Last updated
Manually unpack executable via the
Tail Jump
trick, followed by shellcode extraction from process memory.
Attempting to debug the binary in IDA Pro shows that there's only 1 function available. This indicates the executable is packed to makes reverse engineering harder.
Analyzing the executable in DetectItEasy
shows that it is UPX-packed.
However, attempting the unpack it with the UPX command-line utility shows that the executable is tampered, and was not able to be unpacked automatically.
When attaching the executable in x32dbg
, it automatically jumps to the nearest DLL imports. Click on Run to user code
to return to the program's entry point, until pushad
is reached.
On the entry point of pushad
, scroll down until the pushad
equivalent assembly of popad
is located.
A few instructions below popad
is a jmp
instruction directly to the program's Original Entry Point (OEP). This is the initial Entry Point of the executable before packing. Place a breakpoint on the following address, then Step Into
until a function call is reached.
Use OllyDumpEx
to fix the OEP address and dump the executable. The resulting executable is now semi-unpacked.
The newly dumped executable will not execute normally as it is still missing dependencies/imports from the Import Address Table (IAT). We can fix it using Scylla's IAT Autosearch
:
Remove any corrupted thunks that still exists, then continue fixing the executable by correcting the imports:
Once all the fixes above are completed, dump the executable for a second time. When prompted, select the previously dumped file vacine_dump.exe
. This will produce the final executable vacine_dump_SCY.exe
:
After manual unpacking, IDA will now show all the original functions:
If you use
IDA
to disassemble the unpacked executable, it should point you to the main function directly!
Decompiling the unpacked binary in Ghidra
shows that the entry point is FUN_0006136A()
. However, this is not the main function. We can instead find out the real main
function using the ret
trick.
This trick is fairly simple, as it only requires us to locate the return value. In this case, the return value of unaff_ESI
is dictated by the function real_main()
, so we know this is the actual/real main function.
At a glance, the executable is performing a classic Process Injection procedure with the following Win32 APIs:
VirtualAllocEx
-> WriteProcessMemory
-> CreateRemoteThread
-> WaitForSingleObject
After spending a long time trying to find interesting code for dissecting, it turns out that we can easily extract the flag using Floss
:
Decode the base64 encoded string.
Flag: sibersiaga{1e6d2ea80a28da53e5308d5aa664f1f8}
Looking back at the decompiled code, we noticed a XOR decryption routine shortly before WriteProcessMemory
is called. The odds are, the shellcode is being decrypted at runtime, before it is copied from vacine.exe
to notepad.exe
:
By tracing the flow of execution back to the origin where the encrypted shellcode is stored, we know that dword_62218
holds the shellcode in the .data
section of the PE file.
Export the encrypted shellcode using IDA's Export data
function.
Write a simple solver in C to decrypt the shellcode.
Compile the C code and execute the solver to get the flag.
Flag: sibersiaga{1e6d2ea80a28da53e5308d5aa664f1f8}
Understanding that the unpacked executable is just merely performing Process Injection on a notepad.exe
process, there is a high chance that the flag is living in disguise, within the virtual memory space of notepad.exe
. To debug the executable on runtime, we will need the following tools:
API Monitor 32-bit
- To place inline hooks on API calls.
Process Hacker
- To inspect memory region of processes.
Firstly, place a detour or inline hook to monitor the API CreateRemoteThread
. This will halt program execution right before the shellcode is executed on the notepad
process.
Secondly, use Process Hacker
to look up the victim process. If our theory was correct, the decrypted shellcode containing the flag should be visible in the memory region of notepad
as plaintext.
Since VirtualAllocEx
was called with MemoryProtection = 0x40
, this should resolves to a memory region of RWX
. This makes it easier to locate the exact memory region containing the shellcode, as not a single legitimate memory region in notepad
will be RWX
other than the one allocated by vacine.exe
:
Flag: sibersiaga{1e6d2ea80a28da53e5308d5aa664f1f8}