El libro "Hacking: The Art of Exploit. 2ª ed.

imagen Todo programador es esencialmente un hacker. Después de todo, la piratería inicial se llamó la búsqueda de una solución hábil y no obvia. Comprender la programación lo ayuda a encontrar vulnerabilidades, y las habilidades de detección de vulnerabilidades lo ayudan a crear programas, por lo que muchos hackers hacen ambas cosas al mismo tiempo. Existen movimientos interesantes no estándar tanto en técnicas para escribir programas elegantes como en técnicas para encontrar puntos débiles.

Por donde empezar Para sobrescribir la memoria utilizando desbordamientos de búfer, acceder a un servidor remoto e interceptar conexiones, debe programar en C y ensamblador, usar código de shell y registros de procesador, familiarizarse con las interacciones de red y el cifrado, y mucho más.

No importa cuánto queramos creer en los milagros, el software y las redes informáticas de las que depende nuestra vida diaria tienen vulnerabilidades.

Un mundo sin hackers es un mundo sin curiosidad y soluciones innovadoras. (John Erickson)

Contramedidas


Hay una rana así: un terrible listolaz (Phyllobates terribilis). Las glándulas de su piel contienen el veneno más fuerte. Solo tócalo para obtener una intoxicación mortal. Un remedio tan poderoso se explica por el hecho de que estas ranas se alimentan de ciertos tipos de serpientes que han desarrollado inmunidad al veneno. Poco a poco, el veneno de las ranas se hizo más y más fuerte. Como resultado de tal evolución conjunta, a los trepadores tremendos de la hoja no les quedan otros enemigos naturales. Algo similar está sucediendo con los hackers. Las técnicas que inventaron se conocen desde hace mucho tiempo, por lo que la aparición de contramedidas es bastante natural. En respuesta, los piratas informáticos están buscando formas de eludir y destruir los mecanismos de defensa, lo que lleva a la creación de nuevas técnicas defensivas.

Este ciclo de búsqueda de medidas y contramedidas es muy útil. Los virus y los gusanos se convierten en la causa de numerosos problemas y traen grandes pérdidas a las empresas, pero al mismo tiempo obligan a los desarrolladores a tomar medidas de represalia para resolver los problemas que han surgido. Los gusanos se auto-replican usando vulnerabilidades de software de baja calidad. Los errores a menudo pasan desapercibidos a lo largo de los años, y los gusanos relativamente inofensivos como CodeRed o Sasser obligan a los desarrolladores a corregirlos. Este proceso se puede comparar con la varicela, que es mejor para enfermarse en la infancia y no en la edad adulta, cuando puede tener consecuencias desastrosas. Si los gusanos de Internet no hubieran atraído la atención universal a los agujeros de seguridad, estos agujeros permanecerían abiertos al ataque con propósitos mucho más maliciosos que la simple auto-reproducción. De esta manera, los gusanos y los virus contribuyen a la seguridad a largo plazo. Pero hay métodos más activos: técnicas que intentan anular los resultados de un ataque o hacerlo completamente imposible. El concepto de "contramedidas" es bastante vago, estas palabras pueden significar medios técnicos de seguridad, un conjunto de reglas, un programa o simplemente un atento administrador del sistema. Las contramedidas se dividen condicionalmente en dos grupos: tratar de detectar un ataque y tratar de proteger la vulnerabilidad.

Herramienta de detección de ataques 0x610


Las contramedidas del primer grupo se reducen a intentos de notar la intrusión a tiempo y de alguna manera reaccionar ante ella. El proceso de reconocimiento se puede implementar de cualquier manera, desde el administrador que lee los registros del sistema hasta los programas que analizan el tráfico de la red. A veces, la reacción a una invasión se reduce a desconectar automáticamente la conexión o cerrar el proceso, y a veces el administrador tiene que estudiar cuidadosamente todo el contenido de la consola.

Los métodos conocidos para explotar vulnerabilidades no son tan peligrosos para el administrador del sistema como los que aún no conoce. Cuanto antes se pueda detectar una intrusión, antes comenzará el trabajo con ella y es más probable que se localice. La intrusión que ha sido ignorada durante meses es un motivo de grave preocupación.

