Every network has rules. Firewalls stand guard, access control lists dictate who talks to whom, and security teams spend considerable resources ensuring only sanctioned traffic flows through their environment. It’s a game of gates and locks — and for the most part, it works.

But a locked door is only as strong as the walls around it. When an attacker can’t knock on the front door, they look for the ventilation shaft. In networking, that ventilation shaft is often a protocol so trusted that most firewalls wave it through without a second glance — ICMP.

In this post, we’ll break down how ICMP tunneling works, how to detect and investigate it forensically, and then build a hands-on lab using LXC containers to see it all in action. Let’s get into it.

What Even Is ICMP?

Before we dive deep, let’s set the stage. The Internet Control Message Protocol (ICMP) is one of those foundational protocols that lives in the background of every network. You’ve used it every time you’ve typed ping into a terminal. Its primary job is diagnostic — it carries error messages and operational information about IP operations. When a router drops a packet, when a host is unreachable, or when you simply want to test latency, ICMP is the messenger.

Here’s the thing about ICMP — it’s not a transport protocol. Unlike TCP or UDP, it has no ports, no sessions, and no concept of a connection. Firewalls, historically, have been kinder to ICMP than to arbitrary TCP/UDP traffic, often permitting echo request (type 8) and echo reply (type 0) packets through with little to no inspection. Network administrators need ping to work. And that leniency? Attackers have been exploiting it for decades.


The Art of ICMP Tunneling

ICMP tunneling is a technique where arbitrary data — say, full TCP sessions including SSH traffic — is encapsulated inside the payload of ICMP echo request and reply packets. To the firewall, it looks like harmless ping traffic. Under the hood, you’re running a fully functional covert channel.

The mechanics are surprisingly elegant:

  1. Client-side tool takes your TCP data (e.g., an SSH session) and chunks it into payloads.
  2. Each chunk is wrapped inside an ICMP echo request packet and sent to a relay server outside the restricted network.
  3. The relay server unwraps the ICMP payload, extracts the TCP data, forwards it to the actual destination (say, an SSH server), and sends the response back — again wrapped in an ICMP echo reply.
  4. The client reconstructs the TCP stream from the ICMP replies.

Tools like icmptunnel, ptunnel-ng, and hans make this almost trivially easy to set up. With ptunnel-ng, for example, you can be SSH-ing over ICMP in under five minutes.

What makes this particularly dangerous from a defensive standpoint is intent disguise. The packets look legitimate at a glance — they’re ICMP, they have valid checksums, and they follow the expected request/reply pattern. Many next-generation firewalls (NGFWs) that aren’t performing deep packet inspection (DPI) will let them sail right through.


Why This Works

Most corporate and restrictive network environments are configured to:

  • Block inbound/outbound TCP on non-standard ports
  • Block UDP unless explicitly required (DNS, VoIP, etc.)
  • Allow ICMP — because blocking ping entirely breaks too many network diagnostic tools and monitoring systems

This creates a deliberate gap. Nation-state actors, red teamers, and malicious insiders have all used ICMP tunneling in documented engagements. It’s not theoretical — it’s real-world TTPs catalogued under MITRE ATT&CK T1095 (Non-Application Layer Protocol).

The Digital Forensics Perspective

Detecting ICMP tunneling is absolutely possible — it just requires knowing what anomalies to look for.

1. Volume and Frequency Anomalies

Normal ICMP traffic is sporadic. A standard ping sends maybe 4–10 packets. Tunneling sustains a continuous, high-frequency stream. Alert on any host sending more than 50–100 ICMP packets per minute consistently.

2. Payload Size Inspection

A legitimate ICMP echo request carries 32 bytes on Windows or 56 bytes on Linux. Tunneling tools stuff MTU-sized payloads of 1400+ bytes. In Wireshark, filter: icmp and data.len > 64

3. Payload Entropy and Content Analysis

High-entropy payloads suggest encrypted tunneled data. Tools like binwalk, scalpel, or custom Python scripts using scipy can quantify this.

4. Timing Correlation

Tunneling tools generate precise, programmatic timing patterns. Statistical analysis of inter-packet arrival times reveals automation.

5. Endpoint Forensics

Look for running processes (ptunnel, hans, icmptunnel), unusual loopback listeners, shell history, and prefetch/event logs.

Defensive Countermeasures

  • Rate-limit ICMP at the perimeter (throttle to ~10 pps per host)
  • Enable DPI on NGFWs to inspect ICMP payload content
  • Deploy IDS signatures — Snort and Suricata both have ICMP tunneling rules
  • Block large ICMP payloads — anything over 128 bytes at the perimeter is suspicious
  • Monitor and baseline ICMP traffic continuously

