Intelligence in Offensive Security: How to Level Up
Echelon Cyber ICMP

Packet Paranoia – Manipulating ICMP Packets to Covertly Exfil and Infil Data

Many, many, many moons ago, when I was still working for the “Big Fed,” I attended a training and somehow found myself in an argument with a penetration tester about the realism of manipulating established protocols to be used in a manner they weren’t intended to be used for and for that manipulation to still be stealthy and functional.

To their credit, at the time, protocol manipulation wasn’t much of an attack vector unless you were a nation-state APT, but I proved my point by showing them how simple and easy it was to infil and exfil data using Internet Control Message Protocol (ICMP).

Here’s are some of the techniques involved, and how easy it is for those packets under a packet sniffer to look - at face value - no different than a generic ping.

Manipulating ICMP to Covertly Exfil and Infil Data: The Resurgence of ICMP Tactics

ICMP packet manipulation is not an unknown technique for every type of threat actor, but somehow, it is still a very little-known technique for cybersecurity professionals, and we rarely see this TTP tested in both penetration testing or red teaming engagements. If I had to guess, this is because most organizations have just chosen to outright block ICMP traffic.

Recently I was reminded of this TTP and with my team seeing a resurgence of the ability to ping out of a network from Network and System administrator hosts, I thought it would be cool to revisit the concept. So, I created a proof-of-concept tool called Ping-based Information Lookup and Outbound Transfer or just PILOT for short.

I had seen other implementations of this over the years, but for my implementation, the idea around PILOT was simple and had only two requirements.

  • First, PILOT needed to be able to be run by any user on a Windows host, meaning it shouldn’t require any additional software and shouldn’t require administrative privileges to run. This means we needed a living-off-the-land binary with scripting ability, which my go-to for anything scripting within Windows is PowerShell.
  • Second, the packets needed to, as closely as they could, mimic how Windows ping packets look, which could be accomplished by looking at the packets in Wireshark.

Understanding ICMP's Structure: A Foundation for Manipulation

So, before I break down the code itself, let's take a step back and understand why data exfil and infil using ICMP is even possible in the first place, and the best place to start is by looking at this diagram of how IMCP is structured.

When most people think about ICMP, they usually think only of layers 1-3 of the OSI model. This is because most tech-savvy individuals out there are using the command “ping” to determine if they have an outbound connection (usually ping “8.8.8.8”).

However, to no surprise for network architects, ICMP is actually a layer 1-4 protocol. This is because ICMP actually goes far beyond general connectivity testing and can be used for things like determining a network path’s MTU (maximum transmission unit) size or even transferring NTP information. It’s able to do this through its header information.

  • To break down the ICMP header section, we have the following sections;
    Type (8 bytes): Indicates the type of the ICMP message; for example, the two most common are Echo Request (Type 8) and Echo Reply (Type 0) as these are the types used in ping, but to most people’s surprise, including my own back in the day, there are actually 256 types, but really only 40 are used today.
  • Code (8 bytes): Provides further information about the message type. These codes are similar to HTTP codes in that they state things like unreachable, redirected, or failure events.
  • Checksum (16 bytes): Used for error-checking the header and data, ensuring the integrity of the message. Pretty obvious use.
  • Data Payload(0-65507 bytes): This portion of the packet is where data is included and can vary in size. In the context of Echo Request/Reply messages, arbitrary data is typically placed for the echo operation. This is really the section that we care about most when manipulating the packet.

Unveiling Covert Exfil Techniques through ICMP Manipulation

So now that we have an advanced knowledge of the ICMP packet, we can begin to manipulate it to work to our advantage as adversaries. Because we only care about the data section, we should focus our efforts there on understanding what default looks like. For Windows 10 hosts, Microsoft puts the alphabet in the packet, so if we add the alphabet, which is 32 bytes, and the default header information, which is 32 bytes, we are left with a packet that is 64 bytes, which can be seen here in Wireshark.

Now that we have the background let's talk about manipulating it. Because ICMP is a well-defined structure, it means that there are plenty of functions organic to programming/scripting languages to manipulate these layer 4 items like the data section. This is exactly what I did with PILOT, and it was all done in less than 100 lines of code in PowerShell.

PowerShell is super powerful (hence the name). It's basically scriptable .Net. So, we can pull the same functions, methods, and assemblies that we could get from the .Net API and manage them in our script. To manipulate packets, you really only need the assemblies “System.Net” and “System.Net.NetworkingInformation.” Then, to call them to manipulate the ICMP packet, you just need to create an object and assign it to a variable.

"$icmpPacket = New-Object System.Net.NetworkInformation.Ping”

Once we have our object assigned, it’s as simple as calling that variable and using the “Ping” assembly’s “Send” method. This can be found here in the Windows .Net API documentation.

