C.2 RELRO

RELRO is a generic exploit mitigation technique to harden the data sections of an ELF[109] binary or process. ELF is a common file format for executables and libraries that is used by a variety of UNIX-like systems, including Linux, Solaris, and BSD. RELRO has two different modes:

Partial RELRO
  • Compiler command line: gcc -Wl,-z,relro.

  • The ELF sections are reordered so that the ELF internal data sections (.got, .dtors, etc.) precede the program’s data sections (.data and .bss).

  • Non-PLT GOT is read-only.

  • PLT-dependent GOT is still writeable.

Full RELRO
  • Compiler command line: gcc -Wl,-z,relro,-z,now.

  • Supports all the features of Partial RELRO.

  • Bonus: The entire GOT is (re)mapped as read-only.

Both Partial and Full RELRO reorder the ELF internal data sections to protect them from being overwritten in the event of a buffer overflow in the program’s data sections (.data and .bss), but only Full RELRO mitigates the popular technique of modifying a GOT entry to get control over the program execution flow (see Section A.4).

To demonstrate the RELRO mitigation technique, I made up two simple test cases. I used Debian Linux 6.0 as a platform.

Test Case 1: Partial RELRO

The test program in Example C-1 takes a memory address (see line 6) and tries to write the value 0x41414141 at that address (see line 8).

Example C-1. Example code used to demonstrate RELRO (testcase.c)

01    #include <stdio.h>
02
03    int
04    main (int argc, char *argv[])
05    {
06      size_t *p = (size_t *)strtol (argv[1], NULL, 16);
07
08      p[0] = 0x41414141;
09      printf ("RELRO: %p
", p);
10
11      return 0;
12    }

I compiled the program with Partial RELRO support:

linux$ gcc -g -Wl,-z,relro -o testcase testcase.c

I then checked the resulting binary with my checksec.sh script:[110]

linux$ ./checksec.sh --file testcase
RELRO           STACK CANARY      NX            PIE                     FILE
Partial RELRO   No canary found   NX enabled    No PIE                  testcase

Next I used objdump to gather the GOT address of the printf() library function used in line 9 of Example C-1 and then tried to overwrite that GOT entry:

linux$ objdump -R ./testcase | grep printf
0804a00c R_386_JUMP_SLOT   printf

I started the test program in gdb in order to see exactly what was happening:

linux$ gdb -q ./testcase

(gdb) run 0804a00c
Starting program: /home/tk/BHD/testcase 0804a00c

Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()

(gdb) info registers eip
eip            0x41414141     0x41414141

Result: If only Partial RELRO is used to protect an ELF binary, it is still possible to modify arbitrary GOT entries to gain control of the execution flow of a process.

Test Case 2: Full RELRO

This time, I compiled the test program with Full RELRO support:

linux$ gcc -g -Wl,-z,relro,-z,now -o testcase testcase.c

linux$ ./checksec.sh --file testcase
RELRO           STACK CANARY      NX            PIE                     FILE
Full RELRO      No canary found   NX enabled    No PIE                  testcase

I then tried to overwrite the GOT address of printf() again:

linux$ objdump -R ./testcase | grep printf
08049ff8 R_386_JUMP_SLOT   printf

linux$ gdb -q ./testcase

(gdb) run 08049ff8
Starting program: /home/tk/BHD/testcase 08049ff8

Program received signal SIGSEGV, Segmentation fault.
0x08048445 in main (argc=2, argv=0xbffff814) at testcase.c:8
8          p[0] = 0x41414141;

This time, the execution flow was interrupted by a SIGSEGV signal at source code line 8. Let’s see why:

(gdb) set disassembly-flavor intel

(gdb) x/1i $eip
0x8048445 <main+49>:    mov    DWORD PTR [eax],0x41414141

(gdb) info registers eax
eax            0x8049ff8        134520824

As expected, the program tried to write the value 0x41414141 at the given memory address 0x8049ff8.

(gdb) shell cat /proc/$(pidof testcase)/maps
08048000-08049000 r-xp 00000000 08:01 497907     /home/tk/testcase
08049000-0804a000 r--p 00000000 08:01 497907     /home/tk/testcase
0804a000-0804b000 rw-p 00001000 08:01 497907     /home/tk/testcase
b7e8a000-b7e8b000 rw-p 00000000 00:00 0
b7e8b000-b7fcb000 r-xp 00000000 08:01 181222     /lib/i686/cmov/libc-2.11.2.so
b7fcb000-b7fcd000 r--p 0013f000 08:01 181222     /lib/i686/cmov/libc-2.11.2.so
b7fcd000-b7fce000 rw-p 00141000 08:01 181222     /lib/i686/cmov/libc-2.11.2.so
b7fce000-b7fd1000 rw-p 00000000 00:00 0
b7fe0000-b7fe2000 rw-p 00000000 00:00 0
b7fe2000-b7fe3000 r-xp 00000000 00:00 0          [vdso]
b7fe3000-b7ffe000 r-xp 00000000 08:01 171385     /lib/ld-2.11.2.so
b7ffe000-b7fff000 r--p 0001a000 08:01 171385     /lib/ld-2.11.2.so
b7fff000-b8000000 rw-p 0001b000 08:01 171385     /lib/ld-2.11.2.so
bffeb000-c0000000 rw-p 00000000 00:00 0          [stack]

The memory map of the process shows that the memory range 08049000-0804a000, which includes the GOT, was successfully set to read-only (r--p).

Result: If Full RELRO is enabled, the attempt to overwrite a GOT address leads to an error because the GOT section is mapped read-only.

Conclusion

In case of a buffer overflow in the program’s data sections (.data and .bss), both Partial and Full RELRO protect the ELF internal data sections from being overwritten.

With Full RELRO, it’s possible to successfully prevent the modification of GOT entries.

There is also a generic way to implement a similar mitigation technique for ELF objects, which works on platforms that don’t support RELRO.[111]

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.77.114