Auth

Category
Reverse Engineering
Points
30
Solves
-1
Tags

ZeroTrace intercepts a stripped-down authentication module running on a remote industrial gateway. Assembly scrolls across glowing monitors as she unpacks the logic behind the plant’s digital checkpoint.


The challenge revolves around bypassing the authentication logic implemented in the main function. The key comparison happens at memcmp(&s1, &s2, 8uLL), where s1 is derived from user input and s2 is a hardcoded value 0xEFCDAB8967452301LL. To pass this check, we need to understand the transform function and reverse its logic.

int __fastcall main(int argc, const char **argv, const char **envp)
{
  FILE *stream; // [rsp+8h] [rbp-168h]
  __int64 s1; // [rsp+10h] [rbp-160h] BYREF
  unsigned __int64 s2; // [rsp+18h] [rbp-158h] BYREF
  char s[64]; // [rsp+20h] [rbp-150h] BYREF
  char v8[264]; // [rsp+60h] [rbp-110h] BYREF
  unsigned __int64 v9; // [rsp+168h] [rbp-8h]

  v9 = __readfsqword(0x28u);
  s2 = 0xEFCDAB8967452301LL;
  printf("[?] Enter unlock code: ");
  if ( fgets(s, 64, _bss_start) )
  {
    s[strcspn(s, "\r\n")] = 0;
    if ( strnlen(s, 0x40uLL) == 8 )
    {
      s1 = *(_QWORD *)s;
      transform(&s1, 8LL);
      if ( !memcmp(&s1, &s2, 8uLL) )
      {
        stream = fopen("flag.txt", "r");
        if ( stream )
        {
          if ( fgets(v8, 256, stream) )
            printf("[+] Access Granted! Flag: %s", v8);
          else
            fwrite("Error reading flag\n", 1uLL, 0x13uLL, stderr);
          fclose(stream);
          return 0;
        }
        else
        {
          perror("fopen");
          return 1;
        }
      }
      else
      {
        puts("[!] Access Denied!");
        return 1;
      }
    }
    else
    {
      puts("[!] Access Denied!");
      return 1;
    }
  }
  else
  {
    fwrite("Error reading input\n", 1uLL, 0x14uLL, stderr);
    return 1;
  }
}

The main function reads an unlock code from the user, processes it using the transform function, and compares the result with the hardcoded value s2. If the comparison succeeds, the flag is read from flag.txt.

unsigned __int64 __fastcall transform(__int64 a1, unsigned __int64 a2)
{
  unsigned __int64 result; // rax
  unsigned __int64 i; // [rsp+18h] [rbp-8h]

  for ( i = 0LL; ; ++i )
  {
    result = i;
    if ( i >= a2 )
      break;
    *(_BYTE *)(a1 + i) ^= 0x55u;
  }
  return result;
}

The transform function modifies the input by XORing each byte with 0x55. This means that to reverse the transformation, we can simply XOR the bytes of the hardcoded value s2 with 0x55 to get the original input that would pass the memcmp check.

To bypass the memcmp check, we need to calculate the input that, after being transformed, matches s2. This can be achieved by reversing the transformation:

from pwn import *

context.binary = './auth'
context.log_level = 'debug'

# r = process('./auth')
r = remote('10.10.57.42', 9005)

def transform(a1, a2):
    result = bytearray(a1)
    for i in range(a2):
        result[i] ^= 0x55
    return bytes(result)

code = bytes.fromhex('EFCDAB8967452301')[::-1]
decrypted_code = transform(code, 8)

log.info(f'Decrypted code: {decrypted_code.hex()}')
r.sendline(decrypted_code)
r.interactive()
Auth Flag: THM{REDACTED}