Al realizar un análisis de seguridad de una red WiFi es habitual el uso de aircrack, una herramienta muy popular para capturar información de redes WiFi. Esta utilidad tiene soporte nativo en sistemas operativos como Linux o FreeBSD, pero no es posible la captura de tráfico en sistemas Windows.

La herramienta Acrylic Wifi proporciona una interfaz que permite activar el modo monitor de una tarjeta Wireless y capturar todos los paquetes recibidos. Gracias a ella, podemos obtener de forma nativa en sistemas Windows de plenas capacidades de monitorización WiFi.

Para hacer uso de esta funcionalidad es necesario seguir los siguientes pasos:

  • Instalar el driver NDIS que se proporciona junto con la versión gratuita y superiores de Acrylic WiFi
  • Descargar la utilidad modificada de airodump para windows junto con la librería DLL (aquí).
  • Para ejecutarlo abre una consola de comandos (cmd.exe), nos situamos en el directorio bin y ejecutamos del siguiente modo:

airodump-ng.exe TRLNDISAircrackWrapper.dll

En caso de tener más de una interfaz de red inalámbrica podemos seleccionarlas añadiendo un pipe seguido del número de interfaz (1,2,3, etc). Como ejemplo:

airodump-ng.exe “TRLNDISAircrackWrapper.dll|2”

Se pueden añadir opciones antes del parámetro de la dll, que en realidad hace las veces de interfaz. La sintaxis en la ayuda de airodump es la siguiente:

Airodump-ng 1.2 beta2 - (C) 2006-2013 Thomas d'Otreppe
http://www.aircrack-ng.org

usage: airodump-ng <options> <interface>[,<interface>,…]

Sustituir el parámetro de <interfaz> por la librería será suficiente para tener la suite aircrack completamente funcional de manera nativa en Windows!

Información para desarrolladores:

En esta sección se explica brevemente los pasos seguidos para portar aircrack a Windows de forma nativa. Nos centramos en la utilidad airodump-ng que es la que capturará tráfico. Puedes descargar el código fuente de airodump con las modificaciones aquí: Descargar código fuente de aircrack para windows

Airodump-ng está preparado para usar una interfaz a través de una librería dinámica que encapsule el comportamiento de un dispositivo de conexión inalámbrico. En caso de que se le pase una librería en el parámetro de , cargará esta dll en busca de las 7 funciones que encapsulan toda la funcionalidad necesaria para interactuar con la interfaz inalámbrica.

Esto se puede observar en el código de aircrack, en el fichero osdep/osdep.h aparece la definición de la estructura que representa una interfaz inalámbrica:


struct wif {
int (*wi_read) (struct wif *wi, unsigned char *h80211, int len,
struct rx_info *ri);
int (*wi_write) (struct wif *wi, unsigned char *h80211, int len,
struct tx_info *ti);
int (*wi_set_channel) (struct wif *wi, int chan);
int (*wi_get_channel) (struct wif *wi);
int (*wi_set_freq) (struct wif *wi, int freq);
int (*wi_get_freq) (struct wif *wi);
void (*wi_close) (struct wif *wi);

int (*wi_fd) (struct wif *wi);
int (*wi_get_mac) (struct wif *wi, unsigned char *mac);
int (*wi_set_mac) (struct wif *wi, unsigned char *mac);
int (*wi_set_rate) (struct wif *wi, int rate);
int (*wi_get_rate) (struct wif *wi);
int (*wi_set_mtu) (struct wif *wi, int mtu);
int (*wi_get_mtu) (struct wif *wi);
int (*wi_get_monitor) (struct wif *wi);

void *wi_priv;
char wi_interface[MAX_IFACE_NAME];
};

Esta estructura es la que se usará a lo largo del código de la suite para interactuar con las interfaces inalámbricas. El siguiente paso es analizar dónde se rellena esta estructura para el caso concreto de un sistema Windows y para la tool airodump-ng. Esto se encuentra en los archivos osdep/cygwin.c y osdep/cygwin.h. Este último contiene lo siguiente:


// DLL function that have to be exported
#define CYGWIN_DLL_INIT cygwin_init
#define CYGWIN_DLL_SET_CHAN cygwin_set_chan
#define CYGWIN_DLL_INJECT cygwin_inject
#define CYGWIN_DLL_SNIFF cygwin_sniff
#define CYGWIN_DLL_GET_MAC cygwin_get_mac
#define CYGWIN_DLL_SET_MAC cygwin_set_mac
#define CYGWIN_DLL_CLOSE cygwin_close

