Published on

HTB - Cyber Apocalypse 2024 - Rev - Follow The Path

Authors

Challenge Description

alt text

Solution

Follow The Path was a medium reversing challenge that had a self-decrypting code, where each logic block was being unwrapped after 0x31 instructions. I did it manually, but after talking to 72ghoul, he wrote a pretty amazing unicorn script (I know I should really learn Unicorn by now). Well anyways, I really loved this challenge.

So, let's start by analyzing the binary in IDA:

int __fastcall main(int argc, const char **argv, const char **envp)
{
  FILE *v3; // rax
  char v5[128]; // [rsp+40h] [rbp-98h] BYREF

  puts("Please enter the flag");
  v3 = _acrt_iob_func(0);
  common_fgets<char>(v5, 127LL, v3);
  JUMPOUT(0x140001000LL);
}

Now, the code was simply jumping to an offset on the stack, the disassembly showed: jmp [rsp+0D8h+var_A0]. I set-up a breakpoint here, and ran the program

alt text

The program asks for the flag, and I know the format is HTB{.*}, so to get started, I sent the flag: HTB{testflagtrustme}

Now, stepping into the address, where the program was jumping to

alt text

Now, one thing that I noticed:

loc_7FF6BBD0101E:                       ; CODE XREF: .text:00007FF6BBD01015↑j
inc     rcx
lea     r8, loc_7FF6BBD01039
xor     rdx, rdx

loc_7FF6BBD0102B:                       ; CODE XREF: .text:00007FF6BBD01037↓j
xor     byte ptr [r8+rdx], 0DEh
inc     rdx
cmp     rdx, 39h ; '9'
jnz     short loc_7FF6BBD0102B

The loc_7FF6BBD0101E label was loading the data at loc_7FF6BBD01039 to r8 register. Then emptying rdx; setting it up as a counter variable. The next label i.e. loc_7FF6BBD0102B was simply xorring the instructions at r8+rdx with 0xDE. And kept xorring until rdx was 0x39. So, I understood that it was simply decrypting the instructions at runtime. I ran the code 0x39 times:

alt text

However, IDA wasn't happy with this. For this, I switched to x64dbg. The first thing I did was find the actually logic jump and setup a breakpoint there:

alt text

Now, after stepping into the call, I found the logic in the disassembly

alt text

After running the instructions 0x39 times, I saw the instructions build up slowly in x64dbg

alt text

Here I saw four instructions that really stood out to me:

00007FF6BBD01007   | 49:81F0 C4000000   | xor r8,C4                                |
00007FF6BBD0100E   | 49:81F8 8C000000   | cmp r8,8C                                |

00007FF6BBD01040   | 49:81F0 55000000   | xor r8,55                                |
00007FF6BBD01047   | 49:81F8 01000000   | cmp r8,1                                 |

After simply checking in python:

alt text

Now, I was pretty certain that this is the flag. So, I simply checked each instruction; step-by-step. But since my r8 would be different, and xor r8, N would result in a different r8. So on each cmp instruction, I had to change the value. One more problem that I had, I did not the size of the actual flag. So I had to go through each of these steps a few time ;-;. Here's the final script that got me the flag:

solve.py

elem = [
    0xC4^0x8c,
    0x55^0x1,
    0x1b^0x59,
    0x95^0xEE,
    0xD5^0xA6,
    0x9^0x3A,
    0xc^0x60,
    0x3c^0x7a,
    0xC7^0x98,
    0x1b^0x7f,
    0xb^0x38,
    0xF8^0xBB,
    0xCB^0x99,
    0xDF^0x86,
    0x28^0x58,
    0x11^0x65,
    0x8c^0xbd,
    0x80^0xb0,
    0xBF^0xF1,
    0xAE^0x83,
    0xF9^0xC8,
    0x26^0x55,
    0x95^0xCA,
    0xE9^0x82,
    0x97^0xA6,
    0x60^0xE,
    0x9D^0xF9,
    0x87^0xb3,
    0x52^0xD,
    0xE5^0x86,
    0x53^0x63,
    0x2E^0x1E,
    0xF6^0x9A,
    0x4^0x5B,
    0x78^0x11,
    0x22^0x17,
    0x85^0xEB,
    0x3E^0x4A,
    0xF^0x50,
    0x4D^0x7C,
    0x4^0x70,
    0xC8^0xB5
]

flag = "".join([chr(i) for i in elem])
print(flag)
alt text

Overall, a pretty fun an interesting challenge. I should've solved this with Unicorn, but idk. ;-;