SiberSiaga 2023: Obstacles

The best satisfaction always comes after overcoming many annoying obstacles. Overcome this challenge to gain the satisfaction of a flag!

TL;DR

Bypass anti-debugging techniques via manual patching to circumvent branching controls.

Initial Analysis

Running the executable doesn't seem to be doing anything at all.

 ┌──(kali💀JesusCries)-[~/…/CTF/SiberSiaga2023 (Finals)/Rev/Obstacles]
 └─$ wine Obstacles.exe                      
 ^C   

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.

Identifying Anti-Debugging Techniques

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.

Manual Patching

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.

Sleep

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:

Program Termination

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.

Sandbox

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.

Time & Date

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 Zeroand Invert Patch respectively.

Environment

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:

File Check

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.

Solution

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}

Last updated