1

The assembly code I'm trying to run is simply a syscall 60.

# exit.s
.intel_syntax noprefix
.section .text
.globl _start

_start:

xor rax, rax
mov al, 0x3c
xor rdi, rdi
xor rdi, 1

syscall

assembling this with as exit.s -o exit.o, linking with ld exit.o -o exit and objdump -d exit to get the final code.

// ret.c
const char shellcode[] = "\x48\x31\xc0\xb0\x3c\x48\x31\xff\x48\x83\xf7\x01\x0f\x05";
int main() {
    (*(void(*)())shellcode)();
}

I compile using gcc -fno-stack-protector -z execstack -no-pie -o ret ret.c

On Manjaro Linux (kernel 5.10) and Ubuntu (kernel 5.8), I get a seg fault when trying to run the final executable.

I tried the same on Ubuntu 16.04 (kernel 4.4) and it works flawlessly.

I did some research, and it looks like this commit might have changed the behavior, but I'm not sure.

My question: How do I get the above code to work on the latest kernel version?

3
  • Your code is not supposed to work, and it is a good thing if it doesn't. If you can get it to work, you have managed to bypass the protection that was put there on purpose. – Johan Myréen Jan 18 at 6:12
  • 1
    man mprotect to get details on how to make the shellcode executable. – icarus Jan 18 at 6:13
  • 1
    Try with __attribute__((section(".text"))) const char shellcode[] = .... Yes this will defeat the "shellcode/exploit" idea, but (as already mentioned) it's just how it's supposed to be. – user414777 Jan 18 at 7:20
3

Since you’re asking for an executable stack, putting your code there will make it executable:

int main() {
    const char shellcode[] = "\x48\x31\xc0\xb0\x3c\x48\x31\xff\x48\x83\xf7\x01\x0f\x05";
    (*(void(*)())shellcode)();
}

Alternatively, you can change the page protections on the page(s) containing the shellcode:

#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>

const char shellcode[] = "\x48\x31\xc0\xb0\x3c\x48\x31\xff\x48\x83\xf7\x01\x0f\x05";

int main() {
  long page_size = sysconf(_SC_PAGESIZE);
  void *page_start = (void *) ((long) shellcode & -page_size);
  if (mprotect(page_start, page_size * 2, PROT_READ | PROT_EXEC)) {
    perror("mprotect");
  } else {
    (*(void(*)())shellcode)();
  }
}

The whole point of changes such as the one you found is to fix whole classes of vulnerabilities. Even with the approach above, you won’t be able to inject code into a running process and execute it if the process isn’t “cooperating” to some extent (by requesting executable stacks).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.