Para reconocer la intrusión, debe anticipar lo que hará el atacante. Esto proporciona información sobre lo que se necesita monitorear exactamente. Los detectores buscan patrones de ataque familiares en registros, paquetes de red e incluso en la memoria del programa. Cuando se detecta una intrusión, puede privar al pirata informático del acceso a la máquina, restaurar el sistema de archivos dañado mediante una copia de seguridad e identificar y corregir el agujero de seguridad. Con las capacidades de respaldo y recuperación disponibles en la actualidad, las contramedidas que se reducen a la detección de ataques son bastante efectivas.

Para un atacante, la detección significa contrarrestar todo lo que hace. Por supuesto, no siempre es posible notar de inmediato un ataque, por lo tanto, hay una serie de escenarios de "agarrar y correr" en los que el hecho de la detección no es importante, pero incluso en estos casos es mejor no dejar rastros. El secreto es una de las cualidades más valiosas de los hackers. Obtener acceso al shell de comandos con derechos de administrador mediante la explotación de vulnerabilidades hace posible hacer cualquier cosa en el sistema, y ​​si logra evitar la detección, nadie sabrá sobre su presencia. Es la combinación de permisividad con invisibilidad lo que hace que los hackers sean peligrosos. Pueden interceptar fácilmente contraseñas y datos en la red, agregar marcadores a los programas para su posterior acceso no autorizado y atacar a otros nodos de la red. Para permanecer en la sombra, el pirata informático debe comprender qué métodos de reconocimiento se utilizan en un caso particular. Si sabe exactamente qué están buscando, puede evitar ciertos patrones de explotación de la vulnerabilidad o disfrazar sus acciones como válidas. El factor que impulsa el ciclo de evolución conjunta de las herramientas y técnicas de detección que permiten pasar desapercibidas son ideas que aún no se han dado al otro lado.

0x620 demonios del sistema


Sin embargo, las contramedidas a los piratas informáticos y sus soluciones se discuten mejor utilizando un ejemplo práctico. Ahora consideraremos un ataque a un programa de servidor que acepte conexiones entrantes. En sistemas operativos tipo UNIX, los demonios del sistema son adecuados para estos criterios. Un daemon es un programa que se ejecuta en segundo plano y, en cierta forma, separado del terminal de control. El término fue acuñado en la década de 1960 por piratas informáticos del Instituto de Tecnología de Massachusetts. El prototipo era una criatura mítica que clasificaba moléculas de un experimento mental realizado por el físico James Maxwell. El demonio de Maxwell poseía una habilidad sobrenatural para realizar fácilmente tareas complejas, violando la segunda ley de la termodinámica. Del mismo modo, los demonios del sistema Linux realizan incansablemente tareas como otorgar acceso a SSH y mantener registros del sistema. Los nombres de demonio generalmente terminan con d, que enfatiza su naturaleza: por ejemplo, sshd o syslogd.

Un poco de edición convertirá tinyweb.c de la sección 0x427 en una semejanza realista con un demonio del sistema. La nueva versión del código contiene la función daemon (), que genera un nuevo proceso en segundo plano. Es utilizado por muchos procesos de daemon del sistema Linux. Aquí está la página dedicada a ella desde el directorio:

DAEMON (3) Referencia del programador de Linux DAEMON (3)

Nombre
daemon - ejecuta en segundo plano
Sintaxis

#include <unistd.h> int daemon(int nochdir, int noclose); 

DESCRIPCION

La función daemon () desconecta el programa del terminal de control y comienza
ella en el fondo como un demonio del sistema.
Con un argumento distinto de cero, nochdir, la función daemon () cambia el directorio de trabajo actual.
en la raíz ("/").
Con nullose, la función daemon () redirige los hilos
entrada estándar, salida estándar y errores en / dev / null.
VALOR DEVUELTO

(Esta función genera una copia del proceso, y si fork () tiene éxito
el padre ejecuta _exit (0) para que otros errores solo sean visibles
proceso hijo). Si tiene éxito, devuelve cero. En caso de error, la función
daemon () devuelve -1 y asigna el número de error a la variable global errno
de la biblioteca de funciones fork (2) y setsid (2).

Los demonios del sistema no tienen una terminal de control, por lo que el código del nuevo demonio tinywebd se enviará al libro de registro. Los demonios generalmente están controlados por señales. La nueva versión de tinyweb debería poder recibir una señal de finalización para salir correctamente.

Descripción general de la señal 0x621


