Home > Software design >  Using personality syscall to make the stack executable
Using personality syscall to make the stack executable

Time:10-22

I am trying to understand how to make process stack executable with personality syscall, so I wrote this code that creates a new process with and runs a bash on the stack and I get segment fault because I don't have execute permission on the stack. What am I doing wrong?

#include <stdio.h>
#include <sys/personality.h>

int main() 
{
    setvbuf(stdout, 0, 2, 0);
    unsigned char shellcode[] = "\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\x6a\x3b\x58\x99\x0f\x05"; // open bash
    
    if(personality(READ_IMPLIES_EXEC | ADDR_NO_RANDOMIZE) == -1) // return 0
    {
        printf("personality failed");
        exit(0);
    }

    int (*ret)() = (int(*)())shellcode;
    if(fork() == 0) // child proces
        ret();  
    
    return 0;
}

Compiled with gcc file.c -o file.o

$ uname -r
4.4.179-0404179-generic

$ readelf -l
...
GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x10

CodePudding user response:

There are two problems:

  1. You cannot change the personality of a process after it's started. Doing personality(READ_IMPLIES_EXEC) is not going to do anything per se, it just sets the current process' personality value (a simple 32bit integer) and that's it. In order for the change to take effect a new program will need to be executed (i.e. through execve). The current process (and its children) will not be affected.

  2. Linux will ignore the READ_IMPLIES_EXEC personality flag if the ELF includes a PT_GNU_STACK program header that specifies that the stack should not be executable, which is usually the default choice by compilers.

By default GCC will create ELFs with a PT_GNU_STACK program header whose flags are set to RW and not RWX. In order to have an executable stack, you will have to pass the -z execstack option to GCC when compiling, which will set PT_GNU_STACK to RWX. You can check this with readelf -l your_elf (note: readelf will show E instead of X for program header flags).

Therefore, in your case, gcc -zexecstack -o file file.c should do what you want, and you don't need to call personality() nor fork() really. Just put your shellcode on the stack and jump into it. In theory you could also locate the program headers in the ELF file and edit the flags for PT_GNU_STACK manually (7 = RWX) e.g. using an hex editor, but that'd be more work than needed.

So, at the end of the day:

I am trying to understand how to make process stack executable with personality syscall

You cannot. Personality only affects new executions, not already existing ones, and on top of that ELF properties such as the PT_GNU_STACK program header supersede personality. You can however re-compile your program as explained above.

NOTE: you can nonetheless use mprotect() to change the permissions of stack memory pages to RWX, given that you can somehow extrapolate the stack base address and size at runtime (e.g. taking the address of a local variable in a function and zeroing out the lowest 12 bits).


This is enough information for older kernels like yours (4.4), but since Linux v5.8 the situation is a bit more nuanced. Assuming you are on x86, you can take a look at this comment in the source code for an explanation:

/*
 * An executable for which elf_read_implies_exec() returns TRUE will
 * have the READ_IMPLIES_EXEC personality flag set automatically.
 *
 * The decision process for determining the results are:
 *
 *                 CPU: | lacks NX*  | has NX, ia32     | has NX, x86_64 |
 * ELF:                 |            |                  |                |
 * ---------------------|------------|------------------|----------------|
 * missing PT_GNU_STACK | exec-all   | exec-all         | exec-none      |
 * PT_GNU_STACK == RWX  | exec-stack | exec-stack       | exec-stack     |
 * PT_GNU_STACK == RW   | exec-none  | exec-none        | exec-none      |
 *
 *  exec-all  : all PROT_READ user mappings are executable, except when
 *              backed by files on a noexec-filesystem.
 *  exec-none : only PROT_EXEC user mappings are executable.
 *  exec-stack: only the stack and PROT_EXEC user mappings are executable.
 *
 *  *this column has no architectural effect: NX markings are ignored by
 *   hardware, but may have behavioral effects when "wants X" collides with
 *   "cannot be X" constraints in memory permission flags, as in
 *   https://lkml.kernel.org/r/[email protected]
 *
 */
#define elf_read_implies_exec(ex, executable_stack) \
    (mmap_is_ia32() && executable_stack == EXSTACK_DEFAULT)
  • Related