ret2syscall

一、checksec

32位程序,开启了NX(栈不可执行)保护。

二、ida查看C代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#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;
}

存在gets函数栈溢出漏洞

三、溢出思路

因为开启了栈不可执行保护,所以不可以将自己写的shellcode部署到内存中。由于C代码中也没有系统函数,也不可以调用程序本身的函数。所以此时就可以使用程序中的gadget片段(指令序列,由ret结尾)来获取shell。

四、系统调用

系统调用指运行在用户空间的程序向操作系统内核请求更高权限运行的服务。系统调用提供用户程序与操心系统之间的接口。大多数系统交互式需求在内核态运行。如设备IO操作或者进程间通信。

Linux系统调用:

linux的系统调用通过int 0x80h实现的,用系统调用号来区分入口函数。

操作系统实现系统调用的基本过程:

​ 1、应用程序调用库函数(API)

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

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

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

​ 5、中断处理函数返回到API中

​ 6、API将EAX返回到应用程序

应用程序调用系统调用的过程是:

​ 1、把系统调用的编号存入EXA

​ 2、把函数参数存入其他通用寄存器

​ 3、触发0x80号中断(0x80)

五、利用系统调用获取shell

利用这个系统调用:

1
execve(”/bin/sh“,NULL,NULL)

所以需要几点要求:

​ 1、系统调用号为eax保存着0xb

​ 2、第一个参数,ebx应该指向/bin/sh的地址

​ 3、第二个参数,ecx应该为0

​ 4、第三个参数,edx应该为0

思路:

在程序中寻找部分指令片段(pop,ret),保证上述寄存器的要求。找到pop eax;ret的片段,并且保证栈顶是0xb

1、寻找pop eax; ret

找到一个,选地址为0x080bb196的这一个gadget

2、寻找参数 pop片段

选用pop edx;pop ecx;pop ebx; ret地址为0x0806eb90

3、寻找’/bin/sh’地址

image-20230707130917137

地址为:0x080be408

4、寻找Int 0x80地址

地址为0x08049421

5、exp

偏移量根据esp+1c 和 ebp-64可以得出是112

image-20230510153336133

image-20230510153421353

成功拿到权限

栈中内容(从低地址到高地址):

1
2
3
4
5
6
7
pop eax;ret(这个指令的地址)
0xb
pop edx ; pop ecx ; pop ebx
0
0
/bin/sh(地址)
int 0x80(调用地址)

没次执行到pop指令的时候,都会拿出一个值给寄存器,所以eax,ebx,ecx,edx依此被赋值了,最后调用int 0x80获取shell