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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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).
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.
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.
Presented on March 30, 2000, at CECEC 2000, ESIME Culuhacan
The full text for the lecture I gave for the GASU seminar organized by Computer Security
Department at UNAM in july, 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.
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.
Tutorial I presented at Computer Security conference 2000, giving many examples of small Perl scripts that can help improve security at a site.
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.