/*
* Prototypes:
* int CYGWIN_DLL_INIT (char *param);
* int CYGWIN_DLL_SET_CHAN (int chan);
* int CYGWIN_DLL_INJECT (void *buf, int len, struct tx_info *ti);
* int CYGWIN_DLL_SNIFF (void *buf, int len, struct rx_info *ri);
* int CYGWIN_DLL_GET_MAC (unsigned char *mac);
* int CYGWIN_DLL_SET_MAC (unsigned char *mac);
* void CYGWIN_DLL_CLOSE (void);
*
* Notes:
* - sniff can block and inject can be called by another thread.
* - return -1 for error.
*
*/

En este archivo de cabecera aparecen los símbolos (funciones) que se cargarán de la librería dinámica junto con sus prototipos.

En el archivo osdep/cygwin.c se concretiza la carga de la librería en una estructura interna para uso del módulo cygwin, en base a las definiciones del archivo de cabecera:


/* load lib */
lib = dlopen(file, RTLD_LAZY);
if (!lib)
goto errdll;

priv->pc_init = dlsym(lib, xstr(CYGWIN_DLL_INIT));
priv->pc_set_chan = dlsym(lib, xstr(CYGWIN_DLL_SET_CHAN));
priv->pc_get_mac = dlsym(lib, xstr(CYGWIN_DLL_GET_MAC));
priv->pc_set_mac = dlsym(lib, xstr(CYGWIN_DLL_SET_MAC));
priv->pc_close = dlsym(lib, xstr(CYGWIN_DLL_CLOSE));
priv->pc_inject = dlsym(lib, xstr(CYGWIN_DLL_INJECT));
priv->pc_sniff = dlsym(lib, xstr(CYGWIN_DLL_SNIFF));

Esta estructura se parece a la struct wif vista en un principio, pero no es idéntica. El módulo cygwin.c hará de bridge entre la interfaz expuesta por struct wif y la de la librería dinámica. Esto aparece en la siguiente porción de código, al inicializarse el módulo:

