This program overrides the return address of the main function to point to a character array of encoded x86-64 assembly instructions. The instructions simply encode a NOP followed by the syscall exit(1).
char shellcode[] = "\x90\x48\x31\xc0\x48\x89\xc7\x48\xff\xc7\x04\x3c\x0f\x05";
void main() {
long int *ret;
ret = (long int *)&ret + 2;
(*ret) = (long int)shellcode;
}
It is compiled with
gcc program.c -o program.exe -fno-stack-protector -z execstack
on a WSL. (The Ubuntu app from the Microsoft Store)
The -z execstack
flag should cause the stack, as well as all other writable portions of the program, to be executable.
Since the global character array resides in the .data of the assembly code it should be executable. This is true when running the program on a Linux VM, but apparently not on the WSL? On WSL, control is still properly redirected to the NOP, but it segfaults if you try to execute that instruction (presumably on code-fetch from a non-executable page). Running gdb shows this after returning from main:
=> 0x8004010 <shellcode>: nop
0x8004011 <shellcode+1>: xor %rax,%rax
(gdb) si
Program received signal SIGSEGV, Segmentation fault.
What could be causing this inconsistency between WSL and real Linux? Does WSL enforce strict W^X, overriding even -z execstack
?
Are other Windows security features at play? I do not know if it is a WSL 1 or 2, but the uname -a output is:
Linux LAPTOP-M91FQN9V 4.4.0-18362-Microsoft #836-Microsoft Mon May 05 16:04:00 PST 2020 x86_64 x86_64 x86_64 GNU/Linux
What could be preventing the instructions from running, when the same does not occur on a Linux VM?