Hands-On ICMP Tunneling with LXC

Ethical Notice: This lab is strictly for educational purposes in an isolated, controlled environment. All testing must be performed on infrastructure you own or have explicit written permission to test. As an ISC2 Certified Cybersecurity Personnel, responsible disclosure and ethical practice are non-negotiable.

Theory is powerful. But nothing beats watching packets fly across a network you built yourself. Let’s construct a fully isolated LXC-based lab that replicates a realistic scenario: a restricted internal client, a relay/proxy server, and a target SSH server — all talking over a simulated ICMP-only network boundary.

Lab Architecture

┌─────────────────────────────────────────────────────────┐
│                    LXC Host Machine                      │
│                                                          │
│  ┌──────────────┐    ICMP ONLY     ┌──────────────────┐ │
│  │   lxc-client │ ───────────────► │   lxc-relay      │ │
│  │ 10.10.10.10  │ ◄─────────────── │   10.10.10.20    │ │
│  │ (restricted) │                  │ (outside firewall│ │
│  └──────────────┘                  └────────┬─────────┘ │
│                                             │ TCP/SSH    │
│                                    ┌────────▼─────────┐  │
│                                    │   lxc-target     │  │
│                                    │   10.10.10.30    │  │
│                                    │  (SSH server)    │  │
│                                    └──────────────────┘  │
└─────────────────────────────────────────────────────────┘

Three containers, one bridge network, one simulated firewall rule — and a full covert channel to discover.

Prerequisites

On your host machine (Ubuntu 22.04 / Debian 12 recommended):

# Install LXC and tooling
sudo apt update && sudo apt install -y lxc lxc-utils lxc-templates \
  iptables net-tools tcpdump wireshark-common tshark

Step 1: Create the LXC Containers

# Create three Ubuntu 22.04 containers
sudo lxc-create -n lxc-client -t ubuntu -- -r jammy
sudo lxc-create -n lxc-relay  -t ubuntu -- -r jammy
sudo lxc-create -n lxc-target -t ubuntu -- -r jammy

Step 2: Configure Static IPs

Edit each container’s network config. LXC stores these under /var/lib/lxc/<name>/config.

# lxc-client config
sudo tee -a /var/lib/lxc/lxc-client/config <<EOF
lxc.net.0.type = veth
lxc.net.0.link = lxcbr0
lxc.net.0.flags = up
lxc.net.0.ipv4.address = 10.10.10.10/24
lxc.net.0.ipv4.gateway = 10.10.10.1
EOF

# lxc-relay config
sudo tee -a /var/lib/lxc/lxc-relay/config <<EOF
lxc.net.0.type = veth
lxc.net.0.link = lxcbr0
lxc.net.0.flags = up
lxc.net.0.ipv4.address = 10.10.10.20/24
lxc.net.0.ipv4.gateway = 10.10.10.1
EOF

# lxc-target config
sudo tee -a /var/lib/lxc/lxc-target/config <<EOF
lxc.net.0.type = veth
lxc.net.0.link = lxcbr0
lxc.net.0.flags = up
lxc.net.0.ipv4.address = 10.10.10.30/24
lxc.net.0.ipv4.gateway = 10.10.10.1
EOF

Step 3: Start All Containers

sudo lxc-start -n lxc-client
sudo lxc-start -n lxc-relay
sudo lxc-start -n lxc-target

# Verify they're running
sudo lxc-ls --fancy

Step 4: Install Software Inside Containers

# On lxc-relay and lxc-client — install ptunnel-ng
sudo lxc-attach -n lxc-relay  -- bash -c "apt update && apt install -y ptunnel-ng"
sudo lxc-attach -n lxc-client -- bash -c "apt update && apt install -y ptunnel-ng openssh-client"

# On lxc-target — install and enable SSH server
sudo lxc-attach -n lxc-target -- bash -c "apt update && apt install -y openssh-server && \
  useradd -m labuser && echo 'labuser:labpass' | chpasswd && \
  systemctl enable ssh && systemctl start ssh"

Step 5: Simulate the Firewall — Block TCP, Allow ICMP Only

This is the critical step. We use iptables on the host to simulate a restrictive firewall that only permits ICMP between lxc-client and lxc-relay.

# Allow ICMP between client and relay
sudo iptables -A FORWARD -p icmp -s 10.10.10.10 -d 10.10.10.20 -j ACCEPT
sudo iptables -A FORWARD -p icmp -s 10.10.10.20 -d 10.10.10.10 -j ACCEPT

# Block all other traffic from client to relay (TCP/UDP)
sudo iptables -A FORWARD -s 10.10.10.10 -d 10.10.10.20 -j DROP
sudo iptables -A FORWARD -s 10.10.10.20 -d 10.10.10.10 -j DROP

# Allow relay <-> target unrestricted (relay is "outside" the firewall)
sudo iptables -A FORWARD -s 10.10.10.20 -d 10.10.10.30 -j ACCEPT
sudo iptables -A FORWARD -s 10.10.10.30 -d 10.10.10.20 -j ACCEPT

# Enable IP forwarding on host
sudo sysctl -w net.ipv4.ip_forward=1

Verify the firewall is working — from lxc-client, a direct SSH to lxc-target should fail:

sudo lxc-attach -n lxc-client -- ssh labuser@10.10.10.30
# Expected: Connection timed out — firewall is doing its job

Step 6: Set Up the ICMP Tunnel

Now the fun part. Start the ptunnel-ng proxy on the relay server:

# On lxc-relay — start the tunnel proxy (run in background)
sudo lxc-attach -n lxc-relay -- ptunnel-ng -v 3

On lxc-client, establish the tunnel — forwarding local port 2222 through ICMP to the relay, which connects to the SSH server on port 22:

# On lxc-client — connect through ICMP tunnel
sudo lxc-attach -n lxc-client -- ptunnel-ng \
  -p 10.10.10.20 \
  -lp 2222 \
  -da 10.10.10.30 \
  -dp 22 \
  -v 3 &

Now SSH through the tunnel — over ICMP — to the target:

sudo lxc-attach -n lxc-client -- ssh labuser@localhost -p 2222
# Password: labpass
# You are now SSH'd into lxc-target — through ICMP!

You’ve just tunneled SSH over ping packets through a simulated firewall.


Step 7: Capture and Investigate the Traffic (Forensics in Action)

Open a third terminal and capture the ICMP traffic on the host bridge:

# Capture ICMP traffic on the LXC bridge
sudo tcpdump -i lxcbr0 -w /tmp/icmp_tunnel_capture.pcap icmp

Perform a few SSH commands inside the tunnel, then stop the capture and open it in tshark for analysis:

# Inspect packet sizes — spot the large payloads
tshark -r /tmp/icmp_tunnel_capture.pcap \
  -T fields \
  -e frame.number \
  -e ip.src \
  -e ip.dst \
  -e icmp.type \
  -e data.len \
  | sort -k5 -n | tail -20

# Count ICMP packets per second — spot the frequency
tshark -r /tmp/icmp_tunnel_capture.pcap \
  -q -z io,stat,1,"icmp"

You’ll immediately observe:

  • Payloads of 1000+ bytes vs. the 64-byte baseline
  • Packet rates far exceeding what any human-initiated ping would produce
  • A clear bidirectional cadence of requests and replies

Export to Wireshark for visual inspection:

wireshark /tmp/icmp_tunnel_capture.pcap &
# Filter: icmp and data.len > 100

The evidence is right there in the payload field — exactly what a forensic analyst would use to build a case.


Step 8: Tear Down and Clean Up

# Remove firewall rules
sudo iptables -F FORWARD

# Stop containers
sudo lxc-stop -n lxc-client
sudo lxc-stop -n lxc-relay
sudo lxc-stop -n lxc-target

# Optional: destroy containers after lab
sudo lxc-destroy -n lxc-client
sudo lxc-destroy -n lxc-relay
sudo lxc-destroy -n lxc-target

ICMP tunneling is a beautiful example of protocol abuse — taking something mundane and bending it to an entirely unintended purpose. Building this lab in LXC makes the concept viscerally real: you watch your SSH session travel through ICMP packets, you capture the evidence, and you see exactly what a forensic analyst sees when hunting for this technique in the wild.

As a fullstack developer turned certified security practitioner, what fascinates me most is how deeply understanding a protocol’s design reveals its exploitable edges. Firewalls that trust ICMP implicitly are making an architectural assumption that attackers are perfectly willing to dismantle — one echo request at a time.

Whether you’re a red teamer probing network defenses or a blue teamer building detection pipelines, ICMP tunneling deserves a prominent place in your mental model of covert channels. Ping is never just ping.

Leave a Reply

Your email address will not be published. Required fields are marked *