The Internet Control Message Protocol (ICMP) is one of the foundational internet protocols that define how systems talk to each other. Commands such as ping and traceroute are supported by ICMP. Based on request for comments (RFC) 792, ICMP has been around since the early days of the internet, circa 1981.
So why should you care about analyzing one of the oldest internet-networking protocols? Hasn’t everything that can be done to this protocol already been done?
The recent EternalBlue vector used by the WannaCry malware exposed vulnerabilities in another ancient protocol, Server Message Block (SMB). This proves that no matter how old a protocol is, you must still consider it in your security operations program. In fact, over the years, numerous malware applications have exploited ICMP. Examples include:
- Malicious application of the protocol for DDoS (i.e. The Smurf Attack or ICMP Source Quench Request abuse)
- Use of ICMP for tunneling, enabling covert communications, data exfiltration, and reverse shells
- Use of ICMP for reconnaissance, including gathering network topology, detecting ACLs, trace routes, OS fingerprinting, and port scanning
I recently participated in a Tips and Tricks webinar and spoke about ICMP in regards to Network Threat Detection. Here is the detailed blog on that topic to show you how to analyze ICMP traffic with Network Monitor(NetMon).
An ICMP packet follows a very simple and well-defined format. Let’s look at and understand what can learn from each component.
Figure 1: The Structure of an ICMP Packet
Because IMCP is built on the core IP protocol, every packet has 20 bytes of standard IP header. There’s very little value in the information from the header because many security tools already pull out the source IP, destination IP, and TTL information.
The Type tells what ICMP command was sent. Out of approximately 30 valid type codes, around four can be the most useful within NetMon’s Framework. Specifically, Destination Unreachable, Redirections, Time Exceeded, and Parameter Problems.
The Checksum is usually not valuable as it merely verifies the Type and Code is valid.
Varies by Protocol
The next four bytes were originally unused in this piece, but they have been repurposed several times over the years. Now, the four bytes may contain padding data. They may also be an identifier and a sequence number of the packet used to help manage ICMP over NAT.
The data block is of variable size and content depending on the type and code. It’s also the most interesting part (after Type and Code) because it was designed to be extensible. This means anyone using ICMP for any purpose has to support a variable size/purpose data block. Ultimately, supporting a variable size/purpose data block opens up pathways for all sorts of malicious network shenanigans.
ICMP in NetMon
NetMon natively classifies ICMP. Without any modifications, you can use NetMon to look at ICMP traffic going to or from a specific system or crossing inside/outside the network using the Ingress Egress Traffic Dashboard.
Click images to expand
Figure 2: LogRhythm NetMon Ingress Egress Traffic Dashboard
Note: The above screenshot is from a test network with simulated traffic. Best practice dictates to limit or block egress ICMP traffic except for controlled systems. Nothing should be receiving unmonitored ICMP results from your internal systems!
For next steps, look to deep packet analytics (DPA) rules to parse out this information and extract packet data for deeper analysis.
Working with a DPA Rules and ICMP
As referenced in the recent blog titled Using Deep Packet Analytics to Extract Specific Bytes, you can use DPA rules to extract specific bytes out of each packet. In the case of ICMP, the information from those bytes allow you to do a number of things including:
- Identify the Type
- Based on the Type, possibly extract the Code
- Possibly extract, or at least examine, the body to find signatures
- Possibly raise alarms based on suspicious traffic
The complete source code for the rules described in this article is available in the LogRhythm Community under the NetMon DPA Rules section.
ICMP as a Connectionless Protocol
One caveat to work through in this use case is that ICMP is a connectionless protocol. ICMP does not have the concept of a session, even though NetMon does. This means that each ICMP session inside NetMon is actually a series of independent ICMP requests. ICMP sessions and NetMon are related by:
- The source IP/MAC Address
- The destination IP/MAC address
- The time between packets
ICMP packets going from the same source to the same destination (or vice versa) are considered part of the same ICMP session until there is a long enough time gap to trigger a session closure.
As you work through the packet-level DPA rules, you have to be aware of the possibility of multiple ICMP commands in the same session. However, due to this disconnect, there is no guarantee that you will always see the complete conversation. If the ping request and the echo reply are separated by many seconds, they may end up in different sessions.
Building the NetMon DPA Rule
As mentioned in the previous blog, Using Deep Packet Analytics to Extract Specific Bytes, writing a packet rule is all about performing as few tasks as possible and exiting as fast as possible.
For this use case, the logic for our rule will look like the following.
- Exit if the packet isn’t classified.
- Exit if the packet isn’t ICMP.
- Exit if you’ve already parsed the first type/code in the session (ignore all the subsequent traffic in the session).
- Extract the Type byte and parse it back into English.
- If the Type is 3 (Destination Unreachable), 5 (Redirect), or 11 (Time Exceeded), extract the Code field for additional data.
- Write the Type, Type message, Code, and Code message out as metadata.
Starting the Rule
Remember that this is a packet rule. The basic rule shell should look like this:
Figure 3: NetMon DPA Rule Shell
As you set up the rule, select Packet as the scope. Also, ensure the function header accepts the dpiMsg and the packet.
Performance 101: Exit Quick
Exiting the rule fast is fairly straightforward. First, determine if the flow is ICMP by checking if an ICMP_Type has been generated. You can improve rule performance even more by looking for the ICMP application type (an integer) rather than the application name (a string), as string comparisons are generally slower.
Figure 4: NetMon DPA Rul Quick Exit
The second check for the custom field uses some interesting LUA syntax . In a DPA rule, custom fields are returned as a table. You have to get the table using GetCustomField(). Once you have the table, you can then check to see if there are any entries. If the next entry (the very first one) has a populated value, then the custom field has already been created.
Extracting the Type
Extracting the Type requires pulling a specific byte out of the ICMP packet. Ignoring any wrapper traffic, you want the first byte. However, things get a little difficult because the packet will at least have 20 bytes of IP header. It will also have Ethernet frame wrapping. It may also have VLAN wrapping.
Figure 5: ICMP Ping Request with a VLAN Wrapper
In this sample, you want byte 39, but you also have four bytes of VLAN wrapper to start the packet. This means you actually want byte number 35 in a normal packet (39 minus the four bytes of VLAN wrapper).
Fortunately, in the previous blog, I’ve described a standard way to determine the offset for a VLAN wrapper. Here is that code:
Figure 6: Code to Determine the Offset for a VLAN Wrapper
As you work to write the rule, remember that the function for GetPacketBytes uses a one-based counting system rather than the standard octet display from Wireshark. Meaning, the first byte in the packet is #1 (not #0 as would show in Wireshark). For example, the above rule is asking for byte #35 in the packet, which would be byte #34 in Wireshark.
Converting the Extracted Byte Type into English
Representing the byte as an integer is only helpful for those who have memorized the various ICMP Types. To make this more usable, you should translate the integer into English. To do this, create a global table of Type values matched to the representative text.
Best practice is to create this table as a global value and wrap the creation in a check to determine if it already exists. This way, this creation only runs the first time the rule runs.
Figure 7: Table of Type Values Matched to Representative Text
Once you’ve built your table to convert integers to plain English, you can use your Type value to extract the string and write the values out as metadata fields. Make sure to wrap your check call to reveal if you parsed out an invalid Type. If you see the resulting message, it may indicate strange hardware, malicious traffic or possibly garbled network traffic.
Figure 8: Code to Save ICMP Information as Metadata Fields
Extracting the Code
If the Type is 3 (Destination Unreachable), 5 (Redirect), or 11 (Time Exceeded), you want to follow the same basic pattern and parse out the next byte (the Code). All three types use the same pattern of steps, but use different look up tables. Type 3, Code 0 is “Net Unreachable,” but Type 11, Code 0 is “TTL Exceeded.”
I’ll quickly walk through how to build out the code that will extract the Code from Type 3. You can use this same format for the other two ICMP Types that use meaningful Code values. To start, create a data table.
Figure 9: Data Table
Next, take the Code byte and have the script perform a lookup.
Figure 10: Script to Lookup Code Byte
There you have it: Now you have a DPA rule to analyze ICMP traffic. All that is left is to tune the rule and add your personal coding style. As mentioned earlier, the complete rule can be found on the LogRhythm Community under the DPA Rules section. Here you can download the complete code, add a comment on the rule, or make suggestions for changes there.
Running the DPA Rule
Once you’ve created the rule in NetMon, save it and verify that you don’t have any syntax errors. To reveal the IMPC information, open any dashboard and filter for ICMP traffic. Next, open up the table on any session, and you’ll see ICMP_TypeMsg_NM and ICMP_Type_NM values.
Figure 11: Resulting ICMP_TypeMsg_NM and ICMP_Type_NM Fields
If you happen to grab a 3, 5, or 11, you’ll also see the ICMP_Code_NM and ICMP_CodeMsg_NM fields.
Figure 12: Resulting ICMP_Code_NM and ICMP_CodeMsg_NM Fields
Now that you have the rule set up and you are parsing the data, you can use the resulting information to do several things, including:
- Identify which of your systems are getting hit with which kind of ICMP traffic
- Create a dashboard to organize traffic by source, destination, ICMP type, and count
- Capture ICMP so you can look at the packet details
- Write Flow Rules to raise alarms when NetMon sees suspicious ICMP on specific secure systems
If this post inspires you to develop your own network security use case, check out our free network monitoring and forensic tool NetMon Freemium. Then submit your use case into our Rule Your Network Challenge for a chance to win over $18,000 USD in cash and prizes!