execve shellcode linux segmentation fault
Your problem is that the .text
section is not writable by default. The easiest thing to do is put your code into a new custom section that is marked as writable. Add this line at the top of your asm file:
section .shellcode progbits alloc exec write align=16
You could also pass the -N
switch to the linker.
Alternatively, you could rewrite the shellcode so that it uses the stack to create the arguments.
Why does my shellcode segfault when executed from C, but not as a stand-alone executable?
It is normal, because your shellcode is not setting the registers rsi and rdx, and when your C program executes the shellcode will have garbage in the registers rdi and rdx. It is because the syscall execve needs more arguments.
int execve (const char *filename, const char *argv [], const char *envp[]);
As extra information, the segmentation fault is because after your execve syscall you will get an error in rax and you will move 60 to the last 8 bits of rax and call to this syscall that doesn't exist.
Executing shellcode segmentation fault
You forgot to set up edx
so it contains whatever the C code last used it for and that's unlikely to be a valid environment pointer. In the standalone code, edx
happened to be zero due to the initial startup state of the program. If you use strace
you can see that the execve
returns with -EFAULT
and then execution continues past your code into garbage which then truely segfaults. You can fix the shellcode for example like this:
char shellcode[] = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69"
"\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\x31\xd2\xcd\x80";
(I included a xor edx, edx
before the int 0x80
.)
execve x86 - Segmentation Fault
As pointed out in the comments the arguments need to be NULL terminated.
Also mov al, 0xb
only sets the lower 8 bits of the (32 bit) eax
register.
Earlier on you also loaded an address from the stack into eax mov eax, esp
and since the stack grows down, the value stored in eax
will be much closer to 0xFFFFFFFF
that it is to 0
. When you later mov al, 0xb
you only substitute the last F
and eax
needs to be exactly 0xb
.
Thus you need to either move the value to whole eax
register or make sure its upper 24 bits are zeroed beforehand - for example by doing xor eax, eax
.
global _start
section .text
_start:
push dword 0x0068732F ; Push /sh
push dword 0x6E69622F ; Push /bin
mov eax, esp ; Store Pointer To /bin/sh In EAX
push dword 0x0000632D ; Push -c
mov ebx, esp ; Store Pointer To -c In EBX
push dword 0x00000068 ; Push h
push dword 0x7361622F ; Push /bas
push dword 0x6E69622F ; Push /bin
mov ecx, esp ; Store Pointer To /bin/bash In ECX
push 0 ; <----- NULL args terminator
push ecx ; Push /bin/bash Pointer
push ebx ; Push -c Pointer
push eax ; Push /bin/sh Pointer
mov ebx, eax ; Move /bin/sh Pointer To EAX
mov ecx, esp ; Store /bin/sh -c /bin/bash Pointer in ECX
xor edx, edx ; Store 0 In EDX
;xor eax, eax ; <----- either xor eax, eax or mov into eax
mov eax, 11 ; sys_execve
int 0x80 ; system call
32 bit shellcode causes a segmentation fault when trying to push /bin//sh to the stack
After main
returns into your shellcode, ESP will probably be pointing just above that buffer. And EIP is pointing to the start of it; that's what returning into it means.
A couple push
instructions may modify the machine code at the end of the buffer, leading to a SIGILL with EIP pointing at a byte you just pushed.
Probably the easiest fix is add esp, -128
to go all the way past your buffer. Or sub esp, -128
to go higher up the stack. (-128
is the largest magnitude 8-bit immediate you can use, avoiding introducing zeros in the machine code with sub esp, 128
or 1024
. If you wanted to move the stack farther, you could of course construct a larger value in a register.)
I didn't test this guess, but you can confirm it in GDB by single-stepping into your shellcode with si
from the end of main
to step by instructions.
Use disas
after each instruction to see disassembly. Or use layout reg
. See the bottom of https://stackoverflow.com/tags/x86/info for more GDB debugging tips.
The given solution is more complicated because it apparently sets up an actual argv
array instead of just passing NULL pointers for char **argv
and char **envp
. (Which on Linux is treated the same as valid pointers to empty NULL-terminated arrays: http://man7.org/linux/man-pages/man2/execve.2.html#NOTES).
But the key difference is that it uses jmp/call/pop to get a pointer to a string already in memory. That's only one stack slot not three. (The end of its payload before the return address is data, not instructions, but it would fail in a different way if it did too many pushes and overwrote the string instead of just storing a 0
terminator. The call
jumps backwards before the pushed return address actually modifies the buffer, but if it did overwrite anything near the end it would still break.)
@Margaret looked into this in more detail, and spotted that it's only the 3rd push that breaks anything. That makes sense: the first 2 are presumably overwriting the part of the payload that contained the new return address and the saved EBP value. And it just so happened that the compiler put main
's buffer contiguous with that.
If you actually used tcc
not gcc
, that's probably not a surprise. GCC would have aligned it by 16 and probably for one reason or another left a gap between the buffer and the top of the stack frame.
Segmentation fault when i try to run the extracted shellcode even if there is no null
Okay, this looks like it's on Linux. You have to make your stack executable in order to make it work. You basically have to call mprotect() on your region of memory and set its permissions to PROT_EXEC and PROT_READ.
http://linux.about.com/library/cmd/blcmdl2_mprotect.htm
Why is this shellcode causing a segmentation fault?
Writeable data in static storage (such as your code
array) usually ends up in the .data
section which is usually marked “not executable.” Mark the array as const
so it ends up in .rodata
resp. .text
and can be executed.
Related Topics
Ldd Says "Not Found" Even Though Library Is in My Ld_Library_Path
Scripts Launched from Udev Do Not Have Display Access Anymore
Why Does Gcc Force Pic for X64 Shared Libs
Linux: How to Detect That Ftp File Upload Is Finished
Building a Simple (Hello-World-Esque) Example of Using Ld's Option -Rpath with $Origin
Merging Through Fuzzy Matching of Variables in R
How to Mmap the Stack for the Clone() System Call on Linux
Bash: Delete Based on File Date Stamp
How to Connect to Postgresql Server: Could Not Connect to Server: Permission Denied
Linux Kernel: How to Capture a Key Press and Replace It with Another Key
How to Return Spawned Process Exit Code in Expect Script
What Is the Use of File Descriptor 255 in Bash Process
Pseudo-Random Stack Pointer Under Linux