Las señales proporcionan comunicación entre procesos en UNIX. Después de que un proceso recibe una señal, el sistema operativo interrumpe su ejecución para invocar el controlador de señal. Cada señal tiene su propio número y su propio controlador. Por ejemplo, cuando presiona la combinación de teclas Ctrl + C, se envía una señal de interrupción, cuyo controlador finaliza el programa que está abierto en el terminal de control, incluso si ha entrado en un bucle infinito.

Puede crear sus propios manejadores de señales y registrarlos utilizando la función signal (). Veamos un código en el que varios manejadores están registrados para algunas señales, y en la parte principal hay un bucle infinito.

 signal_example.c #include <stdio.h> #include <stdlib.h> #include <signal.h> /*    signal.h * #define SIGHUP 1  * #define SIGINT 2  (Ctrl+C) * #define SIGQUIT 3  (Ctrl+\) * #define SIGILL 4   * #define SIGTRAP 5   * #define SIGABRT 6  * #define SIGBUS 7   * #define SIGFPE 8     * #define SIGKILL 9   * #define SIGUSR1 10   1 * #define SIGSEGV 11   * #define SIGUSR2 12   2 * #define SIGPIPE 13    ,    * #define SIGALRM 14   ,  alarm() * #define SIGTERM 15  (  kill) * #define SIGCHLD 17    * #define SIGCONT 18 ,    * #define SIGSTOP 19  (  ) * #define SIGTSTP 20   [] (Ctrl+Z) * #define SIGTTIN 21       * #define SIGTTOU 22       */ /*   */ void signal_handler(int signal) { printf("  %d\t", signal); if (signal == SIGTSTP) printf("SIGTSTP (Ctrl-Z)"); else if (signal == SIGQUIT) printf("SIGQUIT (Ctrl-\\)"); else if (signal == SIGUSR1) printf("SIGUSR1"); else if (signal == SIGUSR2) printf("SIGUSR2"); printf("\n"); } void sigint_handler(int x) { printf(" Ctrl-C (SIGINT)   \nExiting.\n"); exit(0); } int main() { /* Registering signal handlers */ signal(SIGQUIT, signal_handler); //  signal_handler() signal(SIGTSTP, signal_handler); //    signal(SIGUSR1, signal_handler); signal(SIGUSR2, signal_handler); signal(SIGINT, sigint_handler); //  sigint_handler()  SIGINT while(1) {} //   } 

Cuando se ejecuta un programa compilado, se registran los controladores de señal y luego el programa entra en un bucle sin fin. Pero, a pesar de esto, las señales entrantes interrumpirán su ejecución y recurrirán a controladores registrados. Los siguientes son ejemplos de la aplicación de señales que pueden activarse desde el terminal de control. Una vez que se completa la función signal_handler (), el control vuelve al bucle interrumpido, mientras que la función sigint_handler () finaliza el programa.

 reader@hacking:~/booksrc $ gcc -o signal_example signal_example.c reader@hacking:~/booksrc $ ./signal_example   20 SIGTSTP (Ctrl-Z)   3 SIGQUIT (Ctrl-\)  Ctrl-C (SIGINT)    Exiting. reader@hacking:~/booksrc $ 

El comando kill le permite enviar un conjunto completo de señales a un proceso. Por defecto, envía una señal de finalización (SIGTERM). Agregarle la opción -l muestra una lista de todas las señales posibles. Veamos cómo el programa signal_example que se ejecuta en otro terminal envía las señales SIGUSR1 y SIGUSR2.

 reader@hacking:~/booksrc $ kill -l 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL 5) SIGTRAP      6) SIGABRT      7) SIGBUS       8) SIGFPE 9) SIGKILL     10) SIGUSR1     11) SIGSEGV     12) SIGUSR2 13) SIGPIPE     14) SIGALRM     15) SIGTERM     16) SIGSTKFLT 17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP 21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU 25) SIGXFSZ     26) SIGVTALRM   27) SIGPROF     28) SIGWINCH 29) SIGIO       30) SIGPWR      31) SIGSYS      34) SIGRTMIN 35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3  38) SIGRTMIN+4 39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8 43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7  58) SIGRTMAX-6 59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2 63) SIGRTMAX-1  64) SIGRTMAX reader@hacking:~/booksrc $ ps a | grep signal_example 24491 pts/3    R+     0:17 ./signal_example 24512 pts/1    S+     0:00 grep signal_example reader@hacking:~/booksrc $ kill -10 24491 reader@hacking:~/booksrc $ kill -12 24491 reader@hacking:~/booksrc $ kill -9 24491 reader@hacking:~/booksrc $ 

