Ret2syscall学习
原理
Ret2syscall,即控制程序执行系统调用,获取 shell。
主要还是要理解系统调用的原理:
系统调用
既然是执行系统调用,在这里就大概记录一下Linux系统下的系统调用,Linux的系统调用通过int 80h
来实现,用系统调用号来区分入口函数,操作系统实现系统调用的基本过程是:
- 应用程序调用库函数(API);
- API 将
系统调用号
存入EAX
,然后通过中断调用使系统进入内核态- 内核中的中断处理函数根据系统调用号,调用对应的内核函数(系统调用)
- 系统调用完成相应功能,将
返回值存入 EAX
,返回到中断处理函数- 中断处理函数返回到 API 中
- PI 将 EAX 返回给应用程序
应用程序调用系统调用的过程是:
- 把系统调用的编号存入
EAX
- 把函数参数存入其它通用寄存器
- 最后 int 0x80
寄存器:
寄存器执行顺序 | |
---|---|
32位 | eax->ebx->ecx->edx |
64位 | rdi->rsi->rdx->rcx->r8->r9 |
32位执行时
eax
的参数为系统调用号
ebx
指向/bin/sh
的地址 ecx
参数为0 edx
参数也应该为0
即为execve("/bin/sh",NULL,NULL)
题目解析
- 使用cyclic来测试溢出点
拖进32位IDA分析程序,看到有/bin/sh的gadgets,就使用execve
如果程序中不存在/bin/sh,就调用read函数将/bin/sh写入bss段,然后在使用execve
然后就开始通过ROPgadgets找各个寄存器和int 0x80
的地址
ROPgadget –binary ./rop –only ‘pop|ret’|grep ‘eax’
ROPgadget –binary ./rop –only ‘pop|ret’|grep ‘ebx’
ROPgadget –binary ./rop –only ‘int’
写exp的思路是先将系统调用函数execve
传入eax
系统号是11,所以参数为0xb
将/bin/sh
作为参数传给ebx
ecx,edx
的参数都为null。然后指向int 0x80
地址
exp:
1 | from pwn import * |
多系统函数调用
关于32位的系统函数调用号
介绍一下关于read()函数
read(int fd,void*buf,size_t count)
fd:是文件描述符,为0
buf:为读出数据的缓冲区
count:每次读取的字节数
ret2sys_32
查看程序,发现没有/bin/sh
所以我们要自己将/bin/sh
写入bss
数据段。
构造payload:
- 调用系统函数read(),上面介绍的有关于read的系统函数调用号,所以我们
eax
要传入的参数为0x3
- 上面就read()函数也详细的介绍了一下。调用的过程就是在内存地址中读取之后用户输入
/bin/sh
先找到eax,ebx,ecx,edx以及int 0x80
的地址eax
参数为read()函数系统调用号0x3
,在edx,ecx,ebx
传入相对应的read()函数参数(使用命令readelf -S 文件名
查看bss段地址)- 调用完read()函数后,再次对
eax ebx ecx edx
进行填充,这次使用execve(),执行read()函数在内存地址中读取到的值,即/bin/sh\x00
- 发送payload,进行溢出。再次发送数据
- 要注意的是每次调用完系统函数后都需要加上
int 0x80
的地址
exp:
1 | #encoding:utf-8 |
ret2sys_64
关于64位函数调用号可以在w22师傅的博客上学习。
和32位的就不同之处
使用寄存器不同
返回函数名不同
32位是
int 0x80
64位是syscall retn
找到所需要的寄存器
然后找一个可用的bss段,还有sys retn的地址
bss地址:0X00000000006c2158
sys retn:0x000000000045bac5
然后构造exp
1 | from pwn import * |
偏有宸机 师傅的文章写的很好,受益匪浅 。例题均由宸机师傅提供。