RELRO: RELocation Read-Only

This article was originally posted to blog.isis.poly.edu on Jun 2, 2011.

This article describes ELF relocation sections, how to abuse them for arbitrary code execution, and how to protect them at runtime.

ELF Relocation Sections

A dynamically linked ELF binary uses a look-up table called Global Offset Table (GOT) to dynamically resolve functions that are located in shared libraries.

80484da:       e8 95 fe ff ff        call   8048374 <printf@plt>
08048374 <printf@plt>:
8048374: ff 25 54 97 04 08 jmp DWORD PTR ds:0x8049754
804837a: 68 20 00 00 00 push 0x20
804837f: e9 a0 ff ff ff jmp 8048324 <_init+0x30>
Contents of section .got.plt:
8049738 6c960408 00000000 00000000 3a830408 l...........:...
8049748 4a830408 5a830408 6a830408 7a830408 J...Z...j...z...
8049758 8a830408 9a830408 ........
  • Because the GOT contains data used by different parts of the program directly, it needs to be allocated at a known static address in memory.
  • Because the GOT is “lazy binded,” it needs to be writable.

GOT Overwrite

Since we know that the GOT lives in a predefined place and is writable, all that is needed is a bug that lets an attacker write four bytes anywhere. We’ll use the following vulnerable program to simulate that. Note that we are operating on the same binary we examined above.

// Include standard I/O declarations
#include <stdio.h>
// Include string declarations
#include <string.h>
// Program entry point
int main(int argc, char** argv) {
// Terminate if program is not run with three parameters.
if (argc != 4) {
// Print out the proper use of the program
puts("./a.out <size> <offset> <string>");
// Return failure
return -1;
}
// Convert size to an integer
int size = atoi(argv[1]);
// Convert offset to an integer
int offset = atoi(argv[2]);
// Place string into its own string on the stack
char* str = argv[3];
// Declare a 256 byte buffer on the stack
char buffer[256];
// Print the location of the buffer for calculating the offset.
printf("Buffer:\t\t%8x\n", &buffer);
// Fill the buffer with the letter 'A'.
memset(buffer, 65, 256–1);
// Null-terminate the buffer.
buffer[255] = 0;
// Attempt to copy the string into the specified location.
strncpy(buffer + offset, str, size);
// Print out the buffer.
printf('%s', buffer);
// Return success
return 0;
}
(gdb) x 0x08049754
0x8049754 <_GLOBAL_OFFSET_TABLE_+28>: 0x0804837a
(gdb) p -(0xbffff284 - 0x08049754) % 0x80000000
$1 = 1208263888
(gdb) r 4 1208263888 \$\$\$\$
Starting program: /home/hake/code/relro/c 4 1208263888 \$\$\$\$
Buffer: bffff284
Program received signal SIGSEGV, Segmentation fault.
0x08048374 in printf@plt ()
(gdb) x 0x08049754
0x8049754 <_GLOBAL_OFFSET_TABLE_+28>: 0x24242424

RELRO: RELocation Read-Only

To prevent the above exploitation technique, we can tell the linker to resolve all dynamically linked functions at the beginning of execution and make the GOT read-only. Note that we are operating on a different binary below compiled from the same source code.

80484fa:       e8 95 fe ff ff         call   8048394 <printf@plt>08048394 <printf@plt>:
8048394: ff 25 f0 9f 04 08 jmp DWORD PTR ds:0x8049ff0
804839a: 68 20 00 00 00 push 0x20
804839f: e9 a0 ff ff ff jmp 8048344 <_init+0x30>
Contents of section .got:
8049fd4 fc9e0408 00000000 00000000 5a830408 ............Z...
8049fe4 6a830408 7a830408 8a830408 9a830408 j...z...........
8049ff4 aa830408 ba830408 00000000 ............
(gdb) x 0x08049ff0
0x8049ff0 <_GLOBAL_OFFSET_TABLE_+28>: 0x0804839a
(gdb) p -(0xbffff284 - 0x08049ff0) % 0x80000000
$1 = 1208266092
(gdb) r 4 1208266092 \$\$\$\$
Starting program: /home/hake/code/relro/r 4 1208266092 \$\$\$\$
Buffer: bffff284
Program received signal SIGSEGV, Segmentation fault.
strncpy (s1=0x8049ff0 "ph\027", s2=0xbffff5fc "$$$", n=4) at strncpy.c:43
43 strncpy.c: No such file or directory.
in strncpy.c
(gdb) x 0x08049ff0
0x8049ff0 <_GLOBAL_OFFSET_TABLE_+28>: 0x00176870

Some Handy Commands

Display Dynamic Relocation Entries objdump -R YOUR_BINARY
Show Program Header Table readelf -l YOUR_BINARY
Show Section Header Table readelf -S YOUR_BINARY
Display Relocations readelf -r YOUR_BINARY

Thanks

Jon Oberheide

Citations

[1] Manual page ld(1)

Related Resources

RELRO — A (not so well known) Memory Corruption Mitigation Technique
How to Hijack the Global Offset Table with pointers
Chapter 9. Dynamic Linking: Global Offset Tables
The ELF format — How programs look from the inside
Resolving ELF Relocation Name / Symbols

Risk philosopher. CISO. Team and program builder. Ex-vulnerability researcher. Ex-CTF organizer and competitor.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store