Al final, el comando kill -9 envía una señal SIGKILL. El controlador de señal no se puede cambiar, por lo que el comando kill -9 siempre se usa para matar procesos. El programa signal_example lanzado en otro terminal muestra que las señales fueron interceptadas y el proceso fue destruido.

 reader@hacking:~/booksrc $ ./signal_example   10       SIGUSR1   12       SIGUSR2 Killed reader@hacking:~/booksrc $ 

Las señales en sí son muy simples, pero la interacción entre procesos puede convertirse rápidamente en una compleja red de dependencias. Afortunadamente, en nuestro pequeño demonio web, las señales se usan solo para el apagado correcto, y todo se implementa de manera muy simple.

0x622 Tinyweb Daemon


La nueva versión de tinywebd es un demonio del sistema lanzado en segundo plano sin un terminal de control. Los datos de salida se escriben en el libro de registro con marcas de tiempo, y el programa en sí mismo espera a que la señal SIGTERM complete correctamente el trabajo.

Los cambios realizados en el original no son muy significativos, pero permiten un estudio más realista de la explotación de la vulnerabilidad. Los nuevos fragmentos de código están en negrita.

 tinywebd.c #include <sys/stat.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <time.h> #include <signal.h> #include "hacking.h" #include "hacking-network.h" #define PORT 80 // ,     #define WEBROOT "./webroot" //   - #define LOGFILE "/var/log/tinywebd.log" //    int logfd, sockfd; //       void handle_connection(int, struct sockaddr_in *, int); int get_file_size(int); //         //  void timestamp(int); //        //       void handle_shutdown(int signal) { timestamp(logfd); write(logfd, " .\n", 16); close(logfd); close(sockfd); exit(0); } int main(void) { int new_sockfd, yes=1; struct sockaddr_in host_addr, client_addr; //    socklen_t sin_size; logfd = open(LOGFILE, O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR); if(logfd == -1) fatal("  ");  if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1)        fatal(" "); if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) fatal("   SO_REUSEADDR"); printf("  tiny web.\n"); if(daemon(1, 0) == -1) //    fatal("   "); signal(SIGTERM, handle_shutdown);   //    handle_shutdown signal(SIGINT, handle_shutdown);   //    handle_shutdown timestamp(logfd); write(logfd, ".\n", 15); host_addr.sin_family = AF_INET;       //    host_addr.sin_port = htons(PORT);     //  ,    host_addr.sin_addr.s_addr = INADDR_ANY; //    IP memset(&(host_addr.sin_zero), '\0', 8); //    if (bind(sockfd, (struct sockaddr *)&host_addr, sizeof(struct sockaddr)) == -1) fatal("  "); if (listen(sockfd, 20) == -1) fatal("   "); while(1) { //   accept sin_size = sizeof(struct sockaddr_in); new_sockfd = accept(sockfd, (struct sockaddr *)&client_addr, &sin_size); if(new_sockfd == -1) fatal(" "); handle_connection(new_sockfd, &client_addr, logfd); } return 0; } /*         *         FD. *    -,    *   .     . */ void handle_connection(int sockfd, struct sockaddr_in *client_addr_ptr, int logfd) { unsigned char *ptr, request[500], resource[500], log_buffer[500]; int fd, length; length = recv_line(sockfd, request); sprintf(log_buffer, " %s:%d \"%s\"\t", inet_ntoa(client_addr_ptr->sin_addr), ntohs(client_addr_ptr->sin_port), request); ptr = strstr(request, " HTTP/"); //    if(ptr == NULL) { //    HTTP  strcat(log_buffer, "  HTTP!\n"); } else { *ptr = 0; //      URL ptr = NULL; //  ptr  NULL (           //  ) if(strncmp(request, "GET ", 4) == 0) //  GET ptr = request+4; // ptr -  URL if(strncmp(request, "HEAD ", 5) == 0) //  HEAD ptr = request+5; // ptr -  URL if(ptr == NULL) { //     strcat(log_buffer, "  !\n"); } else { //    ptr,     if (ptr[strlen(ptr) - 1] == '/')   //  ,                                        //  '/', strcat(ptr, "index.html");   //    'index.html' strcpy(resource, WEBROOT);   //  resource                                            //  strcat(resource, ptr);   //      fd = open(resource, O_RDONLY, 0);  //    if(fd == -1) { //     strcat(log_buffer, " 404 Not Found\n");           send_string(sockfd, "HTTP/1.0 404 NOT FOUND\r\n");                send_string(sockfd, "Server: Tiny webserver\r\n\r\n");                send_string(sockfd, "<html><head><title>404 Not Found</title>                                     </head>");            send_string(sockfd, "<body><h1>URL not found</h1></body></html>                                     \r\n"); } else {       //      strcat(log_buffer, " 200 OK\n");             send_string(sockfd, "HTTP/1.0 200 OK\r\n");           send_string(sockfd, "Server: Tiny webserver\r\n\r\n");            if(ptr == request + 4) { //    GET     if( (length = get_file_size(fd)) == -1)          fatal("     ");     if( (ptr = (unsigned char *) malloc(length)) == NULL)          fatal("      ");          read(fd, ptr, length);  //              send(sockfd, ptr, length, 0);  //              free(ptr);  //      } close(fd); //   } //   if  /        } //   if     } //   if    HTTP timestamp(logfd); length = strlen(log_buffer); write(logfd, log_buffer, length); //    shutdown(sockfd, SHUT_RDWR); //    } /*         *    .    -1. */ int get_file_size(int fd) { struct stat stat_struct; if(fstat(fd, &stat_struct) == -1) return -1; return (int) stat_struct.st_size; } /*          *   . */ void timestamp(fd) { time_t now; struct tm *time_struct; int length; char time_buffer[40]; time(&now); //       time_struct = localtime((const time_t *)&now); //    tm length = strftime(time_buffer, 40, "%m/%d/%Y %H:%M:%S> ", time_struct); write(fd, time_buffer, length); //       } 

