9

This is Ubuntu 9.04, 2.6.28-11-server, 32bit x86


$ cat test.c
main() { int *dt = (int *)0x08049f18; *dt = 1; }
$ readelf -S ./test
...
  [18] .dtors            PROGBITS        08049f14 000f14 000008 00  WA  0   0  4
...
$ ./test
Segmentation fault
$

For the uninitiated: gcc creates a destructor segment, .dtors, in the elf executable, which is called after main() exits. This table has long been writable, and it looks like it should be in my case (see readelf output). But attempting to write to the table causes a segfault.

I realize there has been a movement toward readonly .dtors, plt, got lately, but what I don't understand is the mismatch between readelf and the segfault.

4
  • The real question is why do you want it to be writable?
    – alex
    Feb 25, 2011 at 6:53
  • 1
    I'm teaching a security class that involves breaking a series of vulnerable programs, but one exercise involves writing to .dtors to exec shellcode. It no longer works and I'm trying to track down the issue.
    – Fixee
    Feb 25, 2011 at 17:27
  • The mismatch is because there are probably some data relocations (which need to be fixed up before marking read-only, and cannot be lazy anyway, so will be constant once fixed-up) there.
    – ninjalj
    Feb 25, 2011 at 20:31
  • Who else is here from overthewire.org/wargames/vortex/vortex3.html?
    – David Xia
    Dec 26, 2020 at 19:37

2 Answers 2

5

Those sections are marked GNU_RELRO (readonly relocations), which means that as soon as the dynamic loader has fixed up (at load time, there are no lazy relocations there) all the relocations, it marks those sections read-only. Note that most of .got.plt is on another page, so doesn't get the treatment.

You can see the linker script with ld --verbose, if you search for RELRO you'll find something similar to:

.got            : { *(.got) }
. = DATA_SEGMENT_RELRO_END (12, .);
.got.plt        : { *(.got.plt) }

which means that the RELRO sections end 12 bytes into .got.plt (pointers to dynamic linker functions are already resolved, so can be marked read-only).

The hardened Gentoo project has some documentation about RELRO at http://www.gentoo.at/proj/en/hardened/hardened-toolchain.xml#RELRO.

5

I can tell why it's failing, although I don't actually know what part of the system is responsible. While .dtors is marked writable in the binary, it looks like it (along with .ctors, the GOT, and a few other things) are being mapped into a separate, non-writable page in memory. On my system, .dtors is getting put at 0x8049f14:

$ readelf -S test
  [17] .ctors            PROGBITS        08049f0c 000f0c 000008 00  WA  0   0  4
  [18] .dtors            PROGBITS        08049f14 000f14 000008 00  WA  0   0  4
  [19] .jcr              PROGBITS        08049f1c 000f1c 000004 00  WA  0   0  4
  [20] .dynamic          DYNAMIC         08049f20 000f20 0000d0 08  WA  6   0  4
  [21] .got              PROGBITS        08049ff0 000ff0 000004 04  WA  0   0  4
  [22] .got.plt          PROGBITS        08049ff4 000ff4 00001c 04  WA  0   0  4
  [23] .data             PROGBITS        0804a010 001010 000008 00  WA  0   0  4
  [24] .bss              NOBITS          0804a018 001018 000008 00  WA  0   0  4

If I run the executable and check /proc/PID/maps, I see:

08048000-08049000 r-xp 00000000 08:02 163678     /tmp/test
08049000-0804a000 r--p 00000000 08:02 163678     /tmp/test
0804a000-0804b000 rw-p 00001000 08:02 163678     /tmp/test

.data/.bss are still writable in their own page, but the others in 0x8049000-0x804a000 aren't. I assume this is a security feature in the kernel (as you said, "there has been a movement toward readonly .dtors, plt, got lately"), but I don't know specifically what it's called (OpenBSD has something very similar called W^X; Linux has PaX, but not built into most kernels)

You can get around it with mprotect, which lets you change the in-memory attributes of a page:

mprotect((void*)0x8049000, 4096, PROT_WRITE);

With that, my test program doesn't crash, but if I try to overwrite the end sentinel of .dtors (0x8049f18) with the address of another function, that function still doesn't execute; that part I can't figure out.

Hopefully somebody else knows what's responsible for making the page readonly, and why modifying .dtors doesn't seem to do anything on my system

2
  • 3
    If the OP Linux with PaX mprotect can't make an executable page writable or make a page executable which was writable before unless that feature is disabled with paxctl -m.
    – stribika
    Feb 25, 2011 at 13:23
  • @stribika Ah, good to know Feb 25, 2011 at 15:12

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .