# SiberSiaga 2023: Vacine

## TL;DR

> Manually unpack executable via the `Tail Jump` trick, followed by shellcode extraction from process memory.

## **Initial Analysis**

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.

<figure><img src="/files/3FILXmHSVNfU9zjulP0g" alt=""><figcaption></figcaption></figure>

Analyzing the executable in `DetectItEasy` shows that it is UPX-packed.

<figure><img src="/files/pkGYVWaE8Eur7jzgypr9" alt=""><figcaption></figcaption></figure>

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.

```bash
 ┌──(kali💀JesusCries)-[~/…/CTF/SiberSiaga2023 (Finals)/Rev/Vacine]
 └─$ upx -d vacine.exe 
                        Ultimate Packer for eXecutables
                           Copyright (C) 1996 - 2020
 UPX 3.96        Markus Oberhumer, Laszlo Molnar & John Reiser   Jan 23rd 2020
 ​
         File size         Ratio      Format      Name
    --------------------   ------   -----------   -----------
 upx: vacine.exe: CantUnpackException: file is modified/hacked/protected; take care!!!
 ​
 Unpacked 0 files.
```

## **Manual Unpacking**

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.

<figure><img src="/files/uj150svhCl3VAy7ERjF6" alt=""><figcaption></figcaption></figure>

On the entry point of `pushad`, scroll down until the `pushad` equivalent assembly of `popad` is located.

<figure><img src="/files/P1fEbk4u5fcLD21B8ax9" alt=""><figcaption></figcaption></figure>

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.

<figure><img src="/files/Vll8sZKw3D92W66W2C2J" alt=""><figcaption></figcaption></figure>

Use `OllyDumpEx` to fix the OEP address and dump the executable. The resulting executable is now semi-unpacked.

<figure><img src="/files/Uf4AaxiIAaBaH7dFg5uN" alt=""><figcaption></figcaption></figure>

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`:

<figure><img src="/files/wkWpMa4S4SvrKj6lq55a" alt=""><figcaption></figcaption></figure>

Remove any corrupted thunks that still exists, then continue fixing the executable by correcting the imports:

<figure><img src="/files/GgdTD3gfxKz9lTqvqNXi" alt=""><figcaption></figcaption></figure>

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`:

<figure><img src="/files/ZRs5dfzsP50Vb1QkPvRE" alt=""><figcaption></figcaption></figure>

After manual unpacking, IDA will now show all the original functions:

<figure><img src="/files/7sheDC60gvOE5cmYh4s7" alt=""><figcaption></figcaption></figure>

## **Finding Main Function**

> 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.

```c
 void entry(void)
 ​
 {
   ___security_init_cookie();
   FUN_0006136a();
   return;
 }
```

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.

```c
 int FUN_0006136a(void)
 ​
 {
   if ((char)uVar3 != '\0') {
     uVar3 = ___scrt_acquire_startup_lock();
     if (DAT_00063334 != 1) {
     }
       unaff_ESI = real_main();
       uVar7 = FUN_00061a9c();
       if ((char)uVar7 != '\0') {
         ___scrt_uninitialize_crt(1,'\0');
         return unaff_ESI;
       }
       goto LAB_000614dd;
     }
   }
   FUN_0006197c(7);
 LAB_000614dd:
 ​
   exit(unaff_ESI);
 }
```

At a glance, the executable is performing a classic Process Injection procedure with the following Win32 APIs:

`VirtualAllocEx` -> `WriteProcessMemory` -> `CreateRemoteThread` -> `WaitForSingleObject`

