Although I am interested and work in different computer security-related projects, I am not affiliated to any kind of underground movement.
I know that there are many underground groups which have made important contributions to computer security, I don't think that attacking from the shadows to vulnerabilities is not the way to go - The correct way is, instead, to raise conciousness in users, administrators and programmers on how things should be done. To whoever can contribute in further ways, with their own research and development, please go ahead. But everything, everything in an open and public way, and respecting above all other things other people's rights.
Si bien yo me intereso y trabajo en diferentes proyectos de seguridad en cómputo, no estoy afiliado a ningún tipo de movimiento underground.
Si bien hay muchos grupos underground que han hecho importantes contribuciones a la seguridad en cómputo, el camino a seguir en mi opinión no es el del ataque oculto a las vulnerabilidades, sino que el de conicentizar a usuarios, administradores y programadores de cómo debemos hacer las cosas. Para quien tenga la capacidad de contribuir no sólo con concientización sino que con desarrollo propio, adelante. Pero todo, todo de manera abierta, pública, y respetando por sobre de todas las cosas los derechos de los demás.
| Title | Urna electrónica es menos confiable que el papel |
| Publication Type | Broadcast |
| Year of Publication | 2011 |
| Authors | Wolf, G |
| Abstract | Interview for UDG Noticias (Guadalajara), talking about how electronic voting stations are inherently insecure |
| URL | http://medios.udg.mx/node/11157 |
| Attachment | Size |
|---|---|
| The audio as transmitted (mp3) | 22.46 MB |
| Title | Prácticas seguras de programación para sistemas Web |
| Publication Type | Journal Article |
| Year of Publication | 2009 |
| Authors | Wolf, G |
| Refereed Designation | Refereed |
| Journal Title | Ventana informática |
| Issue | 21 (jul-dic 2009) |
| Pages | 91-111 |
| Start Page | 91 |
| Journal Date | 07/2009 |
| Type of Article | Artículo monográfico |
| ISSN | 0123-9678 |
| Keywords | cross-site scripting, galletas HTTP, inyecciones SQL, mapeo objeto-relacional, redefinición, resumen criptográfico, Seguridad en cómputo, sesiones HTTP, XSS |
| Abstract | El campo de la seguridad en cómputo muchas veces es visto como uno donde el trabajo es investigativo (búsqueda de nuevas categorías de fallos), reactivo (corrección de fallos encontrados, o incluso buscarlos proactivamente) o, en el peor de los casos, un campo donde los personajes más destacados son quienes saben abusar de los sistemas ajenos. Nada más lejano de la realidad — En la seguridad en cómputo, el rol más importante debe ser el desarrollador de sistemas, una de las piezas más importantes de la sociedad actual. Este trabajo parte definiendo qué debemos entender por seguridad en cómputo, y por qué este concepto debe ir más allá de definiciones duras y frías, para reflejar que antes que otra cosa, el cómputo es una disciplina con la misma flexibilidad que los estudios humanísticos. La parte medular del trabajo se centra en ejemplificar, analizando tres categorías de vulnerabilidad informática de alto perfil hoy en día (inyecciones de SQL, Cross-Site Scripting y manejo de sesiones a través de galletas HTTP), de especial relevancia para los sistemas Web desarrollados hoy en día — Y poco abordadas en específico por los principales textos con que se enseña la disciplina. |
| Full Text | La Evolución del rol que cumplen los sistemas en las organizaciones ha cambiado por completo -afortunadamente- el punto de vista que la mayor parte de los desarrolladores tiene con respecto a la seguridad. |
| Attachment | Size |
|---|---|
| Versión impresa (PDF) | 395.62 KB |
Short text going over several tools that can help a system administrator work on incident prevention, network monitoring and intrusion detection in Linux. I presented this talk in UNAM's Departamento de Seguridad en Cómputo internal admin-unam seminar, in June 2005. It was also published in the August 2005 issue of PC Magazine en español.
Texto corto mencionando diversas herramientas que pueden ayudar a un administrador de sistemas implementar prevención de incidentes, monitoreo de red y detección de intrusos en Linux. Presenté esta plática en el seminario interno Admin-UNAM del Departamento de Seguridad en Cómputode la UNAM, en junio de 2005. Fue publicado además en la revista de agosto de 2005 de PC Magazine en español.
Are you looking for an easy to implement backup solution that allows you to have incremental backups, with low resources consumption, and allowing for immediate retrieval of your data? Rsync and the basic characteristics of any Unix filesystem can be your greatest allies. I prepared this talk for the Admin-UNAM December 2005 seminary, organized by the Computer Security Department, DGSCA, UNAM.
¿Buscas una solución de respaldos simple de implementar, con bajo uso de recursos, que te permita tener respaldos incrementales disponibles de inmediato? Rsync y las características del sistema de archivos de cualquier Unix pueden ser tu principal aliado - Preparé esta plática para el seminario Admin-UNAM de diciembre del 2005, organizado por el Departamento de Seguridad en Cómputo, DGSCA, UNAM.
| Attachment | Size |
|---|---|
| Respaldos con rsync — Presentación en PDF | 624.11 KB |
| Respaldos con rsync — Fuente en LaTeX Beamer | 146.86 KB |
This talk was prepared for IV National Free Software Conference in Universidad Mayor, Real y Pontificia
de San Francisco Xavier de Chuquisaca, Sucre, Bolivia. This is more or less the second part for my Network security talk, bringing down to earth some of the concepts to more concrete solutions (although still fairly generic).
I presented it in 2005 for UNAM's Computer Security Department's Admin-unam internal seminar; I re-wrote it as a full-text article, also available here.
Plática preparada para el IV Congreso Nacional de Software Libre en la Universidad Mayor, Real y Pontificia
de San Francisco Xavier de Chuquisaca, Sucre, Bolivia. Es una continuación a la plática de Seguridad en redes, aterrizando algunos de los conceptos a soluciones más concretas (aunque aún bastante genérico).
Presenté este trabajo en 2005 para el seminario interno Admin-UNAM del ; re-escribí el material como un artículo a texto completo, también disponible aquí.
This talk was prepared for the National Free Software Conference CONSOL 2003 and for Computer Security DGSCA/UNAM 2003 Conference. I analize the main ideas that were used for designing Multics, which of those ideas were adapted for the Unix model, which main shortcomings the traditional Unix model has, and what ideas have evolved to make Unix better.
Plática preparada para el Congreso Nacional de Software Libre CONSOL 2003 y para el Congreso de Seguridad en Cómputo DGSCA/UNAM 2003. Analizo las ideas principales que fueron empleadas en Multics, cuáles de ellas fueron adaptadas para el modelo de Unix, qué carencias tiene el modelo tradicional de Unix, y qué propuestas ha habido para mejorarlo.
I presented this tutorial at Computer Security DGSCA/UNAM 2003 Conference. I talk about the basic characteristics for an Ethernet network, what is sniffing, some sniffers useful for a network administrator, and some advices for protecting against unauthorized sniffers in our network.
Presenté este tutorial en el Congreso de Seguridad de Cómputo DGSCA/UNAM 2003, menciono en ella las características básicas del funcionamiento de las redes Ethernet, qué es el sniffing, varios sniffers útiles para un administrador, y algunos consejos para protegernos de sniffers no autorizados en nuestra red.
I was invited to give a talk on wireless networks to 27 Simposium Internacional de Sistemas Computacionales y Tecnologías de la Información, organized by Tecnológico de Monterrey.
I did not prepare a presentation or slides, so instead of that, I promised I would answer to every question made by the audience. Here are the promised answers.
Fui invitado a dar una conferencia de Seguridad en Redes Inalámbricas al 27 Simposium Internacional de Sistemas Computacionales y Tecnologías de la Información, organizado por el Tecnológico de Monterrey. A falta de llevar una presentación
preparada, me comprometí a responder a todas las preguntas que me hicieron por escrito los asistentes. Aquí están, pues, las respuestas.
Si quieres agregar algo a esta página, ya sea enviarme una pregunta adicional, ampliar una respuesta, o lo que sea, mándame un correo.
¿Cómo puedo conectarme a una red inal´mbrica con una PC (laptop)? ¿Se tiene que hacer alguna configuración?
Investigaciones formales
Notas de prensa
Otros documentos
Bibliografía útil que no tuve a tiempo para la plática
Hay una gran cantidad de licencias libres, que aplican probablemente de diferente manera alguno de estos criterios. Las más conocidas son:
El núcleo de Linux, la colección de compiladores GCC, el editor Emacs, el lenguaje Perl, la base de datos MySQL, las utilidades básicas de los sistemas Linux, y una muy gran cantidad de aplicaciones están protegidas con la licencia GPL.
El servidor de Web Apache, el servidor de XWindow XFree, el lenguaje PHP, la base de datos PostgreSQL, la mayor parte de los sistemas operativos FreeBSD, OpenBSD y NetBSD, y muchos otros proyectos están liberados bajo licencias BSD.
En resumen, el software gratuito no necesariamente es libre. El software libre es gratuito en el sentido de que no es necesario pagar por redistribuirlo. Sin embargo, el desarrollo del software libre sí cuesta dinero, al igual que cualquier desarrollo profesional. Este costo se cubre al liberar lo que fue programado específicamente para un proyecto y puede servir para los demás, al hacer un programa o biblioteca de funciones que muy probablemente nos pueda servir a futuro en otros desarrollos, etc.
La comunidad del software libre tiene una ética muy clara y muy marcada. Muchas veces hacemos desarrollos simplemente por hobby, por creerlos necesarios o porque son una buena idea, no para recibir una remuneración por ellos. Por último, es importante agregar que los usuarios y desarrolladores del software libre no promovemos ni apoyamos la piratería - Defendemos que el modelo de desarrollo que seguimos es superior, y que es mucho mejor utilizar código libre a utilizar programas propietarios, tanto para el desarrollador como para el usuario o cliente. Sin embargo, si queremos que nuestras licencias sean respetadas, tenemos que respetar a las licencias que otros decidan elegir.
El que el código fuente esté disponible y que cualquier pueda meterle mano y redistribuirlo no significa que los cambios que haga cualquiera sean admitidos en el proyecto principal. Yo puedo, por ejemplo, modificar una copia de los fuentes de Linux e intentar redistribuirla con mis modificaciones malignas. Sin embargo, mis modificaciones no serán aceptadas obviamente por los mantenedores oficiales del núcleo, y por tanto no llegarán a las distribuciones, mecanismo por el cual casi todos los usuarios nos hacemos de nuestras copias del sistema. Podrí intentar distribuirlo yo mismo, sin embargo por medio de firmas criptográficas (MD5 checksum, típicamente) cualquiera puede confirmar que los paquetes que tenemos sean legítimos. Además, si yo distribuyo una copia modificada, tendré que hacerlo entre mis conocidos, dañando únicamente mi propio prestigio.
Por último, no es indispensable contar con el código fuente de un programa para distribuirlo modificado - Tomemos, por ejemplo, los programas típicos en Windows - Probablemente sean miles de archivos, y cada uno de ellos ocupa al menos decenas de megabytes. Estos paquetes no contienen ningún tipo de verificación criptográfica. Agregar un archivo al paquete, un binario que envuelva a uno de los binarios legítimos, modificando parte de su conducta o su interacción con el mundo, no es en absoluto difícil.
¿Cuál será el sistema operativo más seguro?
El proyecto OpenBSD ha creado el sistema operativo mundialmente reconocido como el sistema más seguro del mundo. El equipo de desarrollo de OpenBSD nació de una escisión del proyecto NetBSD, un sistema Unix libre y multiplataforma. El motivo de la separación fue precisamente el énfasis en la seguridad - Theo de Raadt, líder del proyecto, prefirió por sobre de agregar nuevas capacidades al sistema el llevar a cabo una árdua auditoría del código fuente, erradicando no únicamente las vulnerabilidades de seguridad que fueran encontrando, sino que todos los errores detectados, e inclusive todas las malas prácticas de programación.
De este tan difícil esfuerzo surgió el sistema operativo OpenBSD, el cual desde su liberación en 1996 no ha tenido ni una sóla vulnerabilidad que proporcione privilegios de administrador a un atacante remoto en la instalación por default. El grupo de programadores de OpenBSD ha continuado el desarrollo de su sistema orientándolo hacia la seguridad. El exitoso proyecto OpenSSH, una implementación completamente libre y no cubierta por patentes del protocolo Secure Shell (ssh) disponible para prácticamente todos los sistemas operativos, fue lidereado por varios programadores de OpenBSD. OpenBSD es también el primer sistema operativo en proporcionar soporte a aceleradoras criptográficas en hardware.
A lo largo de los años, Microsoft ha sido muy poco responsable respecto a la seguridad. En el ciclo de vida de sus productos de rango servidor (NT Server, W2k, XP Professional) sólo hay cinco o seis actualizaciones (Service Packs), cada una de las cuales mide varios megabytes, y I>parcha varias decenas de vulnerabilidades y otros problemas. Esto, en contraposición a un gran número de pequeños parches específicos a uno u otro paquete, disponibles a los pocos dís de haber sido descubierto el problema.
Por último, la granularidad de la instalación es muchísimo mayor en los sistemas libres. Cuando hago una instalación de un sistema libre tengo varios miles de paquetes que puedo decidir instalar. Algunos de estos paquetes medirán sólamente un par de kilobytes, mientras que otros serán de decenas de megabytes. Puedo elegir si instalar o no entorno gráfico, motor de bases de datos, servidores de diferentes tipos, etc. Esto no sólo reduce la carga que en determinado momento tiene nuestro servidor, sino que lo mantiene más seguro -- Todo código introduce posibles vulnerabilidades, y entre menos código tengamos instalado, más seguro será nuestro sistema. En un sistema Unix podemos elegir mucho mejor qué instalar y qué no.
No podemos dejar de lado, claro está, el mantener un claro registro de dónde tenemos instalados puntos de red, a quién pertenecen, la direcci&oacut;en MAC dirección física del adaptador de red) de los dispositivos que ahí conectemos, y siempre que sea posible no instalar puntos en espacios públicos, ya que para un intruso es muchas veces mucho más fácil conectarse a través de la red local que buscar el acceso remotamente.
Si tenemos una red inalámbrica es mucho más difícil asegurar físicamente nuestra red. De todos modos, utilizando las capacidades de cifrado de las redes inalámbricas, como las que menciono aquí, ubicando nuestros puntos de acceso tan al centro de nuestras instalaciones como sea posible para evitar "derramar" señal, y en general utilizando el sentido común al instalar nuestra red, podremos evitar exponernos de más.
Recuerden que una red inalámbrica puede ser muy conveniente y económica, pero si es instalada sin el cuidado necesario, puede ser nuestra mayor vulnerabilidad. Les sugiero fuertemente no instalar redes inalámbricas sin antes haber hecho un estudio en el que concluyan que las ventajas son superiores a los riesgos.
El poder controlar granularmente el acceso entre áreas les dará una gran flexibilidad para definir las reglas de filtrado de paquetes, incrementando fuertemente la seguridad de su red. Es importante recordar que la mayor parte de ataques, tanto intencionales como no intencionales, que sufre cualquier organización tienden a venir del interior de la misma.
La mayoría de las redes inalámbricas hoy en día están basadas en la especificación 802.11b de la IEEE, que es equivalente a una red Ethernet normal, sobre un medio inalámbrico, por lo que la respuesta a esta pregunta no se limita únicamente a redes inalámbricas. En una red Ethernet, el medio de comunicación (sea el cable o el espacio en el espectro electromagnético) es compartido entre todas las computadoras que conforman un segmento. Esto significa que cuando cualquier computadora quiere enviar un paquete a cualquier otra, tiene que cerciorarse primero que ninguna otra computadora esté utilizando la red - Si te interesa investigar más acerca de cómo funciona una red Ethernet, busca información acerca del mecanismo de resolución de conflictos CSMA/CD (Carrier Sense Multiple Access / Collision Detect).
Bueno... A lo que iba - En una red Ethernet, todo el tráfico generado por cualquiera de las computadoras llega a todas las demás. Normalmente, las interfaces de red de una computadora est´n configuradas para no enviar al procesador interrupciones innecesarias. Basta configurar la tarjeta de red para que entre en modo promiscuo (con la bandera I>promisc en ifconfig en los sistemas libres derivados de Unix) para que todo el tráfico de red sea procesado por el kernel. Hay varios programas que nos permiten volcar la información que recibimos con la interfaz de red en modo promiscuo, muy uacute;tiles para depurar el funcionamiento de una red. Estos programas se conocen como sniffers, dado que olfatean lo que ocurre en la red y me lo reportan de la manera que yo prefiera. Uno de los más simples y comunes, que fue el que utilicé en la demostración que hice en la red del ITESM, es tcpdump. Hay muchos más, pero todos se basan en el mismo principio de operación. Utilicé tcpdump pues viene como parte de varios sistemas operativos y es el de más simple uso, si bien pueden especificársele parámetros que me permiten especificar a detalle el modo de operación.
Me permito reformular esta pregunta, dado que un buen cifrado no puede ser estructurado - Al menos no por mí, el administrador de la red. La pregunta, entonces, se convierte en cómo elegir un buen cifrado para dar seguridad a nuestra red. Es importante llevar el cifrado a todos los niveles. No podemos confiar únicamente en cifrado al nivel que nos ofrece WEP, pues sería lo mismo que confiar en la confidencialidad que nos da una red Ethernet tradicional.
Si en su red van a desarrollar o implementar aplicaciones sobre Web, les sugiero hacerlas sobre canal cifrado - Prácticamente todos los navegadores actuales manejan comunicación sobre SSL, y del lado del servidor, también casi la totalidad de los servidores -tanto libres como propietarios- manejan este cifrado. Queda a ustedes la decisión de si comprar un certificado de identidad de alguna autoridad certificadora (como Verisign o Thawte) o firmar ustedes mismos su certificado - aquí tengo una justificación para no acudir a autoridades certificadoras.
Bases de datos como PostgreSQL permiten manejar todas las comunicaciones sobre canal cifrado con SSL. Para manejar sesiones remotas en servidores, sugiero fuertemente utilizar Secure Shell, específicamente la implementación OpenSSH, programada por el grupo de desarrolladores de OpenBSD. En caso de que el protocolo que ustedes requieran, o un programa cliente o servidor que lo implementen, no manejen cifrado nativamente, siempre pueden "envolverlo" con STunnel, programa que crea un túnel SSL para la comunicación de manera casi transparente para los programas.
El correo puede ser protegido fácilmente con PGP, con su implementación libre GnuPG. PGP nos permite, claro, además crear llaveros de confianza, dando a un medio tan poco confiable como el correo electrónico garantís de integridad, confidencialidad, autenticidad e irrefutabilidad. Buena parte de estos programas están basados en una biblioteca de cifrado SSL. La más común es OpenSSL, la cual deriva de SSLeay.
Los invito a revisar un documento que realicé para el Congreso de Seguridad en Cómputo 2000 realizado por el Departamento de Seguridad en Cómputo de la DGSCA, UNAM. Este documento habla acerca de la Infraestructura de llaves públicas (PKI - Public Key Infrastructure), parte fundamental de todas estas implementaciones.
Una vez hecha esta aclaración... Para conectarse a una red inalámbrica que no implementa cifrado WEP basta con ponerle la tarjeta de red inalámbrica a una portátil cualquiera. Típicamente en sistemas Unix no es necesario siquiera configurar los controladores de la tarjeta. Basta con esperar a que el servidor DHCP, típicamente existente en redes inalámbricas, nos asigne una dirección, y podemos comenzar a utilizar la red.
Dejando de lado el ejemplo del Tec, si hablamos de una red genérica, en la que sí hay WEP configurado, lo que necesitamos hacer es configurar a nuestra tarjeta de red con una de las llaves de uso de la red. ¿Cómo podemos hacer esto? Depende del sistema operativo y tipo de tarjeta que empleemos. Por ejemplo, en Linux podemos especificar el ESSID y la llave de cifrado WEP utilizando el programa iwconfig.
Para aumentar el alcance de los equipos, estos pueden conectarse a diferentes tipos de antenas. Existen antenas de alta ganancia unidireccional, de muy diferentes tipos (colinear, helicoidal, Yagi, semiparabólicas, etc.). Hay también antenas omnidireccionales, para aumentar tanto el radio de cobertura de un dispositivo inalámbrico como para aumentar la resistencia a interferencia. En esta página podrán encontrar información mucho más detallada acerca de los tipos de antena, cuándo conviene cada una de ellas y cómo construirlas en casa.
Estas antenas se pueden comprar prefabricadas, o se pueden hacer artesanalmente. Esto es un interesante y divertido experimento, que seguro les enseñará mucho. Hay instrucciones para hacer esto en las diferentes redes de aficionados que dan cobertura inalámbrica -conocidas como Guerrilla.net- a diferentes ciudades en todo el mundo (incluyendo a Tampico en nuestro país, y con la Ciudad de México en proyecto) - Una lista de estas redes en los Estados Unidos está aquí. Estas antenas típicamente se hacen ya sea con tubos PVC o similares, logran una ganancia muy similar a la de una Yagi profesional, a una fracción del precio.
La IEEE recomienda el uso de AES (Advanced Encryption Standard), que será pronto un estándar de cifrado en todo tipo de productos, pero actualmente este nivel de cifrado requiere mucho mayor trabajo matemático del que puede realizar un dispositivo tan simple como una tarjeta de red, y seguramente elevará el precio de venta del hardware que lo implemente, que se espera que aparezca a principios del 2003.
Una buena solución, como lo indica este artículo, es el uso de cifrado en capas superiores, específicamente en la capa de red (IP), requiriendo el uso del estándar IPSec para el uso de nuestra red inalámbrica. Si bien este es el mejor mecanismo para implementar verdadera seguridad, sigue teniendo la desventaja de significar una carga adicional al CPU - El cifrado que realiza IPSec es matemáticamente muy complejo. Sin embargo, de entre todas las opciones que tenemos, es sin duda la más completa y confiable.
Desafortunadamente, por simple omisión. La seguridad en muchas organizaciones, inclusive organizaciones muy grandes, es relegada, dándole muy baja prioridad o hasta ignorándola por completo. Es desafortunadamente demasiado común ver grandes organizaciones en las que no se han tomado ni las más básicas precauciones relativas a la seguridad - Y a nadie sorprende cuando reportan pérdidas millonarias por un ataque.
¿Por qué es tan común que, siendo tan importante y tan simple, la seguridad es relegada? En mi opinión, esto no puede calificarse más que de negligencia. Si bien no me es posible justificar que una organización no invierta en la seguridad adecuada para su red, sí puedo entender que hasta que no tengan un primer incidente importante no contraten a un equipo dedicado a seguridad. Sin embargo, el no configurar los aspectos más básicos de seguridad en una red inalámbrica, los cuales pueden ser implementados sin costo adicional, no puede calificarse más que de negligencia.
Cabe mencionar que, como lo menciona este artículo, configurar dos dispositivos, por más que ambos cumplan con el estándar 802.11b, para que interoperen correctamente con WEP puede ser mucho más difícil de lo que debería, por la falta de homogeneidad en las interfaces.
I presented this tutorial at Congreso de Seguridad en Cómputo 2000, and later rewrote it (and moved it to a free, portable format) for Días de Software Libre at Guadalajara, May 2002. In this tutorial, I review some important key points not to fall in security errors while programming with Perl. I later adapted it together with a coworker, Alex Juárez, and we presented it in English at YAPC::Europe 2002. Here you will find this text both in Spanish and in English.
Un tutorial que presenté para el Congreso de Seguridad en Cómputo en noviembre del 2000, y tras reescribirlo en un formato libre y portable, lo presenté de nuevo en los Días de Software Libre en Guadalajara, mayo del 2002. Lo adapté y traduje junto con mi compañero de trabajo Alex Juárez, y lo presentamos en el YAPC::Europe::2002. Reviso aquí una serie de puntos importantes para no caer en importantes errores de seguridad al programar con Perl. Está tanto en inglés como en español.
I presented this talk ath the II Computer Systems Engineering Conference at Nuevo León Technological Institute. Here I review general security concepts, explaining to a non-expert audience various important factors that as security experts -and as conscious users in general- we must take into account.
Presenté esta plática en el II Congreso de Ingeniería en Sistemas Computacionales del Instituto Tecnológico de Nuevo León. Reviso conceptos generales de seguridad, explicando a un público no iniciado en el tema los diversos factores que como profesionales en la seguridad -y como usuarios conscientes en general- debemos tener en cuenta.
| Attachment | Size |
|---|---|
| seg_en_redes.tar_.gz | 72.34 KB |
| seg_en_redes.pdf | 355.04 KB |
I gave this talk at the GNU/Linux 2001 conference in Jalapa, Veracruz, in September 2001. In this article I show different characteristics and security reccomendations using free operating systems and tools.
Esta plática la dí en el Congreso GNU/Linux 2001 en Jalapa, Ver., en septiembre del 2001. En este artículo muestro diferentes características y recomendaciones de seguridad utilizando los diferentes sistemas operativos y herramientas libres.
I wrote my final paper for graduation on implementing a generic connection wrapper that can be extended to understand and protect specific protocols. I presented this project at YAPC::NA 2001, and published a short article on Usenix's ;login: magazine (published in the June 2002 number).
Escribí mi proyecto final de graduación implementando un wrapper genérico de conexiones que puede ser extendido para comprender y proteger protocolos específicos. Presenté mi proyecto en el YAPC::NA 2001, y publiqué un corto artículo en la revista ;login: de Usenix, en el número de Junio del 2002.
Once again, I was invited to the seminar, with Computer Security Department of UNAM, this time in April 2001. The topic this time: Security guidelines in a Linux installation. How you can make a newly made Linux installation a difficult target for an attacker.
This same material, although updated and enhanced, was also presented as a tutorial at Congreso de Seguridad en Cómputo 2001, also organized (of course) by DSC, UNAM.
De nueva cuenta, me invitaron a participar en el Admin-UNAM del Departamento de Seguridad en Cómputo de la UNAM, esta vez en abril del 2001. El tema expuesto en esta ocasión: Recomendaciones de seguridad en una instalación de Linux. Cómo hacer que un Linux recién instalado sea una presa muy
difícil para cualquier atacante.
Este mismo material, aunque bastante ampliado, lo presenté también como tutorial en el Congreso
de Seguridad en Cómputo 2001, organizado también (claro está) por el DSC de la UNAM.
I presented this proposal about a backup system in the Computer Security 2001 Conference. Here I present a proposal for the implementation of a cyphered, automatic remote backup scheme.
Presenté esta propuesta de esquema de respaldo en el Congreso de Seguridad en Cómputo 2001, presenta una propuesta para la implementación de un esquema de respaldo remoto, cifrado, automático.
Gunnar Wolf
gwolf@campus.iztacala.unam.mx
Congreso de Seguridad en Cómputo 2001
UNAM FES Iztacala
Hay una gran cantidad de implementaciones ya existentes para llevar a cabo de manera automática en sistemas en red -- Por diferentes razones, ninguno de ellos me convenció para las necesidades (nada atípicas, en mi opinión) de mi situación. Lo que aquí presento es un esquema muy simple que permite hacer respaldos de manera automática sobre la red con cifrado y contemplando diferentes tipos de respaldo.
Si estoy proponiendo un nuevo esquema de respaldos, esto es porque los ya existentes tienen alguna carencia que no me permite resolver de una manera simple mis necesidades. Entonces pues, ¿qué hay de malo con los esquemas de respaldo que existen hoy?
...Que pueden no ser las de los demás, pero seguramente habrá alguien con necesidades similares
¿Por qué no usar cintas DAT?
El proceso que sigue una conexión es:
Proceso a agregar próximamente:
Presented on March 30, 2000, at CECEC 2000, ESIME Culuhacan
Presentado el 30 de marzo del 2000 en el CECEC 2000 en la ESIME Culuhacan
The full text for the lecture I gave for the GASU seminar organized by Computer Security
Department at UNAM in july, 2000.
El texto completo de la conferencia que dí para el Seminario GASU del Departamento de Seguridad en Cómputo de la UNAM en julio del 2000.
Talk I gave at the Admin-UNAM seminar, with Computer Security Department of UNAM, in September 2000.
Logcheck is a tool that checks periodically the system's log files, reporting to the administrator whatever is -according to operator-defined patterns- important.
Plática que dí acerca de esta herramienta en el seminario Admin-UNAM del Departamento de Seguridad en Cómputo de la UNAM en septiembre del 2000.
Logcheck es una herramienta encargada de revisar periódicamente las bitácoras del sistema y reportar al administrador lo que sea -según patrones predefinidos por el administrador- importante.
Talk I gave at the Admin-UNAM seminar, with Computer Security Department of UNAM, in September 2000.
PortSentry is a tool that permits easy detection of port scanning, and has the ability to act immediately upon detection.
Plática que dí acerca de esta herramienta en el seminario Admin-UNAM del Departamento de Seguridad en Cómputo de la UNAM en septiembre del 2000.
PortSentry es una herramienta que permite la fácil detección de barridos de puertos y tiene la capacidad de actuar de inmediato al detectarlos.
Tutorial I presented at Computer Security conference 2000, giving many examples of small Perl scripts that can help improve security at a site.
Tutorial que presenté en el congreso Seguridad en Cómputo 2000, dando varios ejemplos de pequeños scripts en Perl que pueden ayudar a la seguridad de un sitio.
Un maravilloso y últimamente muy popular lenguaje de programación de todo propósito, fácilmente extensible.
El lenguaje está diseñado particularmente para buena parte de las necesidades que como administradores de sistemas podemos requerir: Procesar grandes cantidades de datos, buscar patrones en grandes cantidades de datos, manejar conexiones de red, etc.
Todo el código que encuentres aquí está disponible bajo la Licencia Pública de GNU (GPL) versión 2 o posterior, a elección del usuario.
La licencia GPL estipula que el código puede ser libremente distribuído, leído, modificado y redistribuído, incluído en otros proyectos, tomar fragmentos, o utilizado en cualquier manera, siempre y cuando éste y todo código derivado de él sean siempre distribuídos bajo la licencia GPL.
Para mayor información acerca de las licencias libres (copyleft y no copyleft) y no libres, aquí hay una interesante exposición.
No, no es porque me guste el inglés. No hago esto por facilitarme el copiar ejemplos de un libro ni por pocho.
Creo que alguno de estos scripts será útil cuando menos a una persona que no sea hispanoparlante. Espero que así sea. Entre los hispanoparlantes con nivel técnico medio o elevado es mucho más
frecuente el dominio del inglés que en el caso contrario.
Espero que esta decisión mía no ofenda a ninguno de ustedes.
En este tutorial analizaremos varios scripts de Perl de diferentes grados de complejidad. Dada la naturaleza de éste medio, no reproduciré los scripts en la presentación, sino que daré un esbozo estructural y daré ligas a páginas donde éstos estén disponibles.
Los scripts no están preparados, procesados y listos, sino que son muestras de diferentes técnicas que ustedes podrán utilizar en sus programas, o primeros esbozos de programas que requieren ser
perfeccionados para utilizarse en ambientes de producción.
En un servidor con más de cien cuentas se puede volver imposible llevar a mano el registro de qué cuentas están activas, qué cuentas ya expiraron y hace cuánto tiempo. Este sencillo script de menos de 70 líneas puede generar un reporte periódico al administrador del estado de las cuentas en su sistema.
Por simplicidad, el script está construído alrededor de la estructura fija del archivo /etc/shadow. Transportarlo a otros sistemas, sin embargo, debe ser trivial.
Estamos ante uno de los mayores riesgos de seguridad: un programa que debe correr con privilegio de root, el único usuario con derecho de leer el archivo /etc/shadow. Es muy importante tomar todas las precauciones; el script corre con use strict y en modo -Tw (tainted y warnings). Fuera de la lectura inicial no tiene ninguna interacción con el mundo; la salida la envía a STDOUT. Esto es para evitar que alguien se aproveche de sus elevados atributos para atacar al sistema.
Este script está pensado para correr desde el crontab de root.
El funcionamiento de este script es muy sencillo. Tras cargar el módulo Date::Calc y activar el pragma strict, iteramos sobre cada línea del archivo /etc/shadow, revisando su octavo campo (recuerden que el número base de un arreglo es 0), acomodándolo acorde a su estado respecto a la fecha actual en uno de cinco arreglos.Posteriormente, por medio de otra muy simple rutina, imprime cada uno de estos arreglos en órden. El programa nos reportará en este órden las cuentas próximas a expirar o recién expiradas, las que expiraron en el último mes, las que llevan ya más de un mes expiradas, las que no han expirado y las que no tienen fecha de expiración.
En la línea 12 vemos que:
$dias=Delta_Days(1970,1,1,@fecha);
Al poblar los cinco arreglos pueden haber notado que lo hacemos con una referencia a un arreglo [$login,$dias]. Esto es para pasar dos datos en una sola variable escalar.
Al recibir estos datos en &reporta los separamos expresamente en $login y $dias para evitar que los datos nos lleguen en un formato no
previsto y afecten de alguna manera la ejecución y seguridad de nuestro programa.
#!/usr/bin/perl -Tw # expira.pl # # Reporta las fechas de expiracion de las cuentas de los usuarios # use strict; use Date::Calc qw(Today Delta_Days); my ($dias,@fecha,$file,@linea,@nuncaExpira,@expiraPronto,@expiraMes,@expiraAntes,@noExpirada); @fecha=Today; $dias=Delta_Days(1970,1,1,@fecha); $file='/etc/shadow'; # Solo root puede abrir el archivo /etc/shadow open(FILE,$file) or die 'Es necesario ejecutar este script como root'; while (@linea=split(/:/,<FILE>)) { if ($linea[7] eq '' || $linea[7] < 0) { # La cuenta no expira push (@nuncaExpira,[$linea[0],$linea[7]]); } elsif (($linea[7]-5)<$dias && ($linea[7]+5)>$dias) { # La cuenta expira o expiro en estos dias push (@expiraPronto,[$linea[0],$linea[7]]); } elsif ($linea[7]+30>=$dias && $linea[7]<$dias) { # La cuenta expiro hace menos de 30 dias push (@expiraMes,[$linea[0],$linea[7]]); } elsif ($linea[7]<$dias) { # La cuenta expiro hace mas de un mes push (@expiraAntes,[$linea[0],$linea[7]]); } else { # La cuenta no ha expirado push (@noExpirada,[$linea[0],$linea[7]]); } } close(FILE); print "\n"; print '='x37,"\n Cuentas que expiran en estos dias \n",'='x37,"\n"; &reporta(\@expiraPronto); print '='x37,"\n Cuentas que expiraron el ultimo mes \n",'='x37,"\n"; &reporta(\@expiraMes); print '='x37,"\n Cuentas que expiraron hace tiempo \n",'='x37,"\n"; &reporta(\@expiraAntes); print '='x37,"\n Cuentas que no han expirado \n",'='x37,"\n"; &reporta(\@noExpirada); print '='x37,"\n Cuentas que no expiran \n",'='x37,"\n"; &reporta(\@nuncaExpira); sub reporta { my ($login,$dias); foreach (@{$_[0]}) { ($login,$dias)=@{$_}; printf(" %-15s %6s\n",$login,$dias); } }
Es muy importante mantener verificada la integridad de nuestro sistema. Si no llevamos registro de los cambios efectuados a nuestros equipos, un atacante podrá entrar y modificar nuestro sistema sin ninguna dificultad, y será imposible determinar el alcance de los daños provocados por él.
Hay varios paquetes que hacen algo parecido a este script, el más conocido de ellos Tripwire. Todos ellos, sin embargo, pueden ser perfeccionados de más de una manera. Ilustramos aquí el funcionamiento del módulo central.
Presentamos dos versiones del programa - una que revisa únicamente los atributos de los archivos y una que calcula las firmas MD5 de cada
uno de ellos. ¿Por qué?
Aparte del punto de seguridad antes mencionado, nuestro programa debe poder listar todos los directorios a los que entre, ya que de ellos sacará la información necesaria. Si tenemos directorios protegidos con permisos 700, por ejemplo, nuestro programa no podrá entrar a este a menos que corra como el dueño del directorio.
Si decidimos no correr la versión MD5 del programa, un atacante con cierto conocimiento del sistema puede sin ningún problema alterar los
datos para cubrir sus huellas. Esta es una protección contra cambios no deseados y contra script kiddies.
El programa revisa primero que nada si fue llamado en modo de creación de la base de datos o de revisión del sistema. En el primer caso, es llamado para únicamente procesar un archivo. La operación es muy sencilla - Imprime una línea con las características que stat arroja sobre el archivo. Esta línea la podemos guardar en nuestra base de datos.
Al verificar archivos, el programa itera sobre el archivo con los datos y, si hay alguna discrepancia con el estado actual, lo reporta de
una manera fácil de leer y comprender.
[gwolf@server gwolf]$ ./atributos.pl -p /home/gwolf/archivo_prueba.txt /home/gwolf/archivo_prueba.txt|1811|246064|33188|2|1160|0|1035916|2543|973982482|973982482|8192|6 [gwolf@server gwolf]$ ./atributos.pl -p /home/gwolf/archivo_prueba.txt > database.ck [gwolf@server gwolf]$ ./atributos.pl -c database.ck [gwolf@server gwolf]$ echo 'modificando' >> archivo_prueba.txt [gwolf@server gwolf]$ ./atributos.pl -c database.ck /home/gwolf/archivo_prueba.txt: size is now 2918 (should be 2907) mtime is now 973982824 (should be 973982482) ctime is now 973982824 (should be 973982482)
En el script utilizamos el módulo Getopt::Std. Con él, las opciones de línea de comando que son especificadas con el comando
getopt('p:c:');</code son recibidas en las variables <code>$opt_p
$opt_c.
statPara conseguir los atributos de un archivo, no hay nada más útil que stat. Esta función nos regresa el siguiente arreglo:
#!/usr/bin/perl -Tw use Getopt::Std; # We use this for prettier output later in &printchanged() @statnames = qw(dev ino mode nlink uid gid rdev size mtime ctime blksize blocks); getopt('p:c:'); die "Usage: $0 [-p <filename>|-c <filename>]\n" unless ($opt_p or $opt_c); if ($opt_p) { die "Unable to stat file $opt_p: $!\n" unless (-e $opt_p); print $opt_p,"|",join('|',(lstat($opt_p))[0..7,9..12]),"\n"; exit; } if ($opt_c) { open(CFILE,$opt_c) or die "Unable to open check file $opt_c: $!\n"; while (<CFILE>){ chomp; @savedstats = split('\|'); die "Wrong number of fields in line beginning with $savedstats[0]" unless ($#savedstats == 12); @currentstats = (lstat($savedstats[0]))[0..7,9..12]; # print the changed fields only if something has changed &printchanged(\@savedstats,\@currentstats) if ("@savedstats[1..12]" ne "@currentstats"); } close(CFILE); } # iterates through attributes lists and prints any changes between # the two sub printchanged{ my ($saved,$current) = @_; # print the name of the file after popping it off of the array read # from the check file print shift @{$saved},":\n"; for (my $i = 0 ; $i < $#{$saved} ; $i++) { if ($saved->[$i] ne $current->[$i]) { print "\t".$statnames[$i]." is now ".$current->[$i]; print " (should be ".$saved->[$i].")\n"; } } }
Es muy importante mantener verificada la integridad de nuestro sistema. Si no llevamos registro de los cambios efectuados a nuestros equipos, un atacante podrá entrar y modificar nuestro sistema sin ninguna dificultad, y será imposible determinar el alcance de los daños provocados por él.
Hay varios paquetes que hacen algo parecido a este script, el más conocido de ellos Tripwire. Todos ellos, sin embargo, pueden ser perfeccionados de más de una manera. Ilustramos aquí el funcionamiento del módulo central.
Presentamos dos versiones del programa - una que revisa únicamente los atributos de los archivos y una que calcula las firmas MD5 de cada
uno de ellos. ¿Por qué?
Aparte del punto de seguridad antes mencionado, nuestro programa debe poder listar todos los directorios a los que entre, ya que de ellos sacará la información necesaria. Si tenemos directorios protegidos con permisos 700, por ejemplo, nuestro programa no podrá entrar a este a menos que corra como el dueño del directorio.
Si decidimos no correr la versión MD5 del programa, un atacante con cierto conocimiento del sistema puede sin ningún problema alterar los
datos para cubrir sus huellas. Esta es una protección contra cambios no deseados y contra script kiddies.
El programa revisa primero que nada si fue llamado en modo de creación de la base de datos o de revisión del sistema. En el primer caso, es llamado para únicamente procesar un archivo. La operación es muy sencilla - Imprime una línea con las características que stat arroja sobre el archivo. Esta línea la podemos guardar en nuestra base de datos.
Al verificar archivos, el programa itera sobre el archivo con los datos y, si hay alguna discrepancia con el estado actual, lo reporta de
una manera fácil de leer y comprender.
[gwolf@server gwolf]$ ./atributos.pl -p /home/gwolf/archivo_prueba.txt /home/gwolf/archivo_prueba.txt|1811|246064|33188|2|1160|0|1035916|2543|973982482|973982482|8192|6 [gwolf@server gwolf]$ ./atributos.pl -p /home/gwolf/archivo_prueba.txt > database.ck [gwolf@server gwolf]$ ./atributos.pl -c database.ck [gwolf@server gwolf]$ echo 'modificando' >> archivo_prueba.txt [gwolf@server gwolf]$ ./atributos.pl -c database.ck /home/gwolf/archivo_prueba.txt: size is now 2918 (should be 2907) mtime is now 973982824 (should be 973982482) ctime is now 973982824 (should be 973982482)
En el script utilizamos el módulo Getopt::Std. Con él, las opciones de línea de comando que son especificadas con el comando
getopt('p:c:');
son recibidas en las variables $opt_p y $opt_c.
statPara conseguir los atributos de un archivo, no hay nada más útil que stat. Esta función nos regresa el siguiente arreglo:
#!/usr/bin/perl -Tw use Getopt::Std; use Digest::MD5 qw(md5); # We use this for prettier output later in &printchanged() @statnames = qw(dev ino mode nlink uid gid rdev size mtime ctime blksize blocks md5); getopt('p:c:'); die "Usage: $0 [-p <filename>|-c <filename>]\n" unless ($opt_p or $opt_c); if ($opt_p) { die "Unable to stat file $opt_p: $!\n" unless (-e $opt_p); open(F,$opt_p) or die "Unable to open $opt_p: $!\n"; $digest = Digest::MD5->new->addfile(F)->hexdigest; close(F); print $opt_p,"|",join('|',(lstat($opt_p))[0..7,9..12]),"|$digest\n"; exit; } if ($opt_c) { open(CFILE,$opt_c) or die "Unable to open check file $opt_c: $!\n"; while (<CFILE>){ chomp; @savedstats = split('\|'); die "Wrong number of fields in line beginning with $savedstats[0]" unless ($#savedstats == 13); @currentstats = (lstat($savedstats[0]))[0..7,9..12]; open(F,$savedstats[0]) or die "Unable to open $opt_c: $!\n"; push(@currentstats,Digest::MD5->new->addfile(F)->hexdigest); close(F); # print the changed fields only if something has changed &printchanged(\@savedstats,\@currentstats) if ("@savedstats[1..13]" ne "@currentstats"); } close(CFILE); } # iterates through attributes lists and prints any changes between # the two sub printchanged{ my ($saved,$current) = @_; # print the name of the file after popping it off of the array read # from the check file print shift @{$saved},":\n"; for (my $i = 0 ; $i < $#{$saved} ; $i++) { if ($saved->[$i] ne $current->[$i]) { print "\t".$statnames[$i]." is now ".$current->[$i]; print " (should be ".$saved->[$i].")\n"; } } }
Es muy importante para un administrador de sistemas analizar los datos generados por las bitácoras de su sistema. De ellas podemos, entre otras cosas, sacar posibles errores que estemos cometiendo con nuestra configuración del sistema o encontrar posibles intentos de intrusión. Desafortunadamente, leer bitácoras puede ser tedioso.
Presento aquí un script que analiza la salida de last buscando cuántas veces se ha conectado un usuario y por cuánto tiempo en el último mes.
Las bitácoras del sistema típicamente seran legibles únicamente para root. En este caso decidí utilizar un programa que funciona en el espacio de usuario y nos presenta la bitácora binaria wtmp para evitar hacer otra demostración que requiera correr como superusuario.
El programa itera sobre cada línea resultante de correr el programa indicado en $lastProg, ignorando las líneas que no van asociadas a un login verdadero. Utilizando el hash %usedTime registra en una sencilla estructura cuántas veces entró un usuario, y por cuánto tiempo lo hizo. Por último, nos lo reporta utilizando un formato de printf.
En la función toMinutes primero que nada verificamos que el formato de la hora sea el correcto: Dos dígitos representando la hora y otros dos representando los minutos, separados por dos puntos. Nos es más fácil guardar un valor fácil de sumar, como lo son los minutos, por lo que multiplicamos las horas por 60 y sumamos el resultado a los minutos. Sumamos esto al valor ya existente en $usedTime{$user}[0].
Al principio del programa tenemos la directiva use integer;. Esta indica al compilador que use aritmética entera, en vez de trabajar con puntos flotantes, el comportamiento normal de Perl. Esto, además de darnos un poco de velocidad al hacer operaciones, nos permite dar menos rodeos en la función printResults. Para separar horas y minutos basta con que dividamos los minutos entre 60 - El entero resultante son las horas, el residuo los minutos, y no tendremos que pelearnos con fracciones decimales.
#!/usr/bin/perl -w use integer; use strict; my (%usedTime, $lastProg); $lastProg = '/usr/bin/last'; foreach my $line (`$lastProg`) { next if ($line =~ /^(reboot|ftp|wtmp begins)/ || $line =~ /^\s*$/); $line =~ s/\s+$//g; my $user = substr($line,0,10); $user =~ s/\s.+//g; my $time = &toMinutes(substr($line,length($line)-6,5)); $usedTime{$user} = [0,0] unless defined($usedTime{$user}); $usedTime{$user}[0] += $time; $usedTime{$user}[1] += 1; } &printResults(\%usedTime); exit 0; sub toMinutes { my ($in,$hr,$min); $in = shift; return 0 unless ($in =~ /^\d\d\:\d\d$/); $hr = substr($in,0,2); $min = substr($in,3,2); return ($hr*60+$min); } sub printResults { my ($totMin,$hr,$min,$numLogins); print "Login Time used Logins\n"; print "============================\n"; foreach my $user (sort(keys(%usedTime))) { $totMin=$usedTime{$user}[0]; $numLogins=$usedTime{$user}[1]; $hr=$totMin / 60; $min=$totMin % 60; printf("%-10s %02s:%02s %-2s\n",$user,$hr,$min,$numLogins); } }
Muchos programas analizan, de alguna u otra manera, el contenido de las bitácoras del sistema (como ejemplos, Logcheck (tutorial en español disponible aquí) y Swatch). Hay programas también dedicados a monitorear y reaccionar en tiempo real a los errores producidos por Apache, como el Apache Guardian.
Sin embargo, un programa que recorra un archivo de bitácora de Apache y reporte, de una manera limpia y fácil de entender los errores ocurridos tenía que ser ideado a mano por los administradores. Es eso lo que intento aquí cubrir.
Este script debe correr con un usuario que tenga derecho de ver el archivo de bitácoras de Apache, típicamente localizado en /var/www/logs/error_log/var/log/httpd/error_log. Para evitar que este script requiera privilegios de root para correr, sugiero que dicho archivo pertenezca a un grupo de administración (daemon o wheel, por ejemplo), permitiendo a dicho grupo acceso únicamente de lectura, de la siguiente manera:
-rw-r----- 1 root daemon 2188152 Dec 7 10:03 /var/www/logs/error_log
El script procesa una tras otra todas las líneas del archivo indicado en la variable $file. Reporta un resúmen de lo que encuentre en dicho archivo con el siguiente formato:
Directory index forbidden by rule 1 1 /var/www/htdocs/comun/ 7 /var/www/htdocs/data/Icons/ 2 /var/www/htdocs/images/ File does not exist 1 5 /var/www/htdocs/biblio/ 2 /var/www/htdocs/CURSO 1 /var/www/htdocs/adm_list/approve.cgi (...)
El formato de las bitácoras de error de Apache es el siguiente:
[<i>fecha</i>] [<i>categoría</i>] [client <i>w.x.y.z</i>] <i>tipo de error</i>: <i>archivo que lo ocasionó</i>
por ejemplo,
[Thu Dec 7 10:03:27 2000] [error] [client 192.168.2.45] File does not exist: /var/www/htdocs/esta/pagina/no/existe
Lo dividimos, entonces, utilizando el caracter ] como separador de campo. Descartamos todas las líneas que no lleven error como categoría, y de las líneas que nos interesen, el último campo lo dividimos en tipo de error y archivo que lo ocasiona utilizando el
caracter :.
En el hash %errores guardamos los datos resultantes, utilizando como llave el tipo de error, y agregando el archivo que lo ocasionó a la lista guardada por referencia relacionada a esa llave.
Al reportar, recorremos los tipos de error y, para cada uno de ellos, recorremos el arreglo, guardando ahora en el hash %sumado como llave el nombre de cada uno de los causantes de error, y sumándole uno al valor de esta llave cada que lo encontremos. Una vez procesado el arreglo completo, reportamos primero el tipo de error, y después (indentando por claridad) cada uno de los archivos que lo ocasionaron, con el número de veces que fue llamado.
#!/usr/bin/perl -Tw use integer; use strict; my (%errores, $file); $file = '/var/www/logs/error_log'; open(IN,"<$file") or die "Could not open $file - $@ $!"; foreach my $line (<IN>) { chomp($line); my (@datos,@detalles); @datos = split(/\]/,$line); next if ($datos[1] ne ' [error'); @detalles = split(/: /,$datos[3]); $errores{$detalles[0]} = [] if (not defined $errores{$detalles[0]}); my ($tipoErr,$archErr) = @detalles; push(@{$errores{$tipoErr}},$archErr); } &printResults(\%errores); sub printResults { my ($llave,%hash,%sumado); %hash = %{$_[0]}; foreach $llave (sort(keys(%hash))) { print "$llave\n"; while (@{$hash{$llave}}) { my ($tmp); $tmp=shift(@{$hash{$llave}}); $sumado{$tmp}++; } foreach my $file (sort(keys(%sumado))) { printf (" %3d %25s\n",$sumado{$file},$file); } } }
Hay varios archivos fundamentales para la operación de nuestro sistema que requieren seguir un formato específico - probablemente, el mejor ejemplo de esto sea el archivo de información de los usuarios, /etc/passwd, y los archivos íntimamente relacionados con él, /etc/groups y /etc/shells. Además de esto, no es difícil para un atacante esconder su presencia utilizando trucos muy sencillos, algunos de los cuales encontraremos con este programa.
No me cansaré de repetirlo: Si no hay una razón que nos obligue a correr como root determinado programa, no tenemos por qué hacerlo
Este script todavía puede ser muy mejorado - No porque este programa no nos reporte una situación riesgosa en estos archivos significa que están seguros. Podríamos agregar verificaciones para cuentas 0 que no pertenecieran única y exclusivamente a root, cuentas con login root que no tuvieran UID 0, cuentas de usuario con UID 65536 (equivalente a root), nombres de usuario pertenecientes a cuentas del sistema (kmem, mail, adm, wheel, etc.) y otras muchas posibles situaciones.
Primero que nada, creamos dos arreglos, uno con el contenido relevante de /etc/group y otro con el de /etc/shells. Al hacer esto, aprovechamos para verificar su correcto formato - En /etc/group revisamos que tenga el número correcto de campos y que no haya ni nombres de grupo ni GIDs repetidos. Después de esto, revisamos que cada uno de los shells listados en /etc/shells exista y sea ejecutable.
Utilizando las mismas estrategias, finalmente abrimos y analizamos línea por línea /etc/passwd. Revisamos número de campos, usernames/UIDs duplicados, directorio home existente y shell listado en /etc/shells.
#!/usr/bin/perl -Tw use strict; my ($grpFileName,$shellFileName,$pwdFileName,%groups,%shells,@pwdData); $grpFileName = '/etc/group'; $shellFileName = '/etc/shells'; $pwdFileName = '/etc/passwd'; # First, load all the needed groups into memory. Check, by the way, for # repeated groups and incorrect line length. %groups = &getGroups($grpFileName); %shells = &getShells($shellFileName); &checkPasswd($pwdFileName,\%groups,\%shells); sub getGroups { my ($filename,@groups,%groupids,%groupnames); $filename = shift; open(IN,"<$filename") or die "Could not open $filename"; while (my $line = <IN>) { # We do not chomp($line), because even an empty members field # is valid, and if we chopped it, we would get false positives. next if $line =~ /^\s*#/; my @data = split(/:/,$line); print "WARNING: Line in $filename with incorrect number of fields (should have four):\n $line\n" if ($#data != 3); print "WARNING: Duplicate GID: $data[2] refers to $data[0] and $groupids{$data[2]}\n" if (defined $groupids{$data[2]}); $groupids{$data[2]} = $data[0]; print "WARNING: Duplicate group name: $data[0] refers to $data[2] and $groupnames{$data[0]}\n" if (defined $groupnames{$data[0]}); $groupnames{$data[0]} = $data[2]; } return(%groupids); } sub getShells { my ($filename,%shells); $filename = shift; open(IN,"<$filename") or die "Could not open $filename"; while (my $line = <IN>) { next if ($line =~ /^\s*#/); chomp($line); print "WARNING: Shell $line does not exist\n" unless (-e $line); print "WARNING: Shell $line appears twice\n" if (defined $shells{$line} ); $shells{$line} = 1; } return(%shells); } sub checkPasswd { my (%userid,%username,$filename,%groups,%shells); $filename = $_[0]; %groups = %{$_[1]}; %shells = %{$_[2]}; open(IN,"<$filename"); while (my $line = <IN>) { chomp($line); next if ($line =~/^\s*#/); my @data = split(/:/,$line); print "WARNING: Line in $filename with incorrect number of fields (should have 7):\n $line\n" if ($#data != 6); print "WARNING: Duplicate UID: $data[2] refers to $data[0] and $userid{$data[2]}\n" if (defined $userid{$data[2]}); $userid{$data[2]} = $data[0]; print "WARNING: Dulpicate username: $data[0] refers to $data[2] and $username{$data[0]}\n"if (defined $username{$data[0]}); $username{$data[0]} = $data[2]; print "WARNING: Home directory non-existant for user $data[0] ($data[5])\n" unless (-e $data[5]); print "WARNING: Shell non-existant for user $data[0] ($data[6])\n" unless (-e $data[6]); } }
Todo administrador de una red grande basada en TCP/IP se topará tarde o temprano con que algún usuario que cree saber lo que hace ha modificado su dirección IP. Esto puede llevar al caos, imposibilitándonos saber qué computadora está causando choques de direcciones o dónde está determinada dirección. Este programa nos ayuda tomando fotografías de la red en determinado momento, para que podamos compararla con fotografías futuras.
El programa es invocado dándole la interfaz sobre la cual trabajar - recuerden que arp es un protocolo no ruteable. El programa obtiene la configuración de la red del comando definido en $ifconfigCmd. Para guardar los resultados abre el archivo indicado en $filename, y si este ya existe lee los datos ya existentes para no volver a buscar direcciones ya resueltas. Posteriormente, revisa todo el segmento (funciona únicamente con redes clase B o clase C).
Sugiero correr el programa a través de cron cada media hora por un par de días para encontrar también a las computadoras apagadas a la hora de la ejecución.
arpmap utiliza ampliamente la capacidad de encontrar patrones de Perl. Al llamar a ifconfig itera sobre el arreglo resultante, buscando primero que nada una línea que calce con ^[\w\d\:]+ indicando el nombre de una interfaz de red. Si no especificamos al llamar al programa, pregunta cuál de las interfaces es la que nos interesa. Después de esto, asigna la siguiente línea a $ip y a $mask, para seleccionar en ambas únicamente la porción que nos interesa, reduciéndolas a cadenas [\d\.]+
Para poder resolver una dirección IP hacia su MAC correspondiente tenemos que tenerla ya en el cache del kernel, es por ello que corremos $findCmd, que típicamente será un ping. Después de esto preguntamos por la IP utilizando $arpCmd, y buscamos en el resultado calzar con el patrón /Address.*HWtype/ (indicando una dirección encontrada) o /incomplete/ (indicando falla, usualmente por ser la dirección local). Para guardar la dirección utilizamos print a secas, dado que en la función &openFile tenemos un select(OUT), enviando toda salida estándar al archivo.
El resultado de ejecutar este script es un archivo
con un formato como el siguiente:
192.168.1.1 - 00:50:DA:66:DB:5C 192.168.1.3 - 08:00:20:AE:C9:0C 192.168.1.6 - 00:50:DA:1F:00:7C 192.168.1.7 - 00:50:DA:66:DB:60 192.168.1.8 - 00:50:DA:0D:12:F2 192.168.1.9 - 00:A0:24:16:92:CA 192.168.1.10 - 00:60:97:60:60:50 192.168.1.20 - 00:50:DA:60:9A:E2 192.168.1.22 - 00:01:02:60:3B:EA 192.168.1.25 - 00:A0:24:16:93:BD 192.168.1.29 - 00:50:04:01:3D:A7 192.168.1.31 - 00:60:08:48:C2:5B 192.168.1.32 - 00:A0:24:C3:F9:49 192.168.1.33 - 08:00:20:76:8D:98 192.168.1.38 - 00:60:08:C7:5B:FB 192.168.1.41 - 00:60:08:C7:5C:00 192.168.1.44 - 00:A0:24:16:93:65 192.168.1.45 - 00:60:08:C6:77:3F 192.168.1.47 - 00:A0:24:1B:51:79 192.168.1.49 - 00:A0:24:15:97:D5 (...)
#!/usr/bin/perl -w # # Maps the IP addresses on your local Ethernet to their ARP equivalents # # REQUIRES: Being run by any user authorized to run ping, arp and ifconfig use strict; use vars qw($ifconfigCmd $findCmd $arpCmd $interactive %done); my ($ip,$mask,$class,$filename); # ---------configuration area-------- # Path to the ifconfig binary $ifconfigCmd = '/sbin/ifconfig'; # Command to run to find a host # (a simple ping will do, just take care not to # execute an eternal ping) # # Put %ip% where the IP should be given. $findCmd = '/bin/ping -c 1 %ip%'; # Path to the arp binary $arpCmd = '/sbin/arp'; # filename for output. If the file already exists, it # will be checked, in order not to repeat the whole # process. # If left empty, output will go to STDOUT. if ($ARGV[1]) { $filename=$ARGV[1]; } else { $filename = 'arpmap.txt'; } # -----end of configuration area----- # # Run in interactive mode if called with no arguments $interactive = ($#ARGV==-1); ($ip,$mask) = &findIp(); $class=&checkMask($mask); &openFile($filename) if (defined $filename && $filename ne ''); $|=1; &getArp($ip,$class); close(OUT) if (defined $filename && $filename ne ''); exit 0; sub openFile { my ($filename); $filename=$_[0]; &readFile($filename) if (-f $filename); open (OUT,">>$filename") or die "Could not open $filename for output"; select(OUT); } sub readFile { my ($filename,@in,$line,$ip,$mac); $filename=$_[0]; open (IN,$filename) or die "Could not open $filename for input"; @in=<IN>; close (IN); while ($line=shift(@in)) { ($ip,$mac) = split(' - ',$line); $done{$ip}=$mac; } } sub findIp { my ($line,@ifconfig,%interfaces,$int,$ip,$mask); @ifconfig=`$ifconfigCmd`; while ($line = shift(@ifconfig)) { chomp $line; if ($line =~ s/^([\w\d\:]+).*/$1/ && $line ne 'lo') { $interfaces{$line} = shift(@ifconfig); chomp($interfaces{$line}); } } if ($interactive) { $int=''; while (not defined $interfaces{$int}) { print "\nThe following interfaces were found on your system:\n"; print join ("\n ",keys(%interfaces)); print "\nWhich interface do you want to map? "; $int = <STDIN>; chomp ($int); } } else { if (defined $interfaces{$ARGV[0]}) { $int = $ARGV[0]; } else { print "\nInterface $ARGV[0] not found\n\n"; exit 0 } } $ip = $mask = $interfaces{$int}; $ip =~ s/^.*inet addr:([\d\.]+).*/$1/; $mask =~ s/^.*Mask:([\d\.]+).*/$1/; return ($ip,$mask); } sub checkMask { my ($mask,@mask,$class); $mask=$_[0]; @mask = split(/./,$mask); if ($mask eq '255.255.255.0') { $class=24; } elsif ($mask eq '255.255.0.0') { $class=16; } elsif ($mask eq '255.0.0.0' || $mask eq '0.0.0.0') { print "Your network is too large - Stay out of troubles, don't try to map it.\n"; exit 0; } elsif ($mask eq '255.255.255.255') { print "This is a point-to-point network or something weird.\n"; print "I'd better cowardly stay away from it..."; exit 0; } else { print "Sorry, arpmap does not handle subclasses yet.\n"; exit 0; } return $class; } sub getArp { my ($myip,$class,$ip,$oct3,$oct4); $myip = $_[0]; $class = $_[1]; $ip = $myip; # $class must be either 16 or 24 if ($class == 24) { $ip =~ s/^(\d+\.\d+\.\d+\.).*/$1/; for ($oct4=1;$oct4<255;$oct4++) { &getIp($ip.$oct4); } } elsif ($class == 16) { $ip =~ s/^(\d+\.\d+\.).*/$1/; for ($oct3=1;$oct3<255;$oct3++) { for ($oct4=0;$oct4<=255;$oct4++) { &getIp($ip.$oct3.$oct4); } } } else { die "I was waiting for a 16 or a 24 and got a $class!"; } } sub getIp { my ($ip,$command,@arp,@arp2,$arp,$line); $ip=$_[0]; return if (defined $done{$ip}); $command = $findCmd; $command =~ s/\%ip\%/$ip/; system("$command > /dev/null"); @arp=`$arpCmd $ip`; foreach $line (@arp) { chomp $line; # Go to next line if this one is the header next if ($line =~ /Address.*HWtype/ || $line =~ /incomplete/); @arp2=split(/\s+/,$line); $arp = $arp2[2]; print "$ip - $arp\n" if ($arp !~ /--/); } }
Todos nosotros odiamos el spam. ¿Cómo consiguen nuestras direcciones los spammers? Buscándolas por Web. Cualquiera de nuestros correos aparece casi seguramente en al menos una página.
Ahora... Si tan solo pudiéramos alimentar con basura a los recolectores de direcciones para que su trabajo fuera inútil... ¡Pero podemos! Podemos darles cantidades ilimitadas de direcciones falsas... Eso es lo que hace este script.
Gracias a Jason Costomiris por el escribirlo y publicarlo en http://www.jasons.org, de donde lo bajé.
Este CGI es bastante sencillo y está programado con cuidado. Sin embargo, hay algunos puntos a considerar:
Es un script muy sencillo. Después de crear dos arreglos, uno con las 26 letras del alfabeto y uno con varios top-level domains. Genera hasta 500 direcciones consistentes de hasta ocho letras para login y hasta 10 letras mas el TLD para el dominio, presentándolas en un CGI como ligas (<a href="mailto:...">). Por último, genera una liga hacia el mismo script, para que un robot recolector de direcciones sin mucha inteligencia caiga en un ciclo infinito de recolección de direcciones.
#!/usr/bin/perl -T # Copyleft 1997 Jason Costomiris (whois:JC1011). # Rights? You had rights? When I was a kid...... require 5.003; use strict; use CGI; my ($i, $j, $k); my @abc = ('a'..'z'); my @tld = ('com', 'net', 'org', 'mil', 'gov', 'hey-dork', 'spam-sucks'); my $fodder = new CGI; print $fodder->header; print $fodder->start_html(-title=>'Spam fodder', -BGCOLOR=>'#FFFFFF'); print "<a href=\"mailto:abuse\?subject=I am a spamming luser\">abuse</a><br/>\n"; srand; my $number = int(rand(500)); for ($i = 0 ; $i <= $number ; $i++) { my $name = ""; my $domain = ""; my $num_name = int(rand(8)); for ($j = 0 ; $j <= $num_name ; $j++) { $name .= $abc[int(rand($#abc))]; } my $num_domain = int(rand(10)); for ($k = 0 ; $k <= $num_domain ; $k++){ $domain .= $abc[int(rand($#abc))]; } my $tld = $tld[int(rand($#tld))]; my $address = $name . '@' . $domain . '.' . $tld; print "<a href=\"mailto:$address\?subject=I am a spamming luser\">$address</a><br/>\n"; } # # Now that we've given lots of bad addresses to them, let's put them in an # endless loop # print "\n<pgt;\n<a href=\"$ENV{SCRIPT_NAME}\">endless loop of these pages for spam-spiders. Yay.</a>\n"; print $fodder->end_html;
Tras un ataque a nuestro sitio es muy importante analizar qué hizo el atacante - No sólo los pasos que siguió para penetrar en nuestro sistema, sino que también qué hizo una vez dentro. Muchas veces quisiéramos poder analizar qué intentos de intrusión ha habido, o frustrarlos dando al posible atacante información falsa. Para esto podemos crear sistemas trampa, que aparenten dar cierto servicio pero en realidad no lo estén dando, y sólo estén registrando todo lo que reciban. Perl es ideal para esto, dada su facilidad para la programación en red.
Hay varios servicios que son muy sencillos de emular, y que pueden no ser necesarios para nuestro sistema. Los ejemplos más clásicos son finger, SMTP, POP3 y HTTP. Claro, el juzgarlos innecesarios depende de cada sitio. Nuestro programa emulará respuestas válidas de estos servicios, registrando en syslog cualquier uso que el atacante intente hacer de ellos.
Como todos estos servicios pueden ser iniciados desde inetd.conf, no nos preocuparemos de los sockets, manejando toda entrada y salida por las vías estándar.
La interacción será manejada por el demonio inet, lo cual nos simplifica mucho la tarea: Toda entrada y salida la manejamos con STDIN y STDOUT.
STDIN puede ser manejado como un socket, y lo hacemos en la función connData, donde obtenemos la dirección y puerto de la cual procede la comunicación.
Con un sistema trampa querremos registrar todo lo que el posible atacante intente hacer. Para hacerlo, utilizamos el Syslog, utilizando el módulo Sys::Syslog que viene incluído con la distribución estándar de Perl. Inicializamos el servicio con las siguientes líneas:
use Sys::Syslog qw(:DEFAULT setlogsock); setlogsock('unix'); openlog('honeypot','cons,pid','honeypot');
logger que simplemente hace:syslog('info',join(' ',@_));
En este script están implementados emuladores de tres populares protocolos: Finger, SMTP y POP3. Finger es un protocolo suficientemente sencillo como para poder sido emulado por completo con sólo un par de líneas. Cuando recibe una solicitud sobre cualquier usuario -válido o no- responde algo que aparenta éxito, al reportar conexiones marca que no hay nadie conectado, y al solicitar una redirección la niega.
POP3 responde en un formato similar al del servidor imapd incluído en muchas distribuciones de Linux; reporta siempre éxito al recibir nombre de usuario y error al recibir la contraseña.
SMTP aparenta ser un sendmail. La implementación es muy sencilla, tal vez demasiado simple (no olviden que estos scripts son sólo ejemplos, no deben ser utilizados sin ser estudiados y modificados por el administrador): Responde exitosamente a cualquier HELO/EHLO, y marca error a cualquier MAIL FROM. Es importante responder siempre así, pues de no hacerlo un usuario podría legítimamente enviarnos un correo y nunca llegaría a su destino.
#!/usr/bin/perl -T # # This script should be run from /etc/inetd.conf as follows: # # (port) stream tcp nowait (user) (path-to-script) (script) (argument) # # for example: # finger stream tcp nowait nobody /usr/local/sbin/honeypot.pl honeypot.pl finger # pop3 stream tcp nowait nobody /usr/local/sbin/honeypot.pl honeypot.pl pop3 # # IMPORTANT NOTE # Having a honeypot system does *NOT* make it any safer. In fact, it makes # it a GREAT target for attacks. An attacker might get angry at you laughing # at him and attack with greater strength. Even if you enhance its behavior, # it is not hard to notice you are not talking to a real server. Use this # ONLY at machines DEDICATED to network monitoring and information # gathering. Most important, USE AT YOUR VERY OWN RISK. use strict; use Sys::Syslog qw(:DEFAULT setlogsock); use IO::Socket; use vars qw($hostname); # Set to the hostname you want to tell the clients $hostname = 'asdf.qwerty.org'; $|=1; setlogsock('unix'); openlog('honeypot','cons,pid','honeypot'); &connData; if ($ARGV[0] eq 'finger') { &finger; } elsif ($ARGV[0] eq 'pop3') { &pop3; } elsif ($ARGV[0] eq 'smtp') { &smtp; } else { &logger("ERROR - $ARGV[0] not defined\n"); } exit 0; sub pop3 { # emulates a POP3 connection, accepting every username and # denying every password. It honors the 'quit' command, in # case a legitimate client tries to connect and refuses to # leave unpolitely. Everything else is denied. my ($request); print "+OK POP3 $hostname v4.76 server ready\n"; while ($request=<STDIN>) { &logger("RECEIVED: '$request'"); $request =~ s/[\r\n]//g; if ($request=~/^user\b.+\w+/i) { print "+OK User name accepted, password please\n"; } elsif ($request=~/^pass\b.+\w+/i) { print "-ERR Bad login\n"; } elsif ($request=~/^quit\b/i) { print "+OK Sayonara\n"; exit 0; } else { print "-ERR Unknown AUTHORIZATION state command\n"; } } } sub smtp { # emulates a SMTP connection, denying access to all senders. # HELO/EHLO is honored. It honors the 'quit' command, in # case a legitimate client tries to connect and refuses to # leave unpolitely. Everything else is denied. my ($request); print "220 $hostname ESMTP MailServer 6.4.5\n"; while ($request = <STDIN>) { &logger("RECEIVED: '$request'"); $request =~ s/[\r\n]//g; if ($request =~ /^mail\s+from:\s?(.+)/i) { print "550 $1...Access denied\n"; } elsif ($request =~ /^helo\s+(.+)/i || $request =~ /^ehlo\s+(.+)/i) { print "250 $hostname Hello $1, pleased to meet you\n"; } elsif ($request =~ /^quit/i) { print "221 $hostname closing connection\n"; exit 0; } else { print "Command unrecognized: $request\n"; } } } sub finger { # emulates a finger connection. If a user request is recieved # (no '@' characters), it is served with a dummy template, making # each and every user searched appear as valid. If an empty request # is recieved, it shows no users logged in. If a redirect request is # recieved, it is denied right away. my ($request); $request = <STDIN>; &logger("RECEIVED: '$request'"); $request =~ s/[\r\n]//g; if ($request =~ /\@/) { # Forwarding print "fingerd: forwarding not allowed\n"; } elsif ($request =~ /^$/) { # Who is online print "Login Name Tty Idle Login Time Office Office Phone\n"; } elsif ($request =~ /^\w+$/) { # user's login information print "Login: $request".' 'x(33-length($request)). "Name:$request\nDirectory: /home/$request". ' 'x(23-length($request))."Shell: /bin/bash\nNever logged in. \nNo mail.\nNo Plan.\n"; } } sub connData { # Gets and logs connection data my ($src_conn,$src_port,$src_iaddr,$src_ip_address); $src_conn=getpeername(STDIN); ($src_port,$src_iaddr)=unpack_sockaddr_in($src_conn); $src_ip_address=inet_ntoa($src_iaddr); &logger("Connection recieved from $src_ip_address, source port $src_port for $ARGV[0]"); } sub logger { syslog('info',join(' ',@_)); }
Para evitar ataques a diferentes servicios, ProtoWrap envuelve la comunicación, permitiendo que pase únicamente lo que sea validado como correcto.
Consultar http://www.gwolf.org/seguridad/wrap
Varios de los ejemplos aquí presentados fueron tomados del libro Perl for System Administration, de David N. Blank-Edelman, publicado por O’Reilly en junio del 2000 (ISBN 1-56592-609-9).
Otras importantes fuentes de recursos para scripts aplicables a la seguridad son: