SEEDLab-缓冲区溢出2

SEEDLab-缓冲区溢出2

任务一

以下的程序有一个缓冲区溢出漏洞,我们的任务就是利用这个漏洞。注意需要打开地址随机化保护,利用jmp esp或者call esp(不要使用nop)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/* stackesp.c */

/* This program has a buffer overflow vulnerability. */
/* Our task is to exploit this vulnerability */
/* sudo sysctl -w kernel.randomize_va_space=2 then use jmp esp or call esp(no nops) */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

//use jmp esp or call esp
int someint=0xe4ffd4ff;

int bof(char *str)
{
char buffer[160];

/* The following statement has a buffer overflow problem */
strcpy(buffer, str);

return 1;
}

int main(int argc, char **argv)
{
char str[517];
FILE *badfile;

printf("%x\n",&someint);
badfile = fopen("badfile", "r");
fread(str, sizeof(char), 517, badfile);
bof(str);

printf("Returned Properly\n");
return 1;
}

图1 编译、设置Set-UID位并关闭保护 如图1所示,将漏洞程序进行编译,关闭栈保护,打开栈可执行。再设置Set-UID位,打开地址随机化。

图2 生成测试输入、获得someint地址与断点地址 图3 反汇编main()获得返回地址 图4 查看栈内容 观察代码,可以发现程序中存在可利用的全局变量someint,变量中存储了jmp esp指令,同时程序中还打印了someint的地址,全局变量不受地址随机化的影响,因此可以利用。如图2、3、4,运行stackesp程序,得到someint的地址,同时使用gdb调试程序,获取返回地址在栈中的位置,计算得到返回地址在buffer+172的位置。

图5 构造输入 badfile 有了以上条件,可以构造程序输入badfile。如图5所示,首先用someint的地址覆盖bof()函数的返回地址,需要172字节的填充字符。在bof()函数执行完毕之后将会执行leave; ret的操作,leave指令首先将ebp的值赋给esp,此时esp指向主调函数main()的栈底地址在栈中的位置,再执行一次pop指令,将esp指向的值赋给ebp,同时esp增加4。此时,ebp指向main()函数的栈底,esp指向bof()的返回地址。再执行ret指令,执行一次pop操作,将eip的值设置为返回地址,同时,esp再增加4,指向了返回地址所在位置-4的位置。

图6 成功获得root shell 覆盖之后,在bof()函数执行结束之后,会执行jmp esp的操作,从而从esp指向的位置继续执行,返回地址所在位置-4的位置被覆盖为shellcode,因此成功获取root shell,如图6所示。

任务二

下面的程序有一个缓冲区溢出漏洞。编译该程序时,需要开启栈不可执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/* overfun.c */

/* This program has a buffer overflow vulnerability. */
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int good(int addr) {
printf("Address of hmm: %p\n", addr);
}

int hmm() {
printf("Win.\n");
setuid(geteuid());
execl("/bin/sh", "sh", NULL);
}

extern char **environ;

int main(int argc, char **argv) {

int i, limit;

for(i = 0; environ[i] != NULL; i++)
memset(environ[i], 0x00, strlen(environ[i]));

int (*fptr)(int) = good;
char buf[32];

strcpy(buf,argv[1]);

int (*hmmptr)(int) = hmm;

(*fptr)((int)hmmptr);

return 0;
}

观察漏洞程序,发现hmm()函数可以获得shell,同时程序中打印出了hmm()函数的地址。main()中使用函数指针调用good(),因此可以覆盖栈中的函数指针来调用hmm()函数。

图7 获得各个函数的地址 图8 查看函数指针的位置 图9 成功获得root shell 如图7,编译代码并设置Set-UID位,运行程序得到hmm()的地址。再使用GDB进行调试。查看good()函数的地址,再在main()函数中设置断点查看栈中的情况,可以看到指向good()的函数指针距离buf36字节,如图8所示。由此可以构造输入,如图9所示,成功获得root shell。


SEEDLab-缓冲区溢出2
https://eleco.top/2025/10/31/SEEDLab-缓冲区溢出2/
作者
Eleco
发布于
2025年10月31日
许可协议