SiberSiaga 2023: Obstacles
The best satisfaction always comes after overcoming many annoying obstacles. Overcome this challenge to gain the satisfaction of a flag!
Last updated
The best satisfaction always comes after overcoming many annoying obstacles. Overcome this challenge to gain the satisfaction of a flag!
Last updated
Bypass anti-debugging techniques via manual patching to circumvent branching controls.
Running the executable doesn't seem to be doing anything at all.
Importing the executable in any debugger/disassembler of choice shows that it is written in Go Lang. Luckily for us, most of the function names are resolved automatically without needing to rename them.
At a glance, there seem to be several anti-debugging techniques such as time.Sleep
and main.isDebuggerPresent
implemented. This is then followed by a series of decryption routines.
Further down, there are even checks to verify if the executable is currently running in a sandbox based on the presence of specific processes.
We can verify the process names by going back a few lines.
data_50dbb8
is a pointer that points to 0x4dde33
, so we'll take a look at that address instead.
This reveals the process ollydbg.exe
and procmon.exe
. In the meantime, take note of notepad.exe
as well.
The last anti-debugging technique seems to be checking the current date against a specific point of time.
As the challenge description suggests, we need to overcome these obstacles, a.k.a anti-debugging techniques via assembly patching, while leaving all the decryption routine untouched. I am going to use BinaryNinja
for patching due to it's intuitiveness.
When trying to run the executable, it will go to sleep indefinitely due to time.Sleep
. We can patch this out easily with Skip and Return Zero
:
Next, we will need a way to prevent the program from terminating. Right click on os.Exit
and select Patch -> Edit Current Line
:
Instead of terminating, we can neutralize it by jumping to the nearest block. Obtain the address of the nearest block with Copy Address
:
This way, it will continue execution on the same block, regardless of the comparison test rax, rax
made on top.
Up until this point, we have defeated time.Sleep
and os.Exit
, but we are met with sandbox checks.
To neutralize this, select Invert Branch
to turn je
into jne
. Do note that the sandbox checks are still in place since main.sandboxFilepath
remains intact. However, any positive detection will not trigger anything since we have flipped the decision tree.
Sandbox detection is now gone.
Apply the same technique as patching time.Sleep
for all date-time equivalent function calls.
Recall that we received the error message panic: time: missing Location in call to Date
on runtime. This matches with the following function call.
We can bypass this with a simple Invert Patch
:
However, we now encounter a different problem.
A little below, we can find the culprit for that problem.
Patch the instructions with a Return Zero
and Invert Patch
respectively.
An environment issue.
A little below, we can find the code block responsible for this via data_50c300
strings.
Perform an Invert Patch
on the jump above, so it changes from jg
to jle
:
Once that is completed, we have a final part to fix before our flag gets fully decrypted.
We can either patch jne
or os.Exit
here, both methods will lead to the same result.
Once the flag is fully decrypted, it will be injected into an existing process, as verified with the usage of API calls:
OpenProcess
-> VirtualAllocEx
-> WriteProcessMemory
-> CreateRemoteThreadEx
Since OpenProcess
is invoked (instead of CreateProcessW
), the process injection must be targeting an existing process on the system. Before this, we knew that notepad.exe
was somehow involved in this challenge, so it must've been the one! To solve this challenge, open a new instance of notepad.exe
and run the executable.
Flag: sibersiaga{G0_GO_G0L4NG_0BST4ClES}