5. Filtrado en Snort

Anuncio
Capítulo 5: Filtrado en Snort
5. Filtrado en Snort
5.1. Introducción
En esta parte del documento se retoma la idea del uso de un filtrado previo a la búsqueda exacta de
patrones, cuya estructura ocuparía un espacio en memoria mucho menor que el del algoritmo de
búsqueda exacta. Con este filtrado, según las ideas lanzadas por el equipo de sigMatch, explicadas
en el capítulo tres, se reduciría el número de cache misses, puesto que se evitarían muchas llamadas
a la unidad de verificación y, por tanto, se conseguiría una ejecución más rápida de la aplicación.
Será en este capítulo donde se iniciará la puesta en práctica de esta idea de filtrado.
Para ello se ha debido trabajar bastante. No ha bastado únicamente haber entendido y asimilado la
propuesta de Jignesh M. Patel y su equipo, también se ha tenido que realizar un estudio de la teoría
algorítmica y, no menos importante, se ha debido profundizar en el funcionamiento de Snort para
así poder llegar a una conclusión y decidir cuál es el punto óptimo del código fuente en el que se
podría situar un filtrado previo.
Durante este capítulo, en primer lugar se realizará un recorrido por los posibles puntos de
implementación del filtro dentro de Snort. Estas localizaciones fueron las que se barajaron como
candidatas para albergar el filtro. Se explicarán los aspectos que llevaron a elegir una única
localización posible, descartando las demás.
Una vez seleccionada la localización final, se estudiará el funcionamiento interno del motor de
detección de Snort a nivel de código fuente para, de esta forma, poder decidir de qué manera se
integrará el filtrado previo. Antes de ello se hará un repaso por los distintos métodos de búsqueda
que Snort pone a disposición del usuario y se comprobará que casi todos implementan el algoritmo
Aho-Corasick, pero en diferentes variantes.
Finalmente, se explicará a nivel de código fuente cómo se integrará el filtro dentro del motor de
detección de Snort, indicando de qué forma el usuario podrá activar o desactivar el filtro, qué
ficheros han debido modificarse para ello y cómo se ha preparado el motor de detección para
albergar la implementación de un filtrado previo.
5.2. Situación del filtro en Snort
Después de plantear los objetivos del proyecto, de haber estudiado los principios de la algoritmia
desde sus inicios hasta la actualidad, de comprender y asimilar la propuesta del filtrado previo
1
Capítulo 5: Filtrado en Snort
bautizada como sigMatch y de haber conocido en profundidad el funcionamiento de Snort, ha
llegado el momento de discutir el diseño general que adquirirá la implementación del filtrado previo
en Snort.
Al plantear el desarrollo de un filtrado de tráfico que pudiera agilizar la búsqueda de patrones de
Snort, se establecieron varios principios que la implementación final debería cumplir. Uno de ellos
fue la escalabilidad, que estaba íntimamente ligada al diseño general que establecería dónde debía
introducirse el filtro dentro de Snort.
Snort es una aplicación en continua evolución, con un alto ritmo de lanzamiento de versiones
nuevas. Por tanto, la solución elegida para el diseño debía facilitar su portabilidad a nuevas
versiones. Esto también beneficiaría al hecho de que cualquier usuario pudiera implantar el filtrado
en su propia instalación de Snort, simplemente siguiendo unas sencillas instrucciones que no le
obligarían a modificar excesivamente el código fuente ni a poseer unos conocimientos de Snort
elevados. Para cumplir este objetivo, el código original de Snort debía ser lo menos modificado
posible.
Puesto que el motor de detección es el componente en el que se encuentra el algoritmo de búsqueda
multi-patrón, las posibilidades de implementación del filtro previo se limitan a insertarlo entre el
final del descodificador de paquetes y antes del inicio del motor de detección, estando situado el
preprocesador entre estos dos componentes.
Se recuerda que la función del descodificador es la de recibir el paquete entrante del módulo DAQ y
descodificarlo para que pueda ser organizado en estructuras de datos que posteriormente serán
utilizadas por el resto de componentes de Snort. Sin embargo, el preprocesador se dedica al estudio
del paquete e incluso existen casos, ya vistos en el cuarto capítulo, en los que se modifica el
contenido del mismo antes de que continúe su camino hacia el motor de detección.
Seguidamente, se hará un recorrido por el camino definido entre la salida del descodificador y la
entrada del motor de detección. En este recorrido se explicará el razonamiento llevado a cabo para
decidir cuáles serían los posibles puntos de implementación del filtrado, analizando en cada caso su
viabilidad. Finalmente, se planteará la conclusión a la que se llegó en su momento y que determinó
el lugar donde se decidió situar el filtro.
5.2.1. Propuestas iniciales
Se eligieron tres puntos posibles en los que el filtro podría tener cabida. El primero de ellos se
situaría a la salida del descodificador y antes del paso por el preprocesador. La siguiente opción
propuesta fue situar el filtro como un módulo de preprocesador más que se ejecutara al final de
todos ellos, una vez que cada uno hubiera ejecutado su tarea. La tercera y última opción que se
planteó fue colocarlo justo antes de la llamada al motor de detección.
5.2.1.1. Entre el Descodificador y el Preprocesador
El primer punto que se encuentra en el camino es la unión del final del descodificador con el inicio
2
Capítulo 5: Filtrado en Snort
del preprocesador, como se muestra en la figura 5.1. Se podría considerar la posibilidad de
implementar el filtro previo en este punto, pero se mostrarán los motivos que llevaron a pensar que
no era éste el lugar adecuado.
Antes de seguir, se sugiere al lector repasar el ejemplo que se presentaba como demostración de la
utilidad del componente preprocesador, concretamente del preprocesador HTTP Inspect, explicado
en el capítulo cuatro. En él se presentaba el caso en el que un cliente solicitaba una petición de la
dirección web http://servidor/%61%74%61%71%75%65%2E%65%78%65 a un servidor. Se comprobó
que al no usar un preprocesador que modificara la URI el paquete no se detectaba como una
amenaza.
Figura 5.1: Implementación del filtro entre la salida del descodificador y la entrada del
preprocesador.
En la situación que se quiere plantear ahora se propone el mismo ejemplo pero con la diferencia de
que el preprocesador siempre estará presente y será el filtro el que podrá estar implementado o no.
El ejemplo se muestra en la figura 5.2. En él se puede ver el caso en el que se implementa el filtro
después del descodificador de paquetes y antes del preprocesador, como se muestra en la figura 5.1,
así como el caso en el que se omite el uso del mismo.
Se tiene la firma ataque.exe y el resumen que obtiene el filtro en su fase offline es que.e. Se
puede observar como en el caso superior, al usar el filtro en la posición indicada, éste no encuentra
el resumen de la firma dentro de la dirección web, por tanto consideraría el paquete como si de un
verdadero negativo se tratara y lo descartaría sin dejarlo avanzar hacia el preprocesador y el motor
de detección. Mientras que en una situación normal en la que no se implementara el filtro, como la
que se muestra en el caso inferior, el paquete pasaría por el preprocesador, que modificaría el
contenido del paquete para que pudiera ser detectado por el motor de detección.
Este fue el motivo por el que quedó descartada en su momento la opción de situar el filtro delante
del preprocesador. Una de las premisas del filtrado fue que no se admitiría la pérdida de ninguna
alerta y se comprobó que esta solución no cumplía con ella.
3
Capítulo 5: Filtrado en Snort
Figura 5.2: Ejemplo en el que se coloca el filtro entre el descodificador y el preprocesador.
5.2.1.2. Como último módulo del Preprocesador
La segunda posibilidad fue la de colocar el filtro después de haber ejecutado cada módulo del
preprocesador. De esta forma, el paquete llegaría a este punto con las modificaciones necesarias
realizadas por preprocesadores como el HTTP Inspect o el Frag3.
La solución propuesta para este caso sería la que se representa en la figura 5.3, en la que aparece el
filtro como un módulo más del preprocesador y cuya función sería la de analizar el contenido de
cada paquete en busca de los resúmenes de firmas obtenidas del conjunto de reglas de Snort. Este
nuevo módulo de filtrado debería ejecutarse en último lugar, detrás de los módulos ya existentes en
el preprocesador, para así evitar casos como el de la figura 5.2, en los que al no preprocesar un
ataque podía darse el caso de que no pudiera ser detectado. No obstante, se verá que este punto
tampoco es el adecuado para implementar el filtro.
Para demostrarlo se debe retroceder de nuevo al capítulo cuatro y repasar el esquema de la función
Preprocess, mostrada en la figura 4.15. En él se puede apreciar el modo en el que se invoca a la
función Detect. El proceso no consiste en ejecutar todos los módulos de los preprocesadores para
después pasar el control al motor de detección, sino que se puede invocar al motor de detección
después de la ejecución de un único preprocesador. En el siguiente párrafo, y con la ayuda de la
4
Capítulo 5: Filtrado en Snort
Figura 5.3: Implementación del filtro antes de la salida del preprocesador, considerándolo así como
un módulo más del preprocesador.
figura 5.4, se aclara esta situación.
Ya se ha visto en el capítulo anterior que los módulos de preprocesador se pueden dividir en dos
tipos: el que no modifica el contenido del paquete y el que sí lo hace. Ahora bien, suponiendo que
existan M módulos del primer tipo y N del segundo, se llamarían 1+N veces a la función Detect.
La primera llamada se produciría después de ejecutar los M módulos que no modifican el contenido
del paquete y las siguientes N llamadas se realizarían después de ejecutar cada módulo de los que
modifican el paquete. Por tanto, como se muestra en la figura 5.4, situar el filtro como último
módulo de preprocesador no serviría de mucho, puesto que antes de poder filtrar los paquetes, éstos
ya se habrían enviado hacia el motor de detección en varias ocasiones.
Una alternativa sería colocar el filtro antes de cada llamada a la función Detect, como se muestra
en la figura 5.5. En principio, esta sería la solución óptima; no se obstaculizaría la tarea de los
módulos del preprocesador y ningún paquete dejaría de pasar por el filtro. No obstante, en cuanto a
eficiencia, presenta algunos puntos débiles que se describen en los siguientes párrafos.
En el capítulo cuatro se ha estudiado una estructura denominada lista enlazada 3D en la que se
almacenan las reglas y las estructuras de detección del algoritmo de búsqueda multi-patrón.
También, se ha visto que un paquete recorre esta lista enlazada hasta llegar al RTN al que pertenece
y una vez allí comprueba si cumple con alguna de las reglas representadas por los OTNs. Este
camino que sigue el paquete no se realiza hasta entrar en el motor de detección, es decir, hasta que
no se ejecuta la función Detect. Por tanto, en el punto en el que se sitúa el filtro en la figura 5.5
aún no se habría recorrido la lista enlazada, y realizar un filtrado en esa parte del código sería
cuanto menos un problema.
Una de las ventajas que aporta la lista enlazada es el hecho de evitar que se deba realizar una
búsqueda multi-patrón sobre todas las reglas. La lista permite que la búsqueda que se realice incluya
únicamente a un grupo reducido de reglas que tengan en común sus cabeceras. Esto quiere decir que
el hecho de que el paquete no haya recorrido todavía la lista enlazada hasta el nivel de las RTN
obligaría a realizar un filtrado usando resúmenes obtenidos de todo el conjunto de reglas, puesto
que no se conoce aún a qué grupo de reglas pertenece el paquete.
5
Capítulo 5: Filtrado en Snort
Figura 5.4: Implementación del filtro como último módulo del preprocesador.
Ya se explicó en el capítulo cuatro, cuando se planteó el ejemplo de la búsqueda de palabras en
libros de varios colores, que este camino llevaba a dos soluciones diferentes. La primera de ellas era
ineficiente, mientras que la segunda daba un mejor rendimiento. Trasladando el ejemplo a la
problemática presente, la primera solución sería la equivalente a realizar una búsqueda de todos los
patrones del conjunto de resúmenes y, en el caso de obtener una coincidencia, se comprobaría si el
protocolo y direcciones IP y puertos coincidiera con la cabecera de la regla en cuestión. La segunda
solución, consistiría en organizar los resúmenes en conjuntos cuyas cabeceras correspondiesen con
el protocolo y direcciones IP y puertos del paquete, para después realizar una búsqueda de patrones
sobre un único conjunto.
Como en el ejemplo de los libros, esta última solución proporcionaría un mejor rendimiento, pero
llevaría a cabo una tarea que ya la realiza el motor de búsqueda de patrones, que es recorrer la lista
enlazada para llegar a un RTN. Por tanto, en cuanto a rendimiento, podría decirse que no es muy
óptimo realizar este recorrido por la lista dos veces: antes de entrar en el motor de detección y una
vez dentro.
Como conclusión, se podría pensar que el mejor punto en el que se podría implementar el filtro
sería aquél en el que el paquete ya hubiera recorrido la lista enlazada, pero siempre antes de haber
6
Capítulo 5: Filtrado en Snort
ejecutado la búsqueda de patrones.
Figura 5.5: Implementación del filtro antes de cada llamada al motor de detección.
5.2.1.3. Dentro del Motor de Búsqueda de Patrones (MPSE)
Esto nos lleva al tercer punto posible de la implementación del filtro. Podría parecer una buena
opción incluir el filtrado dentro del motor de búsqueda multi-patron, como se muestra en la figura
5.6. De esta forma bastaría con recorrer sólo una vez la lista enlazada y, en lugar de invocar
directamente la búsqueda de patrones, se pasaría el paquete por el filtro para que éste decidiera si
reenviarlo a la búsqueda multi-patrón, o no.
El modo en el que está programada la búsqueda de patrones en Snort hace posible la
implementación en este punto. Esta es la situación que menos interfiere en la tarea esencial de la
aplicación, por lo que facilita la integración en cada cambio de versión, algo importante para el
usuario final de Snort que desee incorporar un filtrado previo.
Con todo esto, y puesto que no se encontraron mayores inconvenientes, se puso en marcha el
desarrollo de esta idea de implementación.
En la siguiente sección se explicará con detalle el funcionamiento del motor de detección, para que
7
Capítulo 5: Filtrado en Snort
Figura 5.6: Implementación del filtro al principio del motor de detección.
de esta forma se pueda entender mejor el próximo capítulo, en el que se explica detalladamente el
desarrollo de filtro.
5.3. Motor de detección de Snort
Después de haber decidido, sobre el esquema de Snort, cuál es el lugar óptimo donde situar el filtro,
se procede al estudio del código fuente del IDS para así poder comenzar con la implementación de
la idea de filtrado que propuso el equipo de sigMatch. Concretamente, esta sección se centrará en el
funcionamiento del motor de detección de Snort, ya que es en este punto donde se implementará el
filtrado y por tanto se requerirá un conocimiento en profundidad del funcionamiento interno de este
componente.
Ya se ha visto que, cuando Snort recibe un paquete, éste atraviesa la aplicación hasta llegar a la lista
enlazada de reglas, recorriéndola hasta el último nivel, en el cual se decidirá si el paquete debe
pasar por el motor de detección, o no. También se sabe que la invocación al motor de detección se
realiza mediante la función mpseSearch, localizada en el fichero mpse.c. Cada función
contenida en el archivo en cuestión se encarga de redirigir el curso de la aplicación hacia el
algoritmo de búsqueda de patrones que se haya elegido en el archivo de configuración
snort.conf.
A continuación se enumerarán algunos de los algoritmos de búsqueda de patrones, así como ciertos
aspectos interesantes que convendría saber de ellos para, de esta forma, conocer en mayor medida el
funcionamiento del motor de detección.
5.3.1. Algoritmos en Snort
8
Capítulo 5: Filtrado en Snort
El algoritmo más usado en Snort es Aho-Corasick. No obstante, a la hora de implementarlo existen
diversas variantes que pueden ser seleccionadas a través del archivo de configuración. Ya se
introdujeron en el capítulo tres algunas de estas variantes, como son Full matrix, Sparse, Banded y
Sparsebands. Es la primera variante que se menciona la que ofrece mayor rendimiento pero, como
inconveniente, requiere un consumo de memoria mayor. También, se explicó que el consumo de
memoria elevado podría reducir considerablemente este rendimiento. En cambio, las demás
variantes no ofrecen un rendimiento tan alto pero cuentan con un consumo de memoria moderado.
En 2009, la variante de Aho-Corasick establecida por defecto en el archivo de configuración de
Snort era Aho-Corasick Binary NFA (ac­bnfa), la cual contaba con un bajo consumo de memoria
y un alto rendimiento. A finales de ese mismo año se introdujo la opción split­any­any, que
permitía construir en un modo diferente la lista enlazada de reglas.
Por defecto, las reglas cuyos puertos de origen y destino son any­any se incluyen en todos los
grupos de puertos que no son del tipo any­any, como se muestra en la figura 5.7. De esta forma
sólo sería necesario evaluar una vez cada paquete entrante.
Figura 5.7: Lista enlazada de reglas sin la opción split-any-any.
La nueva opción agrupa todas las reglas del tipo any­any en un nuevo grupo, como se puede ver
en la figura 5.8, con lo que se consigue reducir drásticamente el uso de memoria, puesto que solo
existe una copia de las reglas del tipo any­any en toda la lista enlazada, en lugar de una copia por
cada grupo de la lista. Por tanto, si en el primer modo hay N grupos, aplicando la nueva opción
quedarían N+1 grupos, pero se reduciría bastante el uso de memoria de cada uno de ellos. No
obstante, esta solución requiere dos evaluaciones por paquete en los casos en los que el puerto de
9
Capítulo 5: Filtrado en Snort
Figura 5.8: Lista enlazada de reglas construida con la opción split-any-any.
origen y destino del paquete se corresponda con algún grupo que no sea any­any, ya que tendría
que evaluar su propio grupo de reglas así como el nuevo grupo introducido correspondiente a las
reglas any­any.
La opción split­any­any se puede usar con cualquier método de búsqueda disponible en Snort,
pero su desarrollo fue ideado especialmente para el método ac, que corresponde con la variante
Aho-Corasick Full, y que era la que requería un consumo de memoria mayor. Al combinarlo con
este método el consumo de memoria se ve drásticamente reducido y aunque sería lógico pensar que
el rendimiento podría llegar a bajar a la mitad no sucede así puesto que, como ya se sabe, un bajo
consumo de memoria puede mejorar el rendimiento mediante, entre otros factores, la reducción del
número de cache misses.
La conjunción del método de búsqueda ac junto con el uso de la opción split­any­any fue
acotada como el nuevo método de búsqueda ac­split, que es el método establecido por defecto
actualmente.
Esta mejora no hubiera sido posible sin las ideas aportadas por Charlie Lasswell, un
miembro de la comunidad de Snort. Este tipo de situaciones son las que deben concienciar
a los desarrolladores de software de las ventajas que supone el hecho de crear aplicaciones
Open Source.
5.3.2. Dentro del Motor de detección
En este punto del capítulo se analizará el código fuente correspondiente al motor de detección. Ya
en el capítulo cuatro se hizo un estudio interno de Snort, desde la ejecución de la aplicación hasta la
llamada a las funciones del motor de detección. A partir de aquí se retomará el mismo estudio,
continuando con el análisis de las funciones implicadas en el proceso de búsqueda de patrones.
En capítulos anteriores se ha explicado que el funcionamiento de Snort, así como el de su motor de
detección, se puede separar en dos fases: offline y online. Para el estudio de su código fuente se
continuará con esta división, por lo que en primer lugar se hará un recorrido interno a lo largo de la
fase offline y en segundo lugar se analizará la fase online.
10
Capítulo 5: Filtrado en Snort
5.3.2.1. Fase offline
A continuación, y con ayuda de la figura 5.9, se recorrerá la fase offline desde que es invocada por
la función fpCreateFastPacketDetection hasta que alcanza el algoritmo de búsqueda de
patrones, que creará su estructura de detección, nombrando cada función implicada y describiendo
la tarea que lleva a cabo1:
1. Una vez que, desde SnortInit, se han registrado y configurado los preprocesadores y
módulos de salida, obtenido las reglas y creada la lista enlazada, se realiza la llamada a la
función fpCreateFastPacketDetection, que se encargará de realizar la llamada al
motor de búsqueda de patrones para que gestione la creación de la estructura de detección.
2. Desde fpCreateFastPacketDetection se llega, después de pasar por algunas
funciones intermedias, ya vistas en el capítulo cuatro, y dedicadas al posicionamiento sobre
la lista enlazada de reglas, a la función fpCreatePortObject2PortGroup, que
ejecutará
las
funciones
fpAllocPms,
fpAddPortGroupRule
y
fpFinishPortGroup, en ese orden. Cada una de estas tres funciones realizará llamadas
al motor de detección.
3. La primera de ellas es fpAllocPms, invocará a mpseNew, para asignar en memoria la
estructura de búsqueda de patrones.
4. La siguiente, fpAddPortGroupRule, y a través de fpFinishPortGroupRule,
invocará a mpseAddPattern para añadir los patrones que formarán parte del diccionario
que use el algoritmo de búsqueda multi-patrón.
5. La última, fpFinishPortGroup, ejecutará la función mpsePrepPatterns, que
creará la estructura de detección en base a todos los patrones que fueron añadidos en el paso
anterior.
Hay que tener en cuenta que una vez dentro del motor de búsqueda de patrones (MPSE), este
invocará al algoritmo correspondiente para realizar todas las tareas mencionadas anteriormente,
como son la asignación inicial de memoria de la estructura de detección, realizada por acsmNew2,
la inclusión de patrones en la estructura de detección, por parte de acsmAddPattern2, y la
creación definitiva de la estructura de detección, llevada a cabo por acsmCompile2.
En este caso se han incluido las funciones usadas por el método de búsqueda ac­split, las cuales
están contenidas en el fichero acsmx2.c. Pero podría haberse usado cualquier otro método sin
haber alterado apenas el esquema del diagrama de secuencia de la figura 5.9.
5.3.2.2. Fase online
A partir de aquí, Snort se mantiene a la espera de la llegada de paquetes. Cuando comienza a
1 En el anexo II se muestran fragmentos del código fuente de las funciones más importantes involucradas en los
procesos offline y online de la búsqueda de patrones de Snort que se describen en este capítulo.
11
Capítulo 5: Filtrado en Snort
Figura 5.9: Diagrama de secuencia de la fase offline del motor de detección.
recibirlos invoca, a través del preprocesador, al motor de detección para que realice la búsqueda de
patrones.
Este proceso se puede ver en el diagrama de secuencia mostrado en la figura 5.10. También en este
caso se han incluido las funciones usadas por el método de búsqueda ac­split, pero el ejemplo
podría trasladarse al caso en el que se usara cualquier otro método. A continuación se describirá el
recorrido descrito en el diagrama:
12
Capítulo 5: Filtrado en Snort
1. Recordando el diagrama de secuencia de la fase online de Snort propuesto en el capítulo
cuatro, desde snort.c se invocaba al módulo preprocesador a través de la función
Preprocess, localizada en el fichero detect.c. Ésta a su vez ejecutaba la función
Detect, que sería la que llamaría a fpEvalPacket, contenida en fpdetect.c. Este
camino ya se vio en el capítulo anterior, por lo que se ha omitido en la figura 5.10.
2. Una vez invocada la función fpEvalPacket, ésta, en función del protocolo del paquete,
llamará a la siguiente función. En el caso de tratarse de un paquete TCP invocará a
fpEvalHeaderTcp, mientras que al tratarse de paquetes UDP, IP o ICMP, se invocarán a
las funciones fpEvalHeaderUdp, fpEvalHeaderIp o fpEvalHeaderIcmp,
respectivamente.
3. A continuación, e independientemente del protocolo del paquete, la función a la que se haya
redirigido la ejecución llamará a fpEvalHeaderSW, que será la encargada de invocar al
motor de detección.
4. Esta invocación se realizará a través de la llamada a la función mpseSearch, contenida en
el fichero mpse.c, y que a su vez ejecutará el método de búsqueda de patrones elegido en
Figura 5.10: Diagrama de secuencia de la fase online del motor de detección.
13
Capítulo 5: Filtrado en Snort
el fichero de configuración, siendo ac­split en el caso del ejemplo y, por tanto,
llamándose a la función acsmSearch2.
5.4. Implementación del filtro en el Motor de detección
Llegados a este punto, había que decidir dónde implementar el filtrado previo dentro del motor de
detección. Se sabe que desde el fichero mpse.c se realizan todas las llamadas relacionadas con la
búsqueda de patrones, tanto las correspondientes a la fase offline como a la fase online. Por lo que
se consideró que este fichero podría ser la situación óptima a partir de la cual implementar el filtro.
El objetivo del filtrado propuesto por el equipo de sigMatch no era otro que el de analizar el tráfico
captado por Snort antes de enviarlo al algoritmo de búsqueda de patrones, para decidir si se debía
llevar a cabo el análisis final a manos de la unidad de verificación o, si por el contrario, se debía
descartar el paquete. La unidad de verificación mencionada sería el algoritmo elegido en el archivo
de configuración, que en el ejemplo correspondería con las funciones del fichero acsmx2.c. Por
tanto, habría que situar el filtrado antes de que, en la fase online, se ejecutara la función
acsmSearch2, encargada de realizar la búsqueda de patrones.
En el caso de la realización de la fase offline del filtrado, también se tomó la decisión de llevarla a
cabo antes de la ejecución de la fase offline de la unidad de verificación final. Aunque en este caso
no hubiera sido determinante colocarla antes o después, puesto que durante la fase offline no se
toman decisiones que afecten a la ejecución del algoritmo definitivo.
La propuesta de implementación del filtrado previo a la búsqueda de patrones se puede ver en las
figuras 5.11 y 5.12, en las que se muestran los diagramas de secuencia de las fases offline y online
del motor de detección modificadas para poder llevar a cabo la integración de dicho filtrado.
En ambos diagramas aparece un nuevo fichero llamado redb.c. Este fichero se crea para albergar
las distintas funciones, entre las que se encuentran redbNew, redbAddPatterns,
redbCompile y redbSearch, que se encargarán de llevar a cabo la tarea de filtrado propuesta
por los autores de sigMatch.
Una vez decidida la localización que ocupará la implementación del filtro, se debe que preparar
Snort para que contemple la opción de activar o desactivar un filtrado previo. En el desarrollo del
proyecto se propuso que esta opción pudiera ser establecida desde el archivo de configuración de
Snort; así, del mismo modo que se elegía el método de búsqueda deseado, se pudiera elegir también
si se deseaba incluir un filtrado previo en la búsqueda de patrones.
La nueva opción incluida en Snort se representa por la palabra filter y va seguida del tipo de
filtro que se quiera añadir. Note el lector que se ha elegido esta sintaxis para seguir con la línea ya
propuesta en el archivo de configuración de Snort.
A continuación se muestran, como ejemplo, dos líneas del fichero de configuración relacionadas
con la configuración del motor de detección; en la primera se establece Aho-Corasick en su modo
14
Capítulo 5: Filtrado en Snort
Figura 5.11: Diagrama de secuencia de la fase offline del motor de detección modificado para
implementar el filtrado previo.
de implementación Full y usando la variante split­any­any, como método de búsqueda multipatrón, y la opción de longitud máxima fijada a 20 bytes, mientras que la segunda línea añade a
todo lo anterior la inclusión del uso de un filtrado previo denominado redborder:
config detection: search­method ac­split max­pattern­len 20
config detection: search­method ac­split filter redborder \
max­pattern­len 20
Para introducir esta nueva opción se tuvieron que modificar los ficheros parser.c, mpse.h,
fpcreate.h y fpcreate.c pertenecientes al código fuente de Snort. De aquí a lo que queda
15
Capítulo 5: Filtrado en Snort
Figura 5.12: Diagrama de secuencia de la fase online del motor de detección modificado para
implementar el filtrado previo.
de documento conviene saber que cada modificación llevada a cabo en cualquier fichero del código
fuente de Snort quedó señalada con las reseñas rbw o rbc, según el tipo de modificación adoptada,
para que de esta forma pudiera ser localizada más fácilmente.
A continuación se describen los cambios realizados en cada uno de los cuatro ficheros anteriores,
pudiendo ver los fragmentos de código fuente modificados en el anexo III:
•
parser.c: Este fichero contiene algunas de las funciones encargadas de recoger las
opciones del fichero snort.conf para almacenarlas en las estructuras de Snort. En este
fichero se define la nueva opción filter, para que posteriormente sea leída en la función
ConfigDetection. Si se detecta la nueva opción en el fichero de configuración,
entonces se llama a la función fpSetDetectFilter, situada en fpcreate.c.
•
mpse.h: En este fichero residen las definiciones de los distintos métodos de búsqueda de
patrones disponibles. Será aquí donde se incluirán las definiciones relativas a los distintos
tipos de filtrado que se quieran añadir.
•
fpcreate.h: Aquí se encuentra la estructura FastPatternConfig y se almacenan las
opciones que se leyeron desde el fichero de configuración relativas al motor de detección.
Por lo que se decidió añadir una opción más que indicara la inclusión del tipo de filtrado
16
Capítulo 5: Filtrado en Snort
previo definido en snort.conf. También se declara la función fpSetDetectFilter,
que se usará en fpcreate.c.
•
fpcreate.c: A este fichero se llega al ejecutar desde parser.c la función
fpSetDetectFilter. Esta función se encarga de almacenar en la estructura
FastPatternConfig la opción de filtrado que se especificó en el archivo de
configuración.
Hasta este momento, la única opción de filtrado que se incluye es redborder, que corresponde
con la implementación del filtro sigMatch. Una vez que se añadió esta opción en el fichero de
configuración, hubo que preparar al motor de detección y modificar su código fuente para que
contemplara la implementación de un filtro previo. Para ello, se realizaron variaciones en la
estructura MPSE, así como en funciones como mpseNew, mpseAddPattern,
mpsePrepPatterns y mpseSearch, entre otras. Todas estas modificaciones se realizaron
dentro del archivo mpse.c y están disponibles para su consulta en el anexo III.
Básicamente, lo que se hizo en cada una de las funciones modificadas fue realizar una
comprobación de la opción de filtrado que se estableció desde el fichero de configuración. Si esta
opción estaba activada, entonces en cada una de las funciones se llevaría a cabo una tarea similar a
la que realizaría cualquier algoritmo de búsqueda de patrones. Por ejemplo, en el caso de que el
filtrado estuviera activo, en la función mpseNew se asignaría en memoria la estructura del filtro, en
mpseAddPattern se añadirían los patrones de firmas de los cuales se extraerían sus resúmenes,
en la función mpsePrepPatterns se compilaría la estructura de detección del filtro y en
mpseSearch se realizaría la búsqueda a través de dicha estructura, reenviando el control a la
unidad de verificación final en el caso en que se hubiera marcado un paquete como candidato.
Como resumen de este capítulo, se podría decir que se ha elegido la situación del filtrado que
parecía más óptima, se ha propuesto un modo de integración dentro del motor de detección y se ha
construido, sobre el código fuente de Snort, el esqueleto que soportará la integración de dicho
filtrado. En el siguiente capítulo se estudiará y se explicará cómo se ha llevado a cabo el desarrollo
del filtro sigMatch dentro de Snort.
17
Descargar