```c
 void real_main(void)
 ​
 {  
   puVar6 = (undefined4 *)s_Well_done,_you've_successfully_r_00062128;
   BVar1 = CreateProcessA(s_C:\Windows\System32\notepad.exe_000621b4,(LPSTR)0x0,
                          (LPSECURITY_ATTRIBUTES)0x0,(LPSECURITY_ATTRIBUTES)0x0,0,0,(LPVOID)0x0,
                          (LPCSTR)0x0,(LPSTARTUPINFOA)local_1e8,&local_200);
   if (BVar1 != 0) {
     Sleep(1000);
     local_204 = OpenProcess(0x1fffff,0,local_200.dwProcessId);
     if (local_204 != (HANDLE)0x0) {
       puVar9 = SHELLCODE;
       lpStartAddress =
            (LPTHREAD_START_ROUTINE)VirtualAllocEx(local_204,(LPVOID)0x0,0x101,0x1000,0x40);
       for (; uVar5 < 0x101; uVar5 = uVar5 + 1) {
         *(byte *)((int)SHELLCODE + uVar5) = *(byte *)((int)SHELLCODE + uVar5) ^ 0x12;
       }
       WriteProcessMemory(local_204,lpStartAddress,SHELLCODE,0x101,(SIZE_T *)0x0);
       pvVar3 = CreateRemoteThread(pvVar3,(LPSECURITY_ATTRIBUTES)0x0,0,lpStartAddress,(LPVOID)0x0,0,
                                   (LPDWORD)0x0);
       WaitForSingleObject(pvVar3,0xffffffff);
       return;
     }
     DVar2 = GetLastError();
     FUN_00061010(s_Failed_to_open_process_(%d)._000621f4,(char)DVar2);
     return;
   }
 }
```

## **Unintended Solution**

After spending a long time trying to find interesting code for dissecting, it turns out that we can easily extract the flag using `Floss`:

```bash
 ┌──(kali💀JesusCries)-[~/Desktop]
 └─$ floss vacine_dump_SCY.exe 
 INFO: floss: extracting static strings...
 INFO: floss.results: echo c2liZXJzaWFnYXsxZTZkMmVhODBhMjhkYTUzZTUzMDhkNWFhNjY0ZjFmOH0= > nul                     
 ​
  ──────────────────── 
   FLOSS TIGHT STRINGS  
  ───────────────────── 
 ​
 D$$[[aYZQ
 echo c2liZXJzaWFnYXsxZTZkMmVhODBhMjhkYTUzZTUzMDhkNWFhNjY0ZjFmOH0= > nul
```

Decode the base64 encoded string.

```bash
 ┌──(kali💀JesusCries)-[~/Desktop]
 └─$ echo "c2liZXJzaWFnYXsxZTZkMmVhODBhMjhkYTUzZTUzMDhkNWFhNjY0ZjFmOH0" | base64 -d
 sibersiaga{1e6d2ea80a28da53e5308d5aa664f1f8}
```

**Flag:** sibersiaga{1e6d2ea80a28da53e5308d5aa664f1f8}

## **Intended Solution**