Este programa crea un proceso duplicado que se ejecuta en segundo plano, escribe en el archivo de registro junto con las marcas de tiempo y finaliza correctamente después de recibir la señal correspondiente. El descriptor del archivo de registro y el socket que acepta la conexión se declaran como variables globales para que la función handle_shutdown () pueda completar su trabajo correctamente. Se define como un controlador de devolución de llamada para las señales de finalización e interrupción, lo que garantiza que el comando kill finalice el programa.

Aquí está el resultado de compilar, ejecutar y completar el programa. Preste atención a las marcas de tiempo en el registro y al mensaje de finalización que apareció después de que el programa recibió la señal correspondiente y llamó a la función handle_shutdown ().

 reader@hacking:~/booksrc $ gcc -o tinywebd tinywebd.c reader@hacking:~/booksrc $ sudo chown root ./tinywebd reader@hacking:~/booksrc $ sudo chmod u+s ./tinywebd reader@hacking:~/booksrc $ ./tinywebd   tiny web. reader@hacking:~/booksrc $ ./webserver_id 127.0.0.1 The web server for 127.0.0.1 is Tiny webserver reader@hacking:~/booksrc $ ps ax | grep tinywebd 25058 ? Ss 0:00 ./tinywebd 25075 pts/3 R+ 0:00 grep tinywebd reader@hacking:~/booksrc $ kill 25058 reader@hacking:~/booksrc $ ps ax | grep tinywebd 25121 pts/3 R+ 0:00 grep tinywebd reader@hacking:~/booksrc $ cat /var/log/tinywebd.log cat: /var/log/tinywebd.log: Permission denied reader@hacking:~/booksrc $ sudo cat /var/log/tinywebd.log 07/22/2007 17:55:45> . 07/22/2007 17:57:00>  127.0.0.1:38127 "HEAD / HTTP/1.0" 200 OK 07/22/2007 17:57:21>  . reader@hacking:~/booksrc $ 

El nuevo programa tinywebd procesa el contenido HTTP, como el tinyweb original, pero se comporta como un demonio del sistema porque no tiene un terminal de control y la salida es un archivo de registro. Ambos programas son vulnerables al mismo desbordamiento del búfer, pero esto es solo el comienzo de esta vulnerabilidad. Ahora que hemos elegido a tinywebd como objetivo del ataque, le mostraré cómo evitar la detección después de que haya ingresado a otra máquina.

»Se puede encontrar más información sobre el libro en el sitio web del editor
» Contenidos
» Extracto

20% de descuento en cupones para Habrozhitel - Hacking

Source: https://habr.com/ru/post/es413989/


All Articles