C - Explotación de desbordamiento de búfer simple, ¿cómo se sobrescribe el EIP en diferentes funciones de llamada de tipo?

2

Antecedentes generales: He escrito un servidor de eco que intenta implementar un ejemplo de BoF en C que utiliza una llamada a la función strcpy () como esta:

// .... including the corresponding libraries depending on host environment

#include <stdio.h>                                              // crossed platform headers
#include <string.h>                                             // string header, for strcpy
#include <stdlib.h>                                             // stardard lib, for exit()


void echo(void);                                 // infinite loop echo serving client
char* vuln_func(char* recieved);                                // BoF vulnerable function that uses strcpy from string.h


int main(void)
{
  ... setting up echo server
  echo();

  return 0;
}

void echo(void)
{
  char *send_str;                                                 // declaring the pointer to the string recieved from vulnFunc
  char recv_str[1024];                                            // variable declaration to store input string from client

  // infinite loop to echo serve clients
  while(1)

  {
    ... recieving string from client and storing it starting at location recv_str
    // calling vuln_func and inputting the received string from client

    send_str = vuln_func(recv_str);                       

    ...echoing the send_str back to the client

  }


}

char* vuln_func( char* recieved)
{
  char* sending_str = (char*)calloc(100, sizeof(char));                               

   char buf[100];                                                                 

  int i;                                                                          // declaring counter for loop
  strcpy(buf, recieved);
  for (i = 0; i < strlen(buf); i++)                                              // for loop to populate return string with the send
  {
    sending_str[i] = buf[i];
  }

  return sending_str;                                                             
}

Problema: Entonces, una vez que alimentemos al servidor con una cadena de entrada lo suficientemente grande, el servidor generará una infracción de acceso con un desplazamiento 41414141 (que es lo que queríamos); sin embargo, una vez que desmontamos el archivo ejecutable en un desensamblador / depurador (utilicé el Depurador de inmunidad) en el momento de la violación, ni el ESP ni el EIP se sobrescribieron con la cadena As (por ejemplo). Mientras que el registro de EBP se sobrescribió con la cadena grande desbordada de As.

Solución: Así que hice algunas búsquedas en Google y vi códigos de ejemplo similares del mismo tipo (Echo Server con un BoF vuln) y probé cosas diferentes con vuln_func como tal:

void vuln_func( char* recieved)
{

   char buf[100];                                                                 

  strcpy(buf, recieved);

}

O:

int vuln_func( char* recieved)
{

  char buf[100];                                                                 

  strcpy(buf, recieved);

  return 1;

}

y la función de eco se verá así:

void echo(void)
{

  char recv_str[1024];                                            // variable declaration to store input string from client

  // infinite loop to echo serve clients
  while(1)

  {
    ... recieving string from client and storing it starting at location recv_str
    // calling vuln_func with string received from client

    vuln_func(recv_str);                       

    ...echoing the recv_str back to the client

  }


}

así que la función echo repite el recv_str en lugar del send_str el cual imho derrota todo el propósito de la llamada vuln_func, pero con el propósito de demostrar esta vulnerabilidad de BoF, la dejaremos ahí. Después de verificar con Immunity Debugger, los registros ESP y EIP se sobrescriben con éxito con la cadena ("A * 128"), ¡el problema se solucionó! Genial

Pregunta: ¿Por qué el problema se comportó de esa manera?

  1. ¿Por qué, al devolver un puntero (dirección) a una cadena a la La función de llamada evita que se sobrescriba el EIP mientras se devuelve. void, o int hará que el registro EIP se sobrescriba como esperado
  2. ¿Cómo almacena el valor el registro EIP en los dos scanrios? mencionado anteriormente (con un vuln_func que devuelve char * y otro vuln_func
    devolviendo el vacío)?
  3. ¿El problema que se comporta de esta manera tiene algo que ver con el alineación de la pila, sé que (habiendo sobrescrito el EIP) sería un problema si se llamó a strcpy en main (), estudiando desde otra SE mensajes / respuestas

Como puede ver, el problema se resolvió pero no entiendo por qué funcionó. Por favor explique, o señale la dirección de escritura. Muchas felicitaciones y gracias de antemano.

    
pregunta Rennitbaby 21.03.2018 - 07:09
fuente

2 respuestas

3

Mi conjetura es que sobrescribiste otras variables locales (i o send_str) y, por lo tanto, causó un bloqueo prematuro.

Tendrá que mirar el código generado para ver qué está pasando. Dependiendo de la configuración, el compilador puede hacer cosas interesantes para su código, incluida la alineación total de la función llamada, por lo que la sobrescritura de la dirección de devolución tendrá efecto mucho más tarde, etc.     

respondido por el manduca 21.03.2018 - 20:12
fuente
2

Votó @manduca por la mejor respuesta porque la respuesta proporcionó la dirección de la investigación y más pruebas. Y después de varias pruebas, pude concluir la respuesta completa a mi pregunta:

  1. el tipo del valor de retorno de la función no tuvo nada que ver con el problema, era simplemente la suposición errónea
  2. para cada uno de los dos escenarios de la pregunta, el EIP almacenará La dirección de memoria de la siguiente instrucción. La única vez que lo hará. tomar una dirección (la dirección de retorno) que ha sido empujada a la Apilar desde el principio de la creación del marco de pila al final de la función llamada / declaración de retorno. Por lo tanto la excepción será ocurrirá tan pronto como sobrescribamos nuestra buf, y si la siguiente línea de la instrucción NO es el final de la función (devolver el vacío) o un declaración de retorno, el EIP no se actualizará desde la pila, ya que solo almacenará en la ubicación de memoria de la siguiente instrucción (Lo que estoy adivinando incrementando desde la ubicación de memoria de la instrucción previa). Y es por eso que el EIP no fue sobrescrito por nuestra cadena de 10MB de "A" s. Mientras tanto la solución tenía la fuerza. llamada de función justo antes de la declaración de retorno o el final de La llamada a la función vuln_func, por lo que el EIP se actualiza mientras la pila está explotando con el desbordamiento de búfer, lo que explica por qué el La solución había sobrescrito el EIP con nuestras "A" s
  3. No, no creo que este problema tenga nada que ver con la pila alineación, que es solo agregar algunos rellenos al tamaño de pila (por defecto 16bytes en sistemas de 64 bits), que también deben tenerse en cuenta si estaban tratando de hacer la manipulación de cadenas en el desbordamiento y deliberadamente Tratando de cambiar el EIP, honestamente siento que solo este problema existe en main (), por lo que evitar la explotación de main () podría ser una solución.
respondido por el Rennitbaby 22.03.2018 - 06:32
fuente

Lea otras preguntas en las etiquetas