### **Static**

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`:

<figure><img src="/files/9VSl114IgwerBV7WUPbg" alt=""><figcaption></figcaption></figure>

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.

<figure><img src="/files/2feYxatGIJuDXj1mZVpG" alt=""><figcaption></figcaption></figure>

Export the encrypted shellcode using IDA's `Export data` function.

<figure><img src="/files/08Z5LiQtwqMdLjdZqoN7" alt=""><figcaption></figcaption></figure>

Write a simple solver in C to decrypt the shellcode.

```c
 #include <stdio.h>
 ​
 int main() {
     unsigned char ida_chars[] =
     {
         0xEE, 0xFA, 0x90, 0x12, 0x12, 0x12, 0x72, 0x9B, 0xF7, 0x23,
         0xD2, 0x76, 0x99, 0x42, 0x22, 0x99, 0x40, 0x1E, 0x99, 0x40,
         0x06, 0x99, 0x60, 0x3A, 0x1D, 0xA5, 0x58, 0x34, 0x23, 0xED,
         0xBE, 0x2E, 0x73, 0x6E, 0x10, 0x3E, 0x32, 0xD3, 0xDD, 0x1F,
         0x13, 0xD5, 0xF0, 0xE0, 0x40, 0x45, 0x99, 0x40, 0x02, 0x99,
         0x58, 0x2E, 0x99, 0x5E, 0x03, 0x6A, 0xF1, 0x5A, 0x13, 0xC3,
         0x43, 0x99, 0x4B, 0x32, 0x13, 0xC1, 0x99, 0x5B, 0x0A, 0xF1,
         0x28, 0x5B, 0x99, 0x26, 0x99, 0x13, 0xC4, 0x23, 0xED, 0xBE,
         0xD3, 0xDD, 0x1F, 0x13, 0xD5, 0x2A, 0xF2, 0x67, 0xE4, 0x11,
         0x6F, 0xEA, 0x29, 0x6F, 0x36, 0x67, 0xF6, 0x4A, 0x99, 0x4A,
         0x36, 0x13, 0xC1, 0x74, 0x99, 0x1E, 0x59, 0x99, 0x4A, 0x0E,
         0x13, 0xC1, 0x99, 0x16, 0x99, 0x13, 0xC2, 0x9B, 0x56, 0x36,
         0x36, 0x49, 0x49, 0x73, 0x4B, 0x48, 0x43, 0xED, 0xF2, 0x4D,
         0x4D, 0x48, 0x99, 0x00, 0xF9, 0x9F, 0x4F, 0x78, 0x13, 0x9F,
         0x97, 0xA0, 0x12, 0x12, 0x12, 0x42, 0x7A, 0x23, 0x99, 0x7D,
         0x95, 0xED, 0xC7, 0xA9, 0xE2, 0xA7, 0xB0, 0x44, 0x7A, 0xB4,
         0x87, 0xAF, 0x8F, 0xED, 0xC7, 0x2E, 0x14, 0x6E, 0x18, 0x92,
         0xE9, 0xF2, 0x67, 0x17, 0xA9, 0x55, 0x01, 0x60, 0x7D, 0x78,
         0x12, 0x41, 0xED, 0xC7, 0x77, 0x71, 0x7A, 0x7D, 0x32, 0x71,
         0x20, 0x7E, 0x7B, 0x48, 0x4A, 0x58, 0x68, 0x73, 0x45, 0x54,
         0x7C, 0x4B, 0x4A, 0x61, 0x6A, 0x48, 0x46, 0x48, 0x79, 0x5F,
         0x7F, 0x44, 0x7A, 0x5D, 0x56, 0x50, 0x7A, 0x5F, 0x78, 0x7A,
         0x79, 0x4B, 0x46, 0x47, 0x68, 0x48, 0x46, 0x47, 0x68, 0x5F,
         0x56, 0x7A, 0x79, 0x5C, 0x45, 0x54, 0x7A, 0x5C, 0x78, 0x4B,
         0x22, 0x48, 0x78, 0x54, 0x7F, 0x5D, 0x5A, 0x22, 0x2F, 0x32,
         0x2C, 0x32, 0x7C, 0x67, 0x7E, 0x12, 0x00, 0x00, 0x00, 0x00,
         0x00, 0x00, 0x00, 0x00
     };
 ​
     size_t ida_chars_size = sizeof(ida_chars) / sizeof(ida_chars[0]);
 ​
     for (size_t i = 0; i < ida_chars_size; i++) {
         ida_chars[i] ^= 0x12;
     }
 ​
     for (size_t i = 0; i < ida_chars_size; i++) {
         printf("%c", ida_chars[i]);
     }
 ​
     return 0;
 }
```

Compile the C code and execute the solver to get the flag.

```bash
 ┌──(kali💀JesusCries)-[~/Desktop]
 └─$ ./solve 
 ���`��1�d�P0�R
 �8�u�}�;}$u�X�X$�f� ӋI▒�:I�4��1����
                    K�XӋ�ЉD$$[[aYZQ��__Z���]j���Ph1�o��ջ���Vh������<|
 ���u�GrojS��echo c2liZXJzaWFnYXsxZTZkMmVhODBhMjhkYTUzZTUzMDhkNWFhNjY0ZjFmOH0= > nul 
```

**Flag:** sibersiaga{1e6d2ea80a28da53e5308d5aa664f1f8}

### **Dynamic**

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:

1. `API Monitor 32-bit` - To place inline hooks on API calls.
2. `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.

<figure><img src="/files/bdMA8giNBlc9KnSwFR0z" alt=""><figcaption></figcaption></figure>

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.

<figure><img src="/files/saUZYBteYyG8ZchvFKXR" alt=""><figcaption></figcaption></figure>

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`:

<figure><img src="/files/UFgLacD5HWI6NN5TtZeW" alt=""><figcaption></figcaption></figure>

**Flag:** sibersiaga{1e6d2ea80a28da53e5308d5aa664f1f8}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://jesuscries.gitbook.io/home/ctf-writeups/reverse-engineering/sibersiaga-2023-vacine.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
