Home > Mobile >  Why would this shellcode work if it does NOT pass 0 as the environment pointer for execve?
Why would this shellcode work if it does NOT pass 0 as the environment pointer for execve?

Time:11-28

https://www.exploit-db.com/exploits/46907

My understanding is that due to x64 calling convention, the 3rd paramfor execve, the envp, should be stored in rdx. But this shellcode doesn't zero this register, it only zero the rsi register (that stores arv). So that would cause a segfault if the current value of rdx doesn't point to a valid location, no ?

Am i missing something ?

CodePudding user response:

The asm does write RDX (with 0): note the cdq right before syscall. The sign bit of EAX=59 is 0, so EDX = 0, and writing EDX zero-extends into RDX.

That's a standard code-golf trick for zeroing EDX/RDX with a 1-byte instruction instead of xor edx,edx, given a known non-negative EAX.


Linux special-cases NULL argv or envp pointers as working like an empty list (pointer to a NULL in memory). See the man page: https://man7.org/linux/man-pages/man2/execve.2.html#NOTES

The man page discourages the practice for C programs because it's not portable to other unixes, but shellcode already isn't, and it saves bytes of machine-code size.

In a _start in a static executable under Linux, all regs other than RSP will be 0. (This isn't guaranteed by the x86-64 SysV ABI, it's just the convenient value the kernel chooses to avoid info leaks before entering user-space.) So it would have worked that way even if it did have the bug you thought.

But they also test by putting the machine code bytes into an array in .data in a C program, and calling it from main(). This would also happen to work with buggy shellcode that left RDX untouched: compiler-generated code for calling via function-pointer would likely leave RDX unmodified.
On entry to main, EDI=argc, RSI=argv, RDX=envp. So this block of machine code would start with RDX already being a valid pointer to char **envp! Maybe a bit less of a test than they intended. :P

The 3rd arg to main being envp is not specified by POSIX but is widely supported: Is char *envp[] as a third argument to main() portable
x86-64 Linux's system-calling convention is quite similar to its function-calling convention, intentionally so that system-call wrapper functions only need mov r10, rcx / mov eax, __NR_... / syscall.


And BTW, bad args to a syscall never result in a segmentation fault (SIGSEGV signal being delivered to your process). Instead, Linux system calls return a -EFAULT error code when you pass a pointer to unmapped memory, in a case where that's not OK.

(Fun fact: write(1, buf, way_past_end) will successfully write up to the end of the pages actually mapped starting at the buf address, and return that length. You only get -EFAULT if 0 bytes were written to the fd before encountering on in an unreadable page.)

  • Related