static struct wif *cygwin_open(char *iface)
{
struct wif *wi;
struct priv_cygwin *priv;

/* setup wi struct */
wi = wi_alloc(sizeof(*priv));
if (!wi)
return NULL;
wi->wi_read = cygwin_read;
wi->wi_write = cygwin_write;
wi->wi_set_channel = cygwin_set_channel;
wi->wi_get_channel = cygwin_get_channel;
wi->wi_close = cygwin_close;
wi->wi_fd = cygwin_fd;
wi->wi_get_mac = cygwin_get_mac;
wi->wi_set_mac = cygwin_set_mac;
wi->wi_get_rate = cygwin_get_rate;
wi->wi_set_rate = cygwin_set_rate;
wi->wi_get_monitor = cygwin_get_monitor;

La estructura wif se rellena con funciones pertenecientes al módulo cygwin. Éstas se encargaran de transformar dichas peticiones en algo comprensible para la librería.

Finalmente, para el desarrollo de la librería, la interfaz que será necesaria implementar será la siguiente (para Visual Studio C++):

#include

#ifdef __cplusplus
extern "C" {
#endif

#ifdef TRLNDISWRAPPERDLL_EXPORTS
#define TRLNDISWRAPPER_API __declspec(dllexport)
#else
#define TRLNDISWRAPPER_API __declspec(dllimport)
#endif

#pragma pack(push)
#pragma pack(1)
struct tx_info {
unsigned int ti_rate;
};

struct rx_info {
uint64_t ri_mactime;
int32_t ri_power;
int32_t ri_noise;
uint32_t ri_channel;
uint32_t ri_freq;
uint32_t ri_rate;
uint32_t ri_antenna;
};
#pragma pack(pop)

TRLNDISWRAPPER_API int cygwin_init(char *p);
TRLNDISWRAPPER_API int cygwin_set_chan(int channel);
TRLNDISWRAPPER_API int cygwin_inject(void *buf, int len, struct tx_info *ti);

TRLNDISWRAPPER_API int cygwin_sniff (void *buf, int len, struct rx_info *ri);

TRLNDISWRAPPER_API int cygwin_get_mac (unsigned char *mac);
TRLNDISWRAPPER_API int cygwin_set_mac (unsigned char *mac);
TRLNDISWRAPPER_API void cygwin_close(void);

#ifdef __cplusplus
}
#endif

Problemas detectados en el port

Hasta aquí es la teoría, implementaríamos dicha interfaz y al pasársela por argumento al airodump-ng, todo debería funcionar correctamente.

Sin embargo, como es habitual, no todo funciona a la primera… Nos encontramos con que aparentemente la librería se carga correctamente pero el programa no hace nada.

Bug en la comunicación de procesos del aircrack

El porte de aircrack a Windows se realiza a través de las cygwin, que implementa una API para proveer compatibilidad POSIX. Es decir, dentro del código de aircrack nos encontramos a llamadas como fork(), dlopen(), etc. existentes en aquellos sistemas compatibles con POSIX (como Linux) pero que no están disponibles en Windows. Cygwin aporta esa capa de compatibilidad necesaria con librerías que implementan dichas funciones, de forma que será posible compilar y usar programas desarrollados en entornos.

No todo funciona exactamente igual en Windows que en un sistema Unix. Airodump-ng hace uso de fork’s para gestionar la pantalla, teclado, captura de datos, channel hopping, etc. La comunicación entre ellos se hace mediante pipes. Depurando se observó que estos procesos no eran capaces de comunicarse entre sí. El núcleo de comunicación consta del siguiente código :


int net_read_exact(int s, void *arg, int len)
{
ssize_t rc;
int rlen = 0;
char *buf = (char*)arg;
while (rlen < len) { rc = recv(s, buf, (len - rlen), 0);if (rc < 1) { if (rc == -1 && (errno == EAGAIN || errno == EINTR)) { usleep(100); continue; } return -1; } buf += rc; rlen += rc; } return 0; }

Aparentemente todo estaba correcto. A excepción de un detalle: se está realizando una llamada recv sobre un descriptor que es un pipe, no un socket. Esto provoca un error, que hará que no se puedan recibir mensajes, de forma que los procesos empezarán a enviar correctamente hasta que se bloquearán en el momento en el que se llene la cola del pipe, puesto que nunca se vacía.

La solución pasa por sustituir esa línea por esta otra:


rc = read(s, buf, (len - rlen));

Una vez hecho esto, los procesos se comunicarán correctamente y veremos cómo se muestra correctamente la información por pantalla.

Bug en las cygwin

El siguiente problema ocurre con el channel hopping, el cual es inexistente permaneciendo siempre en el canal 1. La causa subyacente tiene poco que ver con esta funcionalidad, sino con la implementación interna de las cygwin de fork() y dlopen().

A la hora de hacer un fork() toda la memoria, descriptores, etc se copian al nuevo proceso (obviando el COW), que pasa a ser un gemelo del original.

Sin embargo, ¿qué ocurre con las referencias a librerías cargadas dinámicamente con un dlopen()?

La respuesta es que la implementación de fork() de las cygwin no mantiene las referencias de librerías dinámicas cargadas en tiempo de ejecución. Como resultado tendremos dos procesos idénticos después de hacer el fork(), a excepción de que uno de ellos ha perdido las referencias cargadas con dlopen() y dlsym(), de manera que cuando realice una llamada a alguna de estas referencias no retornará nunca de ella, quedando el proceso colgado.

Este es un comportamiento anómalo de las cygwin y ha sido reportado a su mailing list.

Una solución a este problema implicaría modificar código del núcleo de aircrack, modificando la gestión que hace de los procesos. Hemos optado por un workaround simple que arregle el problema con el menor cambio de código posible:


if (pc->pc_did_init){
do_cygwin_open(wi, wi->wi_interface);
pc->pc_close();
}

Los procesos hacen un do_free() al iniciarse, cerrando todas las interfaces que tuviera el proceso padre. En ese momento de liberación es donde se decidió colocar la llamada de carga de la librería, justo antes de que se use una de las referencias que dejarían colgado el proceso.

Resumen

Airodump-ng permite implementar nuestra propia librería de comunicación, posibilidad que lo dota de una importante flexibilidad, siempre y cuando arreglemos los pequeños obstáculos que hemos comentado. El parche completo es el siguiente:

diff -rupN orig/src/osdep/cygwin.c new/src/osdep/cygwin.c
--- orig/src/osdep/cygwin.c 2010-11-01 15:09:50.000000000 +0100
+++ new/src/osdep/cygwin.c 2014-01-17 08:24:30.059134000 +0100
@@ -376,8 +376,13 @@ static void do_free(struct wif *wi)
close(pc->pc_pipe[1]);
}

- if (pc->pc_did_init)
+ if (pc->pc_did_init){
+ //do_free() is called after a fork(), which loses dll references.
+ //In order to use them it's necessary reload them for this new
+ //process, so do_cygwin_open is called before closing handles.
+ do_cygwin_open(wi, wi->wi_interface);
pc->pc_close();
+ }

assert(wi->wi_priv);
free(wi->wi_priv);
diff -rupN orig/src/osdep/network.c new/src/osdep/network.c
--- orig/src/osdep/network.c 2013-05-26 00:45:58.000000000 +0200
+++ new/src/osdep/network.c 2014-01-16 17:31:27.936351300 +0100
@@ -99,7 +99,7 @@ int net_read_exact(int s, void *arg, int
int rlen = 0;
char *buf = (char*)arg;
while (rlen < len) {
- rc = recv(s, buf, (len - rlen), 0);
+ rc = read(s, buf, (len - rlen));

if (rc < 1) {
if (rc == -1 && (errno == EAGAIN || errno == EINTR)) {

Descarga aquí Airodump para windows