Hay dos lugares donde puede buscar evidencia de tal vulnerabilidad: syslog y coredumps.
Registro del sistema
Supondré que está hablando sobre el registro del intento de explotación en sí mismo y nada antes (como los registros de acceso remoto), y que no ha habilitado ningún registro adicional que pueda realizar un seguimiento de los accesos a los archivos o la creación del proceso. Para el propio exploit, normalmente no queda rastro en los archivos de registro si el ataque tuvo éxito. La única forma sería si fallara al menos una vez y provocó un fallo de segmentación, que puede registrarse en el búfer de registro del kernel (que se muestra a través de dmesg
), que probablemente sea registrado por su elección del daemon syslog.
Archivos principales
Si tiene habilitado Coredumps, es posible que pueda obtener mucha más información sobre la causa exacta de la falla de seguridad, suponiendo que la tenga y sepa cómo usar gdb, y tener el binario original que se había bloqueado. ya que los procesos registrarán su memoria central en el disco en caso de que se produzca una caída. Pero al igual que verifica el registro del sistema, esto solo funciona si el exploit ha fallado al menos una vez y ha provocado un bloqueo. Tampoco proporciona ninguna información sobre quién es el atacante, cuál fue su vector de explotación, qué persiguen o qué utilizaron para atacar. Además, recuerde que, a menos que tenga una configuración especial, los coredumps se escriben en el disco con los mismos permisos que el programa que se bloqueó, por lo que en algunos casos un atacante puede tener suficientes privilegios para borrarlo (y examinar ese acto se aplica a los forenses del sistema de archivos que no voy a entrar y no está particularmente relacionado).
Endurecimiento en tiempo de compilación
En cuanto a lo que hago, compilo todos los programas localmente, por lo que puedo usar funciones de seguridad en tiempo de compilación que intentan desencadenar bloqueos en intentos de explotación. Algunos ejemplos:
-
UBSAN (Undined Behavior SANitizer): atrapa varios tipos de comportamiento no definido en la instrucción ud2
, que es una instrucción intencionalmente ilegal y bloquea el programa. Algunos tipos de comportamiento indefinido se pueden usar para explotar un programa.
-
SSP (Stack Smashing Protector): crea un canario secreto en la pila y cancela el programa si alguna vez se sobrescribe. Un atacante que solo tenga una primitiva de escritura arbitraria no podrá explotar fácilmente un programa que utiliza SSP, a menos que también tenga una primitiva de lectura arbitraria y pueda leer el valor del canario (y ponerlo en su código de desbordamiento).
-
FORTIFY_SOURCE - Una colección de archivos de encabezado que envuelve ciertas funciones que esperan una longitud como su argumento, como strncpy()
, para agregar verificaciones de tiempo de ejecución para asegurar que la longitud máxima no se exceda cuando se conoce en compilación. tiempo.
-
PIE (Position Independent Executable): agrega soporte a un ejecutable para direccionar bibliotecas cuya base no se conoce en tiempo de compilación. Esto permite que un programa utilice ASLR, que carga bibliotecas en direcciones aleatorias para que sea más difícil explotar los desbordamientos de búfer.
-
RELRO (solo para relocaciones): en modo parcial, hace que las secciones ELF sean de solo lectura después de que el enlazador haya configurado todo. En modo completo, además hace que el GOT sea de solo lectura.
-
BINDNOW: hace que las bibliotecas se resuelvan en tiempo de carga ejecutable, en lugar de cuando se hace referencia por primera vez, lo que es necesario para aprovechar todo el potencial de RELRO.
Hay más funciones relacionadas con la seguridad que se pueden hacer en tiempo de compilación, como CFI y SafeStack. Esa fue solo una lista de algunos de los más populares. Puede usar el script actualizado checksec para ver si un binario se ha compilado con varias marcas de protección:
$ checksec --file /bin/bash
RELRO STACK CANARY NX PIE RPATH RUNPATH FORTIFY Fortified Fortifiable FILE
Full RELRO Canary found NX enabled PIE enabled No RPATH No RUNPATH Yes 13 33 /bin/bash
Ejemplos de entradas de registro
En general, los registros solo se crearán cuando el programa se compile con los indicadores de endurecimiento correctos (con la excepción de los valores predeterminados que se producirán independientemente de cómo compile el binario). Estos indicadores de endurecimiento no garantizan que un atacante no pueda explotar un binario dado, pero hace que sea más probable que un exploit falle y posiblemente deje entradas en los registros. Algunos ejemplos:
Segfault que activa SIGSEGV (en este caso, una desreferencia de puntero nulo):
$ gcc <<< 'void main() { char *a = 0; *a = 5; }' -x c - 2>/dev/null
$ ./a.out
a.out[24078]: segfault at 0 ip 000000720ab0b6f4 sp 000003c8ad38d170 error 6 in a.out[720ab0b000+1000]
Segmentation fault
UBSAN activa SIGILL (en este caso, un índice fuera de límites):
$ gcc <<< 'void main() { char a[2]; a[-1] = 5; }' -x c - -fsanitize=undefined -fsanitize-undefined-trap-on-error 2>/dev/null
$ ./a.out
traps: a.out[24130] trap invalid opcode ip:62a7e64673 sp:3be53c5fe20 error:0 in a.out[62a7e64000+1000]
Illegal instruction
Stack Smashing Protector (escrito solo para stderr):
$ gcc <<< 'void main() { char a[2]; strcpy(a, "abcd"); }' -x c - -fstack-protector-all -include string.h 2>/dev/null
$ ./a.out
*** stack smashing detected ***: ./a.out terminated
Killed
FORTIFY_SOURCE (escrito solo en stderr):
$ gcc <<< 'void main() { char a[2]; strncpy(a, "abcd", 4); }' -x c - -O -D_FORTIFY_SOURCE=2 -include string.h 2>/dev/null
$ ./a.out
*** buffer overflow detected ***: ./a.out terminated
Killed
Pero, una vez más, y no puedo enfatizar esto lo suficiente, un exploit exitoso no dejará registros como estos . Debe comprobar si hay signos de la intrusión original y no confiar en la posibilidad de que su vulnerabilidad falle.