I'm learning about format string exploits, and am attempting to use one on an example program to overwrite the .fini_array
section in virtual memory with a stack address containing shellcode (and hence redirect execution to the shellcode once main
exits). Unfortunately, trying to write to this address raises a segmentation fault, and I have written the following program to try to understand what's going on. (Disclaimer, I am running x64 linux with ASLR disabled while I am learning.)
//fini_test.c
#include <stdio.h>
#define OFFSET 0x555555554000
int main(int argc, char **argv) {
unsigned long int addr;
char **ptr;
static int canary=0xdeadbeef;
if(argc<2) {
printf("Usage: %s <position to write to in object file>\n", argv[0]);
exit(0); }
addr=stroul(argv[1], ptr, 16)+OFFSET;
printf("[*] about to print\n");
printf("writing to %p\n%n", (void *) addr, (int *) addr);
printf("[*] print complete\n");
printf("canary @ %p\t%d\t0x%08x\n\n", &canary, canary, canary);
exit(0);
}
So, the program simply takes a position in the object file as a command line argument, and then writes some bytes to the corresponding position in the virtual memory space of the process. With GDB I found that the right offset between the location in the object file and the location in the virtual memory space was 0x555555554000
.
I then compiled and used objdump
to find the desired sections in the object file.
$gcc fini_test.c -o fini_test
$objdump -h ./fini_test
Sections:
Idx Name Size VMA LMA File off Algn
...
19 .fini_array 00000008 0000000000200da8 0000000000200da8 00000da8 2**3
CONTENTS, ALLOC, LOAD, DATA
...
22 .data 00000014 0000000000201000 0000000000201000 00001000 2**3
CONTENTS, ALLOC, LOAD, DATA
...
So, the position of the .data
section in ./fini_test
is 0x201000
and the position of the .fini_array
section is 0x200da8
. I also noted that, like the .data
section, the .fini_array
section does not have a READONLY
flag, so I assumed that I should be able to write to it with the appropriate command line argument. However, this is how execution behaved:
$./dtors_test 0x2010000
[*] about to print
writing to 0x555555755000
[*] print complete
canary @ 0x555555755010 -559038737 0xdeadbeef
$./dtors_test 0x201010
[*] about to print
writing to 0x555555755010
[*] print complete
canary @ 0x555555755010 26 0x0000001a
$./dtors_test 0x200da8
[*] about to print
writing to 0x555555754da8
Segmentation fault (core dumped)
First, to verify that my code and choice of OFFSET
were right, I tried to overwrite the value of canary
. As a first approximation I just inputted the position of the .data
section, which was unsuccessful because – as inspecting the outputted address of canary
shows – the value of canary
is stored beneath the two pointers addr
and ptr
. Hence I inputted the position of the .data
section plus 0x10
to account for this, and indeed successfully overwrote the value of canary
with the expected value of 26
(precisely the number of bytes in the string "writing to 0x555555755010\n"
.)
So everything to that point is working as expected. However, I then try to write to the start of the .fini_array
section* and a segmentation fault is raised. This surprises me because this section is not marked as READONLY
in the object file; can anyone help me understand what's going on? Thank you in advance
*I know that destructor addresses are not stored at exactly the start of the .fini_array
section, and so even if I could write to that address it would not yield the desired results. But for now, before I cross that bridge, I'm just trying to understand why I can't write to an address that seemingly does not have a READONLY
flag enabled. As a broader question, general references to understanding how .fini_array
works in ELF files would be greatly appreciated!