Introduction
ELF files are known for their duality: a set of sections from a linking point of view and a set of segments from a runtime point of view. The interesting part is the mapping between these two perspectives. While working on an ELF parser I noted the unexpected position of the .rodata
section.
Here is an excerpt of readelf output on an arbitrary binary:
$ readelf -S /bin/skype There are 32 section headers, starting at offset 0x2236a00: Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .interp PROGBITS 00000154 000154 000013 00 A 0 0 1 [ 2] .note.ABI-tag NOTE 00000168 000168 000020 00 A 0 0 4 [ 3] .note.gnu.build-i NOTE 00000188 000188 000024 00 A 0 0 4 [ 4] .gnu.hash GNU_HASH 000001ac 0001ac 001bdc 04 A 5 0 4 [ 5] .dynsym DYNSYM 00001d88 001d88 00c000 10 A 6 1 4 [ 6] .dynstr STRTAB 0000dd88 00dd88 01b313 00 A 0 0 1 [ 7] .gnu.version VERSYM 0002909c 02909c 001800 02 A 5 0 2 [ 8] .gnu.version_r VERNEED 0002a89c 02a89c 0001e0 00 A 6 7 4 [ 9] .rel.dyn REL 0002aa7c 02aa7c 265ca8 08 A 5 0 4 [10] .rel.plt REL 00290724 290724 004948 08 A 5 12 4 [11] .init PROGBITS 0029506c 29506c 00002e 00 AX 0 0 4 [12] .plt PROGBITS 002950a0 2950a0 0092a0 04 AX 0 0 16 [13] .text PROGBITS 0029e340 29e340 139b708 00 AX 0 0 16 [14] .fini PROGBITS 01639a48 1639a48 00001a 00 AX 0 0 4 [15] .rodata PROGBITS 01639a80 1639a80 b42c20 00 A 0 0 32 [16] .eh_frame_hdr PROGBITS 0217c6a0 217c6a0 00b264 00 A 0 0 4 [17] .eh_frame PROGBITS 02187904 2187904 03eb20 00 A 0 0 4 [18] .gcc_except_table PROGBITS 021c6424 21c6424 053a22 00 A 0 0 4 [19] .init_array INIT_ARRAY 0221bde4 221ade4 000870 00 WA 0 0 4 [...]
Note how .rodata
is flagged with only ‘A’ for ‘allocate’. No write flag is set, as expected, and no execute flag either. If we inspect the segments (or program headers):
$ readelf -l /bin/skype Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align PHDR 0x000034 0x00000034 0x00000034 0x00120 0x00120 R E 0x4 INTERP 0x000154 0x00000154 0x00000154 0x00013 0x00013 R 0x1 [Requesting program interpreter: /lib/ld-linux.so.2] LOAD 0x000000 0x00000000 0x00000000 0x2219e46 0x2219e46 R E 0x1000 LOAD 0x221ade4 0x0221bde4 0x0221bde4 0x1bafc 0x5d578 RW 0x1000 [...] Section to Segment mapping: Segment Sections... 00 01 .interp 02 .interp [...] .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame .gcc_except_table 03 .init_array .ctors .dtors .jcr .data.rel.ro .dynamic .got .got.plt .data .debug$s .data1 .bss 04 .dynamic [...]
The .rodata
section is mapped to the second segment. This segment is mapped at 0x0, has a size of 0x2219e46 bytes and protection of RE
(read and execute). A closer look at the mapping shows that this segment also includes .text, .fini, .init and .plt, which explains the need for the execute flag.
Consequences
Although the option to execute read-only data may seem limited, it is actually relevant when building a ROP chain during binary exploitation. In the binary described above:
$ objcopy -j .rodata -O binary /bin/skype skype.rodata $ ls -lh ./skype.rodata -rw-rw-r--. 1 user group 12M May 5 07:55 ./skype.rodata
That is 12M of potential extra gadgets! For instance, using rp++, here are some gadgets within .rodata
:
0x0003d0e8: call eax ; (511 found) 0x0003d28a: call ebp ; (471 found) 0x0003ced2: call ebx ; (684 found) 0x0003cca6: call ecx ; (350 found) 0x0003dfa2: call edi ; (1200 found) 0x00057e10: call edx ; (165 found) 0x0003da8f: call esi ; (228 found) 0x0003cdc2: call esp ; (202 found) [...] 0x0003cf40: jmp eax ; (792 found) 0x0003ddfb: jmp ebp ; (1598 found) 0x0003cd95: jmp ebx ; (730 found) 0x0003cdaa: jmp ecx ; (388 found) 0x0003d667: jmp edi ; (2246 found) 0x0003cf6c: jmp edx ; (372 found) 0x0003ceca: jmp esi ; (277 found) 0x0003ee32: jmp esp ; (209 found) 0x0000a8dc: ret ; (28681 found)
ROP tools
I did have a look at some ROP gadgets finder to see how the executables were parsed. Both rp++ and ROPGadget use the segments (program headers) to find gadgets.
Conclusion
This issue has already been raised on the binutils mailing list. The only answer is a reference to gold (new linker) option to set rodata into a separate segment. Unfortunately, the risk to come across compatibility issues makes the implementation of such functionality within the BFD linker unlikely.