---
description: The binary looks so messy. Get me the flag. Please :(
---

# SiberSiaga 2023: Packed

## TL;DR

> Manually unpack the executable via debugging using dnSpy's `Module Breakpoints` feature.

## **Initial Analysis**

Analyzing the executable file via `Detect It Easy` shows a `.NET executable` that is heavily obfuscated using 3 different packers.

<figure><img src="https://80158427-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkoydBBkDRRSxCLl1Wpgr%2Fuploads%2FsCn48xPUEp4aFVxcphSc%2Fimage.png?alt=media&#x26;token=f3e5f9ec-91b6-4e17-9464-7dfc4610b55c" alt=""><figcaption></figcaption></figure>

We can attempt to unpack the executable iteratively via `de4dot`:

```bash
 ┌──(kali💀JesusCries)-[~/…/CTF/SiberSiaga2023 (Finals)/Rev/Packed]
 └─$ de4dot finaldotnet.exe -p cr      
 de4dot v3.1.41592.3405 Copyright (C) 2011-2015 de4dot@gmail.com
 Latest version and source code: https://github.com/0xd4d/de4dot
 Detected Confuser v1.9 (r76186+) (/home/kali/Desktop/CTF/SiberSiaga2023 (Finals)/Rev/Packed/finaldotnet.exe)
 Cleaning /home/kali/Desktop/CTF/SiberSiaga2023 (Finals)/Rev/Packed/finaldotnet.exe
 Renaming all obfuscated symbols
 Saving /home/kali/Desktop/CTF/SiberSiaga2023 (Finals)/Rev/Packed/finaldotnet-cleaned.exe
 ​
 ┌──(kali💀JesusCries)-[~/…/CTF/SiberSiaga2023 (Finals)/Rev/Packed]
 └─$ de4dot finaldotnet-cleaned.exe -p df 
 de4dot v3.1.41592.3405 Copyright (C) 2011-2015 de4dot@gmail.com
 Latest version and source code: https://github.com/0xd4d/de4dot
 Detected Dotfuscator (/home/kali/Desktop/CTF/SiberSiaga2023 (Finals)/Rev/Packed/finaldotnet-cleaned.exe)
 Cleaning /home/kali/Desktop/CTF/SiberSiaga2023 (Finals)/Rev/Packed/finaldotnet-cleaned.exe
 Renaming all obfuscated symbols
 Saving /home/kali/Desktop/CTF/SiberSiaga2023 (Finals)/Rev/Packed/finaldotnet-cleaned-cleaned.exe                                                                                                                         
 
 ┌──(kali💀JesusCries)-[~/…/CTF/SiberSiaga2023 (Finals)/Rev/Packed]
 └─$ de4dot finaldotnet-cleaned-cleaned.exe -p go 
 de4dot v3.1.41592.3405 Copyright (C) 2011-2015 de4dot@gmail.com
 Latest version and source code: https://github.com/0xd4d/de4dot
 Detected Goliath.NET (/home/kali/Desktop/CTF/SiberSiaga2023 (Finals)/Rev/Packed/finaldotnet-cleaned-cleaned.exe)
 Cleaning /home/kali/Desktop/CTF/SiberSiaga2023 (Finals)/Rev/Packed/finaldotnet-cleaned-cleaned.exe
 Renaming all obfuscated symbols
 Saving /home/kali/Desktop/CTF/SiberSiaga2023 (Finals)/Rev/Packed/finaldotnet-cleaned-cleaned-cleaned.exe
```

Verify this against `Detect It Easy` shows that the executable is now fully unpacked.

<figure><img src="https://80158427-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkoydBBkDRRSxCLl1Wpgr%2Fuploads%2FCcuvkQxONd0kGHjA6lwV%2Fimage.png?alt=media&#x26;token=5804285f-c7e0-4c34-8cb4-2233422a1157" alt=""><figcaption></figcaption></figure>

However, this results in no avail. As verified in `dnSpy`, the decompiled code remains obfuscated despite `de4dot` claiming to have successfully unpack it. This is likely due to the use of a custom/unknown packer.

<figure><img src="https://80158427-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkoydBBkDRRSxCLl1Wpgr%2Fuploads%2F0hYifuHfw9k824dQ0zOm%2Fimage.png?alt=media&#x26;token=41b784c6-408c-4414-bdc0-47a932c156db" alt=""><figcaption></figcaption></figure>

## **Manual Unpacking**

We can expect this challenge to be solved with a similar approach as `Vacine` - that is: by debugging the executable until we reach the real entry point of the program.

By default, all assemblies in `.NET` (regardless of it's `.EXE` or `.DLL` extension) will be loaded directly into memory, which makes the debugging of external assemblies practically impossible.

To fix this, go to `Debug -> Windows -> Module Breakpoints` and change the name field to an asterisk wildcard `*`:

<figure><img src="https://80158427-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkoydBBkDRRSxCLl1Wpgr%2Fuploads%2FZLgKPdCrWJvpRLeuCvAx%2Fimage.png?alt=media&#x26;token=ce8d94f1-9b0e-41e0-8240-fc39fc43a876" alt=""><figcaption></figcaption></figure>

Start debugging the program by hitting `Continue - F5` with the default configurations, and notice how `dnSpy` places a breakpoint on the first ever module loaded `mscorlib.dll`:

<figure><img src="https://80158427-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkoydBBkDRRSxCLl1Wpgr%2Fuploads%2FbToraIcgm42FNRjw3idU%2Fimage.png?alt=media&#x26;token=e0b19457-0b6d-4601-a3fd-334165bf4bd5" alt=""><figcaption></figcaption></figure>

As `mscorlib.dll` is a common dependency of `.NET` applications for bootstrapping the Common Language Runtime (CLR) environment, this isn't something of our interest. For the same reason, we will also skip through all subsequent assemblies that looks benign with `Continue - F5`:

1. **finaldotnet.exe** (The application itself)
2. **Microsoft.VisualBasic.dll**, **System.Core.dll**, **System.dll**

A while later, we notice that `Sevenziplib` is loaded. This suggests that the executable is utilizing some kind of compression to obfuscate the application.

<figure><img src="https://80158427-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkoydBBkDRRSxCLl1Wpgr%2Fuploads%2F8cy9U0mHDjja648fbZFx%2Fimage.png?alt=media&#x26;token=11ecfa1b-835a-4360-8850-b1133d2258c9" alt=""><figcaption></figcaption></figure>

Regardless of that, `Continue - F5` one step further shows that `finaldotnet` (without the `.EXE` extension) is now loaded. This breakpoint also corresponds to the function `RuntimeAssembly.nLoadImage(rawAssembly)`, which confirms that `finaldotnet.exe` is loading `finaldotnet` - like an inception.

<figure><img src="https://80158427-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkoydBBkDRRSxCLl1Wpgr%2Fuploads%2F3ywqbh6hAnsP0RZFgxsP%2Fimage.png?alt=media&#x26;token=429bbe35-3be7-44fc-a105-6d00e3779415" alt=""><figcaption></figcaption></figure>

To verify what comes before the loading of `finaldotnet`, hit `Step Out - Shift + F11`. This brings us to the `Assembly.Load(sLcn.Ptpi(array))` function, which sounded like the Caller of our previous function:

* **Caller:** `Assembly.Load(sLcn.Ptpi(array))` <- High Level Function
* **Callee:** `RuntimeAssembly.nLoadImage(rawAssembly)` <- Low Level Function

<figure><img src="https://80158427-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkoydBBkDRRSxCLl1Wpgr%2Fuploads%2FmxakwULxL5l9zVferGgQ%2Fimage.png?alt=media&#x26;token=3aaa6a7d-119e-450d-b438-08db0c027337" alt=""><figcaption></figcaption></figure>

`Step Out - Shift + F11` again leads us to `Assembly assembly = sLcn.lFRS()`. Taking a wild guess, this line of code is most probably referencing the actual assembly that will be loaded, which in this case points to`finaldotnet`.

To sum up, the reason for `Stepping Out` twice, is because there are 3 layers of Abstraction implemented. Therefore, we need to go back 2 levels to reach the top-most level Caller.

<figure><img src="https://80158427-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkoydBBkDRRSxCLl1Wpgr%2Fuploads%2FotDsa9PxgdevrvyIaxol%2Fimage.png?alt=media&#x26;token=f1752624-389e-4b8c-9942-c022be656c11" alt=""><figcaption></figcaption></figure>

A few lines below (Line 99) the current statement (Line 42), indicates the the entry point `entryPoint.Invoke(null, parameters)` of our unpacked executable. Place a breakpoint on it.

<figure><img src="https://80158427-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkoydBBkDRRSxCLl1Wpgr%2Fuploads%2FtfszgpCXFKEhwwqQbyTE%2Fimage.png?alt=media&#x26;token=b884a15a-7798-4e05-baa0-30ae23496425" alt=""><figcaption></figcaption></figure>

Afterwards, hit `Continue - F5` until the program execution stops at the entry point.

<figure><img src="https://80158427-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkoydBBkDRRSxCLl1Wpgr%2Fuploads%2FOVmJN0UnLaKuFBjRXN3K%2Fimage.png?alt=media&#x26;token=e278baed-6b79-4dc6-a5c5-8d42fe2c6ffe" alt=""><figcaption></figcaption></figure>

After stopping at the entry point, hit `Step Over - F10` to start exploring the unpacked executable.

<figure><img src="https://80158427-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkoydBBkDRRSxCLl1Wpgr%2Fuploads%2FPyhb248bVQCJcc6yGGCp%2Fimage.png?alt=media&#x26;token=34d73ee2-7283-40e9-b768-49f268ed18d6" alt=""><figcaption></figcaption></figure>

Recall that we configured `dnSpy` to set a breakpoint every time an external assembly is loaded. As we are now in the bootstrapping stage of the unpacked executable, it makes sense for it to be loading a tons of dependencies it requires. This also means that we have a long way to go (`Step Over - F10` x28 times) until we will see some sensible lines of code.

<figure><img src="https://80158427-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkoydBBkDRRSxCLl1Wpgr%2Fuploads%2FTJogA4aAo96lZ1OvLiAN%2Fimage.png?alt=media&#x26;token=5cf10923-ed96-4d45-9335-b6ebf5dcdfd0" alt=""><figcaption></figcaption></figure>

Continue to `Step Over - F10` repeatedly until the current statement reaches Line 36. This will allow a bunch of string variables to be initialized and populated.

**BEFORE:**

<figure><img src="https://80158427-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkoydBBkDRRSxCLl1Wpgr%2Fuploads%2F4il5dgmRFDGU1pQtKqnR%2Fimage.png?alt=media&#x26;token=a6182f7b-c057-4823-9715-e4cee9188035" alt=""><figcaption></figcaption></figure>

**AFTER:**

<figure><img src="https://80158427-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkoydBBkDRRSxCLl1Wpgr%2Fuploads%2FzFoq65q36oPaEwLq8WZM%2Fimage.png?alt=media&#x26;token=61e58302-55df-4393-8faf-f8ee21dcbd39" alt=""><figcaption></figcaption></figure>

At this point onwards, when trying to run the executable normally, it will go to sleep indefinitely thanks to the code in Line 37. To bypass the sleep function, as well as other insignificant comparison statements marked under the giant cross mark, we need to `Set Next Statement - CTRL + SHIFT + F10` on Line 66.

<figure><img src="https://80158427-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkoydBBkDRRSxCLl1Wpgr%2Fuploads%2FNq2a5bjozf76A1wFHWPl%2Fimage.png?alt=media&#x26;token=20ecabd2-ee71-4c71-86ba-99d630360150" alt=""><figcaption></figcaption></figure>

Line 66 onwards is our point of interest because it seems to be comparing our user input to a base64-decoded string. With that said, this is most probably a flag-checker function.

```c
 using (uNPH.NvYX())
 {
     string jzvx = text6;
     string s = text7;
     byte[] wTdt = Convert.FromBase64String(s);
     byte[] bytes = uNPH.GBrL(wTdt, jzvx);
     string @string = Encoding.UTF8.GetString(bytes);
     Console.Write(value2);
     string a = Console.ReadLine();
     if (a == @string)
     {
         Console.WriteLine(value3);
         Console.WriteLine(value4);
     }
     else
     {
         Console.WriteLine(value5);
     }
 }
```

By the looks of it, the decrypted flag will be stored in variable `@string`. Hence, we can place a breakpoint on the comparison statement, and try to read the value of variable `@string`:

<figure><img src="https://80158427-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkoydBBkDRRSxCLl1Wpgr%2Fuploads%2F8Gmf2XbjMzdr1Px7HrS4%2Fimage.png?alt=media&#x26;token=de6dcfe1-815c-484b-b428-7073e6a2c5ef" alt=""><figcaption></figcaption></figure>

Finally, keep on `Stepping Over - F10` from Line 66 until Line 73 for the values to be populated.

<figure><img src="https://80158427-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkoydBBkDRRSxCLl1Wpgr%2Fuploads%2FSbxsVWdA8G1rWEswOWvl%2Fimage.png?alt=media&#x26;token=44b52af0-56cc-4d62-8b4b-02f2c887c6e7" alt=""><figcaption></figcaption></figure>

**Flag:** sibersiaga{2d50972fcecd376129545507f1062089}
