Hack The Box - CubeMadness1

Hack The Box - CubeMadness1

Write-up and reverse engineering IL2CPP

·

6 min read

Hack The Box - Challenges - GamePWN

The files within the download look like they're from the Unity Game engine. I've worked with Unity before for a simulator game.

I have a Windows VM specifically for CTFs and CrackMes so I started up HackTheBox CubeMadness1.exe.

Looks like the objective of the game is to collect the cubes, but when you try to collect all the cubes, you can only 6 out of 20 cubes. We can either decompile these binaries to look for the flag or we can manipulate the memory so we can achieve 20 out of 20 cubes. I think the challenge intends the user to do the latter.

Cheat Engine

I've used game trainers in the past for single-player games and I think we need something similar. This is where Cheat Engine comes in. I started going through the tutorial which is packaged with the tool. I highly recommend it and the series of official YouTube tutorial videos.

New Scan

Start with the value of 0 since that is the number of cubes we have. The Value Type should be 4 bytes for integer values. The Scan Type can be Exact Value.

Next Scan

Now grab a cube using the arrow keys and space bar to jump. Update the Value to 1 and click Next Scan.

We can see all the memory addresses that changed from 0 to 1. Rinse and repeat. By the time we get to 3, there are a lot fewer memory addresses that are listed:

By the time we get to all 6 cubes, we have three possible memory addresses:

Change Value

Not much change from 5 to 6. We select the first address and try changing the value from 6 to 20 by selecting it and double-clicking on the value in the bottom pane list box.

Flag

Once the value is changed the game gives us the flag:

Reverse Engineer

Strings

The first step would be to just search for a flag prefix of HTB{.

Windows using strings and running under the directory of the Cube Madness.

strings64.exe -a * | findstr /i "HTB{"

Linux

find . -type f | strings | grep "HTB{"

This yielded no results, so it could be that the flag is encoded.

DotPeek

DotPeek is useful for .NET applications, but it could be that this is a Win32 application complied to C++ using IL2CPP.

IL2CPPDUMPER

Knowing this, we can fire up Ghidra, but it would take me a while to search for what would be relevant. IL2CPPDumper is better suited and even has an online version. IL2CppInspector is also helpful. Cpp2IL also has the same functionality and is supposedly significantly faster than either option. Since the .NET Code was compiled to Intermediate Language (IL) and then to C++, we need to map the C++ code back using the metadata provided.

Il2CppDumper.exe <path_to_GameAssembly.dll> <path_to_global_metadata.dat> <output_path>

After it's been analyzed, the files should be dumped to the specified output path in the DummyDll directory:

Now we can use dnSpy or DotPeek to some more details. IL2CPPDumper should also produce il2cpp.h, dump.cs, script.json. It also comes with Python scripts to be able to load up the metadata/function names for IDA and Ghidra.

DnSpy

DnSpy is a .NET debugger and assembly editor and if we open just the .exe we would see the same thing as DotPeek.

If we open the Assembly-CSharp from the IL2CPPDumper output directory (DummyDll) and look at the {} - subtree:

Seems like we want FlagCheck method implementation, but if we try to look for the implementation they are missing. All these are just stubs/interfaces to the actual C++ code.

Even so, you can glean some information from what we've done so far. You can tell that the developer(s) speaks/understands Korean from some of the method names. One can infer that they were Korean, but this could also be disinformation when it comes to malware attribution.

Ghidra

To actually see the decompiled assembly code and then map that back to C++ we use Ghidra.

  1. Open Ghidra and select the GameAssembly.dll to open.

    1. Don't have Ghidra perform the auto-analysis when the binary is opened. This is to avoid the Conflicting data exists at address error.
  2. From the Code Browser, select File -> Parse C Source

  3. Create a new profile or clear the existing one.

    1. Add the Generated C++ type header file from IL2CPPDumper. It should be il2cpp.h.

    2. Use -D_GHIDRA_ for the `Parse Options`. This might not be necessary anymore, but not sure.

    3. Click the Parse to Program and accept any other prompts/warnings (may take awhile).

    4. Open the Script Manager and add the directory of IL2CppDumper, NOT the DumpDll (output directory).

      1. Click Refresh to make the script appear in the Script Manager. Should be ghidra_with_struct.py

      2. Select the script and click the Run the button or right-click it and select Run. The script will ask for the generated script.json.

The Code Browser window will be available, but you'll get prompted to run Analysis. The progress will be in the lower right corner.

We can see a lot more than we did before and actual functions. Before we could only see the Exports populated.

We have the implementation, but it's still like finding a needle in a haystack:

Even searching for the known flag value, partial flag value, CheckFlag, flag in the strings search yielded nothing relevant. Neither did a scalar search of 20. I either don't have the actual implementation or the flag is encoded.

Sad/disappointed panda.

I'd imagine the original C# code was like this:

using UnityEngine;

public class CubeCounter: MonoBehaviour
{
    var _flagCheck = FlagCheck()
    public CubeCounter(IFlagCheck flagCheck)
    {
        _flagCheck = flagCheck;
    }

    public void CheckCount(int score)
    {
        if (score== 20)
        {
            _flagCheck.GetFlag()
        }
    }
}

public class FlagCheck: MonoBehaviour
{
    string _flag;
    public FlagCheck(TextAsset flagAsset) {
        _flag = flagAsset.text;
    }

    public void GetFlag()
    {
        // 축하합니다 깃발을 찾았네요
        if (score== 20)
        {
            GameObject.Find("Canvas").transform.Find("flagText").GetComponent<Text>().text = _flag;
        }
    }
}

Takeaways

It was a fun challenge and exposed me to Cheat Engine. It's worth going through the packaged tutorial where you analyze an application to learn all the features of the tool. You can also create code injection and patch the binary with the pretty cool tool. In our case, it could be patched to instantly give us the flag. It's much easier to reverse-engineer a Mono/C# Unity game compiled to just IL than if IL2Cpp was used in the compilation process. No flag from reverse engineering the assembly, but did learn about tools to assist in reverse engineering a binary compiled with IL2Cpp. Maybe someone out there can dive deeper on this.

References