I wrote this advanced program to only work on my computer but I think I might have made a mistake somewhere, as I can’t even confirm my own identity.

Category: reversing

Solver: t0b1

Flag: HTB{Id3nt1ty_c0nf1rmat1on}


In this challenge we get a Windows executable. We open it up in Ghidra to see what it does.

The main function is printing Starting to confirm identity... and then calls the RegOpenKeyExA function with Control Panel\Desktop as the argument.

local_1e8 = RegOpenKeyExA((HKEY)0x80000001,s_Control_Panel\Desktop_00405090,0,0x20019,(PHKEY)local_204);

This is a Windows function to open the specified Windows registry section. The (HKEY)0x80000001 value means it is opening the HKEY_CURRENT_USER.

Afterwards Ghidra shows some weird code.

local_1f4 = 1;
local_8 = 0;
pcVar1 = (code *)swi(3);

This doesn’t seem right and so we look at the corresponding Assembly code.

00401086 c7 85 10        MOV        dword ptr [EBP + local_1f4],0x1
         fe ff ff 
         01 00 00 00
00401090 c7 45 fc        MOV        dword ptr [EBP + local_8],0x0
         00 00 00 00
00401097 cc              INT3

The INT3 instruction is typically used by debugger to temporarly replace an instruction in a running program in order to set a code breakpoint. In a normal program run, this wouldn’t do anything and just continue running instead.

As this is kinda suspicious, we go through the following instructions and find the following flow.


We find the first jump at 0x40109f that just jumps over some bunch of code. Afterwards we find a CMP instruction, looks at an address very far up in the stack. This will most likely be 0 and so the JZ (jump if zero) will be executed, skipping the next bunch of code. All of this is only some obfuscation to confuse Ghidra and disguise the rest of the function.

Thus we patch the binary at 0x401086 to a JMP LAB_004010cc or eb 44 in hex. That way we will skip this bunch of code which is useless anyway and help Ghidra in decompiling.

This helps, and Ghidra now shows the complete code.

Instead of the weird code, we now find the code reading a value from the registry section opened before.

local_1e8 = RegQueryValueExA(local_204,s_WallPaper_004050a8,(LPDWORD)0x0,(LPDWORD)0x0,aBStack288, &local_208);

This time it actually reads a value, which is the WallPaper key. The complete registry path is thus HKEY_CURRENT_USER\Control Panel\Desktop\WallPaper. This key contains the path to the desktop wallpaper of the current user.

The code then has some anti-debugger checks.

BStack504 = IsDebuggerPresent();
if (BStack504 != 0) {

And later on we find this check as well.

pvStack508 = GetCurrentProcess();
if (iStack512 != 0) {

However, as we execute the binary locally, we can just patch the exit call with a NOP and circumvent those checks.

The code performs several operations on the read registry value. First it locates the last \ in the string.

lastBackslash = strrchr((char *)aBStack288,'\\');

It then locates the last first . in the string. If a dot is found, the dot will be overwritten with a NULL byte, effectively terminating the string early.

pcStack492 = strchr((char *)pBStack480,0x2e);

Then an important check is executed. The string after the last . is compared with the string \proof. If that string matches, the confirmation will succeed and we will probably get the flag.

iVar2 = strcmp(pcStack492,s_\proof_0040504c);

Now you might be thinking, how we could make the check pass and trick the functions we mentioned earlier, to maybe not find the \ after the .. However all of this is not necessary here. As we disabled all anti-debugger checks, we can simply attach a debugger, halt before the call to strcmp and overwrite the string in the memory.

final code

The reason why we can’t just invert the jump in the binary can be found in the image above. The pcStack492 variable which holds the string between the last \ and first . is used again in line 95. Without the right content, we would not receive the correct flag.

Halting before the strcmp and changing the memory does reveal the correct flag.