If you don’t want to spend time reading the short documentation, I’ll quickly break down the options:

  • Hostname - Pretty self-explanatory; can be an IP address as well
  • Timeout – Also pretty self-explanatory.
  • Buffer – This is looking for a byte array to add to the “Data” section of the packet.
  • Options – These are different Ping options that you wish to control; we don’t really care for this in our implementation.

So what that looks like in PILOT is:

$response = $icmpPacket.Send($targetIP, 1000, $chunks[$i])

In this case, $chunks[$i] is a 32-byte piece of data extracted from our file through a function I wrote called “Read-FileInChunks.” Its name should make clear what it's supposed to do, but to quickly break it down, it takes a file as its input and separates that file into 32-byte chunks. It then throws that chunk into an array called $chunks. So to send, we just use a “for loop” to iterate from 0 to “n” of the array, and once complete, we exit the loop.

Also, the reason we tie this command to the variable $response is we want to ensure that each ping request receives a ping response, so we are going to check to see if the return value of the “Success” method is set to True. Doing this adds a bit of covertness as it ensures that the data transfer looks as realistic as possible and doesn’t completely stand out on a packet log.

And honestly, it's as simple as that. Aside from breaking down a file into small 32-byte chunks, PILOT, at its core, uses one simple object that is readily available to any and every user to call.

For the distant end for capturing the data and recompiling it, I honestly just provided ChatGPT my code and told it to write a Python script that doesn’t use non-default libraries like Scapy to capture the data and recompile it back into the original format and that’s how ATC.py was built. The implementation was easy enough that it had no problem with building it and worked without edits, honestly.

The reason I think GPT4 had no problem writing this is that the idea on the Python side was simple. It was to open a socket, only listen for ICMP packets, and once it saw an ICMP packet, it should begin reading the data section of the packet.

The reason I want to call this out is I don’t believe we should all be worried about LLMs really coming in and writing malware. That shouldn’t be the focus here, and honestly, as a malware dev, LLM malware is horrendous to work with and rarely works right off the bat - you still need to have good programming knowledge.

Manipulating ICMP: Exploring Infil Techniques

Anyways, back to the ICMP stuff. So, I’ve talked a lot about how to extract data, but what about the infill side of the house? What does that look like? Well, I’ll make this quite quick, but, once again, it's quite simple, thanks to the power of the .Net API.

See, there is a class called “PingReply,” and that class information can be found here. but within that class, there are properties like “address,” “options,” and what we really care about “buffer.” The “buffer” property is where the data is received in a reply message. So, to pull that information down using PowerShell, all we need to do is parse the PingReply object using “::Parse”. The parse method allows us to take the raw bytes of the ICMP packet and convert them into an object we can understand. In implementation, we need to parse that info into a variable, which we can do like so:

$icmpPacket = [System.Net.NetworkInformation.PingReply]::Parse($buffer, 0, $receivedBytes)

In the code line above, we are taking a $buffer stream, which is the raw ICMP packet, and parsing the information from 0 to the size of the buffer, which is set in a variable called $recievedBytes.

We then just need to pull the data section out, which we can do by calling “$icmpPacket.Buffer” and boom, Bob’s your uncle.

The Significance of ICMP in Modern Networks

So, after all this explanation, you may be left wondering, ok, why do we care? If ICMP is being blocked by most organizations, what’s the big deal?

You would be partly right. This is more relevant today because it appears that there is a resurgence of ICMP making its way back into the “allowed” list of firewalls, more so on administrator hosts than any other. If you are like me, this means you get to make one of those scheming faces like Mr. Burns going, “Excellent.”

Of all the places you don’t want ICMP allowed outbound, it's on the Admin’s host - but also, the one place it's really relevant is the Admin’s host, so what's an Admin supposed to do?

There are two approaches I believe we can take. The first is to return to the old way of solving the issue; if it isn’t broken, don’t try fixing it. Restricting ICMP is a quick-win item that will resolve the issue outright without a heavy lift.

The other option is to do the heavy lifting and start to create ICMP baselines for what would be expected in the data section of the packet. ICMP isn’t something that is encrypted, so it shouldn’t be problematic or resource-intensive to review the packet before forwarding it. If you create these baselines, make sure you understand what the ICMP packets will look like for every single type of OS you have within your firewall.

What we can’t have is a return to the days when we just don’t pay attention to ICMP as a data exfil/infill protocol. Threat actors are only getting better, but that doesn’t mean we need to ignore old good security habits because the bad guys have gone to more advanced and sneaky methods. They will gladly return to the easy stuff if it means it will be a quick win for them too.

Anyways, happy hacking. I hope you learned something.

Sign up to get Cyber Intelligence Weekly in your inbox.