본문 바로가기
Wargame Write-Up/Pwnable.xyz

(Pwnable.xyz) two gargets

by snwo 2022. 1. 14.
Canary                        : ✓
NX                            : ✓
PIE                           : ✘
Fortify                       : ✘
RelRO                         : Partial

first, check the security options, PIE isn’t enabled.


int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  int int32; // eax
  char s[32]; // [rsp+10h] [rbp-40h] BYREF
  _QWORD v5[2]; // [rsp+30h] [rbp-20h] BYREF
  __int64 v6; // [rsp+40h] [rbp-10h]
  unsigned __int64 v7; // [rsp+48h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  setup(argc, argv, envp);
  memset(s, 0, 0x38uLL);
  while ( 1 )
  {
    while ( 1 )
    {
      print_menu();
      int32 = read_int32();
      if ( int32 != 2 )
        break;
      printf("nationality: ");
      __isoc99_scanf("%24s", v5);
    }
    if ( int32 > 2 )
    {
      if ( int32 == 3 )
      {
        printf("age: ");
        __isoc99_scanf("%d", v6);
      }
      else if ( int32 == 4 )
      {
        if ( (unsigned __int8)auth(s) )
          win();
      }
      else
      {
LABEL_14:
        puts("Invalid");
      }
    }
    else
    {
      if ( int32 != 1 )
        goto LABEL_14;
      printf("name: ");
      __isoc99_scanf("%32s", s);
    }
  }
}

in the main function, there are 4 menus.

change name, change nationality, change age, get shell.

if the return address of auth(s) ≠ 0, we can get the flag.

There are two ways to solve the problem.

The first one is to overflow nationality and edit the v6

so we can AAW, printf@got → win

Another way is to find a correct string that is same as s2 after following encrypting

_BOOL8 __fastcall auth(__int64 a1)
{
  signed int i; // [rsp+18h] [rbp-38h]
  char s1[8]; // [rsp+20h] [rbp-30h] BYREF
  __int64 v4; // [rsp+28h] [rbp-28h]
  __int64 v5; // [rsp+30h] [rbp-20h]
  __int64 v6; // [rsp+38h] [rbp-18h]
  unsigned __int64 v7; // [rsp+48h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  *s1 = 0LL;
  v4 = 0LL;
  v5 = 0LL;
  v6 = 0LL;
  for ( i = 0; i <= 0x1F; ++i )
    s1[i] = ((*(a1 + i) >> 4) | (16 * *(a1 + i))) ^ *(main + i);
  return strncmp(s1, &s2, 0x20uLL) == 0;
}

I think reversing is better.

this is simple encrypting, just swap 4 bit and xor with the main function opcode.

import sys
from pwn import *
with open("./challenge", "rb") as f:
    data = f.read()
if sys.argv.__len__() == 4:
    r = remote(sys.argv[1], int(sys.argv[3]))
else:
    r = process("./challenge")
context.log_level = 'debug'
b = ELF("./challenge")
s2 = [0x11, 0xde, 0xcf, 0x10, 0xdf, 0x75, 0xbb, 0xa5, 0x43, 0x1e, 0x9d, 0xc2, 0xe3, 0xbf, 0xf5,
      0xd6, 0x96, 0x7f, 0xbe, 0xb0, 0xbf, 0xb7, 0x96, 0x1d, 0xa8, 0xbb, 0xa, 0xd9, 0xbf, 0xc9, 0xd, 0xff]
main = data[0xB04:0xB04+0x20]
print(main)
print(s2[0] ^ main[0])
name = ''.join(list(map(lambda i: chr((
    (s2[i] ^ main[i]) >> 4 | (s2[i] ^ main[i]) << 4) & 0xff), range(0x20))))
print(name)
r.send(b"1 ")
pause()
r.send(name[:0x20])
r.send(b"4 ")
r.interactive()

yeah