Make all pages readable/writable/executable

1.6k views Asked by At

I would like to grant full permissions (read, write, and execute) to all memory pages in an ELF binary. Ideally, I'd like to be able to do this as a transformation on a binary or object file, in the same way that symbols can be changed with objcopy. I have not yet found a good way to do this. I would also be okay with a solution that involves running code at startup that calls mprotect on every page with the flags PROT_READ | PROT_WRITE | PROT_EXEC. I've tried this briefly, but I haven't found a good way to know which pages are mapped, and therefore which pages need to be mprotected.

It isn't required that dynamically allocated pages have all permissions, only the pages mapped at program startup.

2

There are 2 answers

0
David Hashe On BEST ANSWER

The following script implements Employed Russian's answer in code:

  • sets the p_type of the RELRO segment to PT_NULL
  • sets Flags on LOAD segments to PF_X|PF_W|PF_R.

It depends on pyelftools for python3, which can be installed with pip3 install pyelftools.

#!/usr/bin/env python3

import sys

from elftools.elf.elffile import ELFFile
from elftools.elf.descriptions import describe_p_type

if len(sys.argv) != 2:
    print("Usage: elf_rwe <elf>")

name = sys.argv[1]
with open(name, "rb") as f:
    elf = ELFFile(f)

    rwe_offsets = []
    relro_offsets = []
    for i in range(elf['e_phnum']):
        program_offset = elf['e_phoff'] + i * elf['e_phentsize']
        f.seek(program_offset)
        program_header = elf.structs.Elf_Phdr.parse_stream(f)

        if program_header['p_type'] == "PT_LOAD":
            rwe_offsets.append(program_offset)
        if program_header['p_type'] == "PT_GNU_RELRO":
            relro_offsets.append(program_offset)

    f.seek(0)
    b = list(f.read())

    # Zap RELRO
    pt_null = 0
    for off in relro_offsets:
        b[off] = pt_null

    # Fix permissions
    p_flags_offset = 4
    for off in rwe_offsets:
        b[off + p_flags_offset] = 0x7 # PF_X|PF_W|PF_R

with open(name, "wb") as f:
    f.write(bytes(b)) 
0
Employed Russian On

I would like to grant full permissions (read, write, and execute) to all memory pages in an ELF binary.

Note that some security policies, such as W^X in selinux will prevent your binary from running.

Ideally, I'd like to be able to do this as a transformation on a binary or object file

Run readelf -Wl on your binary. You'll see something similar to:

$ readelf -Wl /bin/date

Elf file type is EXEC (Executable file)
Entry point 0x4021cf
There are 9 program headers, starting at offset 64

Program Headers:
  Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
  PHDR           0x000040 0x0000000000400040 0x0000000000400040 0x0001f8 0x0001f8 R E 0x8
  INTERP         0x000238 0x0000000000400238 0x0000000000400238 0x00001c 0x00001c R   0x1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x000000 0x0000000000400000 0x0000000000400000 0x00dde4 0x00dde4 R E 0x200000
  LOAD           0x00de10 0x000000000060de10 0x000000000060de10 0x0004e4 0x0006b0 RW  0x200000
  DYNAMIC        0x00de28 0x000000000060de28 0x000000000060de28 0x0001d0 0x0001d0 RW  0x8
  NOTE           0x000254 0x0000000000400254 0x0000000000400254 0x000044 0x000044 R   0x4
  GNU_EH_FRAME   0x00cb8c 0x000000000040cb8c 0x000000000040cb8c 0x0002f4 0x0002f4 R   0x4
  GNU_STACK      0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW  0x10
  GNU_RELRO      0x00de10 0x000000000060de10 0x000000000060de10 0x0001f0 0x0001f0 R   0x1

What you want to do then is to change Flags on LOAD segments to have PF_X|PF_W|PF_R. The flags are part of Elf{32,64}_Phdr table, and the offset to the table is in stored in e_phoff of the Elf{32,64}_Ehdr (which is stored at the start of every ELF file).

Look in /usr/include/elf.h. Parsing fixed-sized ELF structures involved here isn't complicated.

You are unlikely to find any standard tool that would do this for you (given that this is such an unusual and unsecure thing to do), but a program to change flags is trivial to write in C, Python or Perl.

P.S. You may also need to "zap" the RELRO segment, which could be done by changing its p_type to PT_NULL.

I haven't found a good way to know which pages are mapped, and therefore which pages need to be mprotected.

On Linux, you could parse /proc/self/maps to get that info. Other OSes may offer a different way to achieve the same.