Format String Vulnerability Lab——格式化字符串漏洞

1 实验概述

格式化字符串漏洞是由像printf(user_input)之类代码引起的,这里user_input变量的内容由用户提供。当这个程序由特权运行(例如Set-UID程序),这个printf会导致以下情况之一:

(1)程序崩溃;
(2)从内存任意位置读取;
(3)修改任意内存位置的值。

应该注意,这个实验的输出依赖于操作系统,我们的描述和讨论是基于Fedora Linux (Core 4),如果你使用了不同的操作系统,可能会出现不同的情况和问题。

2 实验任务

2.1 任务1
在下面的程序中,你将被要求提供一个输入,它将被存放在一个叫做user_input的缓冲区中。接下来程序用printf打印出此缓冲区。这个程序是一个Set-UID程序(有效执行用户是root),即它用root权限运行。不幸的是,在用户输入调用printf的途径中有一个格式化字符串漏洞。我们想要攻击这个漏洞,看看能造成多大的损害。这个程序有两个secret值存在它的内存里,你对这两个值很感兴趣。然而,你不知道这两个值,也不能从二进制代码中找到它们(为简化,我们用常数0x44和0x55将这两个secret值硬编码)。尽管你不知道secret值,在实践中,找到它们的内存地址(范围或确切地址)并不困难(它们的地址是连续的),因为对于许多操作系统而言,任何时候运行程序地址都是完全一样的。本实验中,我们假设你已经知道了确切地址。为达到这个目的,程序“故意”为你打印出地址。有了这些知识,你的目标如下(不需要同时完成):
• (25 分)使程序崩溃
• (25 分)打印出secret[1]的值
• (25 分)修改secret[1]的值
• (25 分)将secret[1]的值修改为一个预先设定的值
注意到(Set-UID)程序的二进制代码对你仅仅是可读/可执行,你无法修改。也就是说,你不能通过修改代码达到上述目标。然而,你有一份源码的拷贝帮助你设计攻击:

/* vul_prog.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; /* other variables, not used here.*/
/* The secret value is stored on the heap */
secret = (int *) malloc(2*sizeof(int));
/* getting the secret */
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); /* getting an input from user */
printf("Please enter a string\n");
scanf("%s", user_input); /* getting a string from user */
/* Vulnerable place */
printf(user_input);
printf("\n");
/* Verify whether your attack is successful */
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;
}

我们先打开地址随机化:sysctl -w kernel.randomize_va_space=2
在这里插入图片描述
编译、设置为setuid程序。

[01/15/2019 19:05] root@ubuntu:/home/seed/lab3# gcc -o vul vul_prog.c
[01/15/2019 19:06] root@ubuntu:/home/seed/lab3# chmod 4755 vul

1 使程序崩溃

我们从代码中可以看到user_input大小为100,所以我们输入的字节数大于100时就会出现段错误。

perl -e 'print "12" . "\n" . "A"x1200 . "\n"' | ./vul

这种输入方式等价于执行该程序然后输入 12(第一个要求输入的整数) ->回车换行->输入1200个A(使程序崩溃)->回车换行
在这里插入图片描述

2 从内存任意位置读取

找出 int_input 在堆栈中的位置。
在这里插入图片描述
已经知道了我们输入的整数对应的位置,我们就可以得到任意地址中存放的值。
得到 Secret[1]的值。
先运行程序,读取secret[1]的地址为0x899c00c,将它转换成十进制,用%s可以将地址中的内容以字符串形式读出来。

在这里插入图片描述
在这里插入图片描述
U的ASCII码为85,转换成16进制为0x55,所以我们读出的U就是secret1中内容转化成字符串的结果。

3 修改 secret[1]的值

将%s 换成%n 可输出当前已经输出的字节数。
将 secret[1]的值修改成预先设定的值 0xab
此处计算为:0xab-(0x3a-0x08)
在这里插入图片描述
修改成功,想修改成其他值方法类似。

还有关闭地址随机化的情况,在这种情况下,不用每次都将secret1地址的值转换成对应的十进制了,因为地址不会每次都改变了,但是这种情况下可能会遇到特殊字符\x0c等,这种情况怎么解决呢?由于我的方法不是很提倡,这里就不放出来了,大家自己探索哦~

(以上是自己实验课做的步骤,可能会有错误,欢迎留言指正)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章