Archives enero 2014

Muy breve: ¿Cómo averiguar los N procesos que más CPU consumen?

Este sencillo guión nos lo permite

#!/bin/bash
[ $# -lt 2 ] || [ $# -gt 2 ] && echo "Uso: consumo.sh -c N " && exit 1
[ "$1" != "-c" ] && echo "Uso: consumo.sh -c N " && exit 2
[[ "$2" =~ ^[0-9]+$ ]] && ps e -o pcpu,state,args --sort pcpu|tail -n $2 && exit 0
echo "Uso: consumo.sh -c N , con 'N' número entero" 
exit 3

Cada línea significa:

  1. Comprobamos que se indican 2 argumentos. Si no salimos.
  2. Que el primero es ‘-c’. Si no salimos.
  3. Que el segundo es un número (para ello utilizamos la expresión regular ^[0-9]+$ y el comando [[ ]]) y, en este caso, ejecutamos la orden ‘ps’ ordenada por consumo de CPU y nos quedamos con el número de salidas indicadas en $2 y terminamos sin error.
  4. Se notifica que el segundo argumento debe ser un entero y se termina con el error 3.

Pregunta: ¿Por qué es necesario en la expresión regular ^[0-9]+$, poner ^ al principio y $ al final? ¿Es necesario?

Referencias:

Expresiones regulares: http://www.regular-expressions.info/


nmap y python. Un ejemplo

Para realizar pruebas sobre una red o equipo -casi- todos pensamos, en seguida, en nmap. Muchas veces queremos automatizar ciertas tareas y cuando interviene nmap, con el módulo python-nmap, podemos programarlas con este maravilloso lenguaje. Si, por ejemplo, necesitamos saber si un equipo tiene abierto un puerto determinado, si está «vivo»,… podemos fácilmente usar la funcionalidad de nmap e incluirla en nuestro programa sin necesidad de hacer invocaciones de comandos del sistema.

Esta librería nos proporciona varias clases que implementan la funcionalidad, síncrona y asíncrona, de nmap. PortScanner es una de ellas y es la que se utiliza en el sencillo guión que se muestra como ejemplo y que permite comprobar el estado de uno o varios puertos indicados por parámetro. Antes de que se me olvide, en esta página podéis encontrar información.

#!/usr/bin/python
try:
    import optparse, sys, re, socket
except:
    print("Error executing 'import optparse, sys, re, socket' command")
    exit(1)
try:
    import nmap
except:
    print "Error importing nmap module. It is possible you need running 'apt-get install python-nmap'"
    exit(1)
def checkIp(ip):
    if (re.match("^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$",ip)):
        host = ip
    elif re.match(r'^[a-zA-Z0-9_]{,63}(.[a-zA-Z0-9_]{,63}){3}$',ip): #("^W+(.W)$", ip):
        host = socket.gethostbyname(ip)
    else:
        host=""
        print "Name/IP host wrong. You have to write a FQDN name or a IP direction"
        exit(3)
    return host
def main():
    parser = optparse.OptionParser("usage%prog " + "-H <target host> -p <target port> + -N <target_network>")
    parser.add_option('-H', dest = 'host', type = 'string', help = 'Please, specify the target host')
    parser.add_option('-P', dest = 'protocol', type = 'string', help = 'Please, specify the transport protocol: tcp or udp')
    parser.add_option('-p', dest = 'ports', type = 'string', help = 'Please, specify the target port(s) separated by comma')
    (options, args) = parser.parse_args()
    if (options.host == None): 
        print '[-] You must specify a target host or a target network.'
        exit(0)
    if (options.ports == None): 
        print '[-] You must specify a target port(s).'
        exit(0)
    if ((options.protocol == None) or (options.protocol != "tcp" and options.protocol != "udp")): 
        print '[-] You must specify the transport protocol: tcp or udp.'
        exit(0)
    protocol = options.protocol
    host=checkIp(options.host)    
    ports = options.ports.split(',')
    scanner=nmap.PortScanner() 
    for port in ports:
        try:
            print('Analizing %s, %s/%d' % (host, protocol,(int(port))))
            if (protocol != "tcp"):
                scanner.scan(host, port, '-sU')
            else:
                scanner.scan(host, port)
            print " [+] "+ host + "(" + scanner[host].state() + ") " + protocol +"/" + port + "->" + scanner[host][protocol][int(port)]['state']
        except nmap.PortScannerError:
            print('Nmap ERROR: %s ' % sys.exc_info()[0])
            exit(2)
        except:
            print("Unexpected ERROR: %s" % sys.exc_info()[0])
            exit(3)
        
if __name__ == "__main__":
    main()

Lo que hace el guión es, tras el formateo de los parámetros de entrada:

  1. Se comprueba si con el parámetro -H se ha especificado una dirección IP válida (regexp: «^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$»),  o un nombre de dominio completamente cualificado (regexp: r’^[a-zA-Z0-9_]{,63}(.[a-zA-Z0-9_]{,63}){3}$’,ip): #(«^W+(.W)$») y, en ese caso, se resuelve por su ip (socket.gethostbyname(options.host)
  2. Declaramos la clase y se lanza la comprobación, en función del protocolo de transporte. Si es UDP, los permisos deben ser de root (no lo comprueba el guión –> mejora)
  3. Se muestra el resultado para todos los puertos indicados (opción -p), del protocolo definido (-P) y del host deseado (-H).

Deshabilitar la carga de ficheros en MySQL

Un posible ataque que se puede realizar contra cualquier aplicación web y, por extensión, contra el gestor de base de datos y el sistema donde este corre, son las SQLi. Sí, al sistema operativo, también. ¿Cómo? Pues, aprovechando una SQLi descubierta, intentar descargar ficheros críticos del sistema. Qué ficheros se podrá cargar dependerá del usuario con el que se esté ejecutando el demonio del gestor de base de datos. Es decir, podrá descargar aquellos para los que tenga permisos de lectura (y /etc/passwd lo puede leer cualquier usuario, ¡recuérdalo!)

Para evitar la fuga de información crítica de nuestro sistema, podemos:

  1. Como buena práctica recomendada para todos los servicios, ejecutar el demonio con un usuario con los mínimos permisos necesarios.
  2. Otra buena práctica recomendada: «enjaular el servicio» Con chroot impedir el acceso al sistema de archivos de nuestro sistema.
  3. Y el caso que nos ocupa: Dehabilitar la carga del ficheros en el gestor. Para el caso del MySQL en /etc/mysql/my.cnf, escribiremos

[mysqld]

local-infile=0

secure-file-priv=/dev/null

Si queremos hacerlo solo para determinados usuarios (los que usan nuestra aplicación web para conectarse a la base de datos son unos excelentes candidatos 😉 ), basta con revocar el privilegio file_priv a esos usuarios:

REVOKE priv_type [(column_list)] [, priv_type [(column_list)]] ...
    ON [object_type] {tbl_name | * | *.* | db_name.*}
    FROM user [, user] ...
FLUSH PRIVILEGES;

Referencias


Centralizar los logs en un servidor dedicado sólo a almacenar nuestros eventos

Es una buena práctica es centralizar los eventos de nuestros sistemas en un servidor cuya única función sea recoger los logs de nuestros sistemas, clasificarlos y almacenarlos (y por tanto, otra excelente buena práctica es que en ese servidor solo ofrezcamos un servicio: el de recogida de eventos). Algunos argumentos para defender esta opinión son:

  • Mejora la seguridad
  • Permite centralizar los análisis de los eventos
  • Puede actuar como «copia de seguridad»
  • ….

Una buena opción en GNU/Linux es el uso de rsyslog cuya configuración es sencilla:

Leer más


Las cookies nos permiten ofrecer nuestros servicios. Al utilizar nuestros servicios, aceptas el uso que hacemos de las cookies. Más información.