Archivos de registro
About 25% of the activity is 200-OKAbout 25% of the activity is 200-OKAbout 25% of the activity is 200-OKAbout 25% of the activity is 200-OKAproximadamente el 25% de la actividad es 200-OK
Descubrí que mi servidor web ha estado guardando algunos archivos de registro durante un año y otros durante un mes. He explorado tres tipos de archivos de registro que contienen información sobre los visitantes del servidor (hay otros archivos que parecen menos interesantes). A continuación se muestran algunos ejemplos típicos:
El autenticación.log (IP cambiadas)
Oct 23 06:02:24 server1 sshd[20172]: Failed password for invalid user admin from 192.168.1.1 port 64721 ssh2
Oct 23 06:09:01 server1 CRON[20181]: pam_unix(cron:session): session opened for user root by (uid=0)
Oct 23 06:09:01 server1 CRON[20181]: pam_unix(cron:session): session closed for user root
Oct 23 06:10:35 server1 sshd[20195]: Did not receive identification string from 192.168.1.1
Oct 23 06:10:40 server1 sshd[20196]: Invalid user admin from 192.168.1.1
Oct 23 06:10:40 server1 sshd[20196]: input_userauth_request: invalid user admin [preauth]
Oct 23 06:10:41 server1 sshd[20196]: pam_unix(sshd:auth): check pass; user unknown
Los intentos de autorización en un servidor web representan intentos de inicio de sesión, ya sea por mí, por el software que pertenece a mi servidor (CRON, arriba) u otra persona o computadora (alguien que intenta una identificación de usuario de administrador, arriba). El administrador cae en la categoría de "chicos malos".
El acceso.log (Servidor Apache) (IP cambiadas)
192.168.2.2 - - [30/Aug/2016:08:30:29 -0400] "GET / HTTP/1.1" 200 1714 "-" "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)"
192.168.1.1 - - [30/Aug/2016:08:51:52 -0400] "GET /hndUnblock.cgi HTTP/1.0" 404 496 "-" "Wget(linux)"
192.168.1.1 - - [30/Aug/2016:08:51:52 -0400] "GET /tmUnblock.cgi HTTP/1.0" 404 495 "-" "Wget(linux)"
192.168.1.1 - - [30/Aug/2016:08:51:52 -0400] "GET /cgi/common.cgi HTTP/1.0" 404 496 "-" "Wget(linux)"
192.168.1.1 - - [30/Aug/2016:08:51:53 -0400] "GET /stssys.htm HTTP/1.0" 404 492 "-" "Wget(linux)"
192.168.1.1 - - [30/Aug/2016:08:51:53 -0400] "GET / HTTP/1.0" 200 1714 "-" "Wget(linux)"
192.168.1.1 - - [30/Aug/2016:08:51:54 -0400] "POST /command.php HTTP/1.0" 404 493 "-" "Wget(linux)"
192.168.3.3 - - [30/Aug/2016:09:09:01 -0400] "GET /?feed=rss2 HTTP/1.1" 200 794 "-" "Feedly/1.0 (+http://www.feedly.com/fetcher.html; like FeedFetcher-Google)"
Las entradas del registro de acceso representan intentos de obtener una página web del servidor. La primera línea de arriba representa una solicitud válida de un rastreador web ( Baidu , un motor de búsqueda chino). La solicitud de obtención fue para / que es la página principal. El 200 significa que la página se entregó correctamente. Las siguientes seis líneas tienen un resultado 404 (no encontrado). Se trata de un hacker que intenta encontrar un servidor web mal configurado. Después de varios intentos, el software de monitoreo (Fail2Ban) notó lo que estaba sucediendo y lo bloqueó. Solía pensar que Fail2Ban era realmente importante (¡todavía lo es!), pero luego descubrí UFW, el Uncomplicated Fire Wall y una amenaza aún mayor.
Aquí esta la UFW.log :
Oct 9 04:35:21 server1 kernel: [5110701.311961] [UFW BLOCK] IN=venet0 OUT= MAC= src=175.9.9.9 DST=192.168.1.1 LEN=60 TOS=0x00 PREC=0x00 TTL=40 ID=53848 DF PROTO=TCP SPT=10651 DPT=23 WINDOW=5808 RES=0x00 SYN URGP=0
Oct 9 04:35:24 server1 kernel: [5110704.311558] [UFW BLOCK] IN=venet0 OUT= MAC= src=175.8.8.8 DST=192.168.1.1 LEN=60 TOS=0x00 PREC=0x00 TTL=40 ID=53849 DF PROTO=TCP SPT=10651 DPT=23 WINDOW=5808 RES=0x00 SYN URGP=0
Oct 9 04:35:36 server1 kernel: [5110715.912365] [UFW BLOCK] IN=venet0 OUT= MAC= src=112.7.7.7 DST=192.168.1.1 LEN=40 TOS=0x00 PREC=0x00 TTL=39 ID=32234 PROTO=TCP SPT=60061 DPT=23 WINDOW=16244 RES=0x00 SYN URGP=0
Oct 9 04:35:46 server1 kernel: [5110726.213736] [UFW BLOCK] IN=venet0 OUT= MAC= src=67.6.6.6 DST=192.168.1.1 LEN=44 TOS=0x00 PREC=0x40 TTL=239 ID=7084 PROTO=TCP SPT=57510 DPT=23 WINDOW=14600 RES=0x00 SYN URGP=0
Oct 9 04:37:02 server1 kernel: [5110802.146865] [UFW BLOCK] IN=venet0 OUT= MAC= src=139.5.5.5 DST=192.168.1.1 LEN=40 TOS=0x00 PREC=0x00 TTL=39 ID=33753 PROTO=TCP SPT=6885 DPT=23 WINDOW=9693 RES=0x00 SYN URGP=0
Oct 9 04:37:53 server1 kernel: [5110853.056320] [UFW BLOCK] IN=venet0 OUT= MAC= src=87.4.4.4 DST=192.168.1.1 LEN=60 TOS=0x00 PREC=0x00 TTL=54 ID=61204 DF PROTO=TCP SPT=47013 DPT=23 WINDOW=5808 RES=0x00 SYN URGP=0
Oct 9 04:37:56 server1 kernel: [5110856.056659] [UFW BLOCK] IN=venet0 OUT= MAC= src=87.3.3.3 DST=192.168.1.1 LEN=60 TOS=0x00 PREC=0x00 TTL=54 ID=61205 DF PROTO=TCP SPT=47013 DPT=23 WINDOW=5808 RES=0x00 SYN URGP=0
Oct 9 04:38:02 server1 kernel: [5110862.056195] [UFW BLOCK] IN=venet0 OUT= MAC= src=87.2.2.2 DST=192.168.1.1 LEN=60 TOS=0x00 PREC=0x00 TTL=54 ID=61206 DF PROTO=TCP SPT=47013 DPT=23 WINDOW=5808 RES=0x00 SYN URGP=0
Oct 9 04:39:04 server1 kernel: [5110924.610656] [UFW BLOCK] IN=venet0 OUT= MAC= src=175.1.1.1 DST=192.168.1.1 LEN=40 TOS=0x00 PREC=0x00 TTL=43 ID=16953 PROTO=TCP SPT=35941 DPT=23 WINDOW=41293 RES=0x00 SYN URGP=0
Cada línea de las líneas UFW BLOCK representa otro tipo de ataque a un servidor: buscar un puerto mal configurado. Todas estas líneas están probando DPT=23, que es Telnet , un protocolo antiguo. Observe que esto sucede al menos una vez por minuto. Todo el día. Cada día.
Cargar los datos en una tabla de datos.
Cada uno de los archivos tiene un formato diferente, es posible que las fechas no contengan el año actual, los campos pueden tener nombre=valor o simplemente ser posicionales. Moví todos los archivos del servidor Linux a mi máquina con Windows y usé FilesInDirectory para obtener una lista de nombres de archivos en JSL. Luego escribí un conjunto de patrones para analizar eficientemente los tres tipos de archivos. Las columnas de la tabla de datos fueron elegidas para representar los datos que quería capturar de cada uno de los archivos:
Columns in tableColumns in tableColumns in tableColumns in tableColumnas en la tabla
ipaddr32 es una columna de fórmula para convertir 1.2.3.4 en 1<<24 + 2<<16 + 3<<8 + 4 para un eje continuo en un gráfico. ipname se buscará más tarde. kind2 es una fórmula para reducir todos los diferentes ID de usuario a simplemente "iniciar sesión".
path = "f:/logs/";
list = Files In Directory( path, "recursive" );
global:dt = New Table( "logData",
New Column( "datetime", Numeric, "Continuous", Format( "ddMonyyyy:h:m:s", 18, 0 ), Input Format( "ddMonyyyy:h:m:s", 0 )),
New Column( "ipaddr", Character, "Nominal" ),
New Column( "kind", Character, "Nominal" ),
New Column( "detail1", Character, "Nominal" ),
New Column( "detail2", Character, "Nominal" )
);
Análisis eficiente
Para obtener la máxima velocidad con archivos grandes, el análisis JSL debe realizarse en una única llamada a la función PatMatch. Cada llamada a PatMatch tiene cierta sobrecarga, y si realiza varias llamadas, probablemente también esté cortando la cadena de entrada a medida que avanza, lo que agrega más sobrecarga. Entonces, hagámoslo con un PatMatch por archivo de registro. Las claves son dos funciones de coincidencia de patrones: PatRepeat y PatTest. PatRepeat permitirá que el patrón se repita para cada línea del archivo. PatTest permitirá que el patrón ejecute algo de JSL durante el partido (nominalmente para probar una condición, pero el resultado de nuestra prueba siempre será "verdadero"). Algunos patrones de ayuda, y luego a lo bueno...
ipPattern = patRegex("(\d{1,3}[-.]\d{1,3}[-.]\d{1,3}[-.]\d{1,3})|([a-fA-F0-9:]+)"); // approximation
ipMonth=patRegex("Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec");
datePattern1 = "[" + patregex("\d{2}")>>day + "/" + ipMonth>>month + "/" + patregex("\d{4}")>>year + ":" + patregex("\d\d:\d\d:\d\d")>>time + patregex(" [-+]\d{4}") +"]"; // [18/Sep/2016:06:14:25 -0400]
datePattern2 = ipMonth>>month + patspan(" ") + patregex("\d{1,2}")>>day + " " + patregex("\d\d:\d\d:\d\d")>>time + pattest(year="2016";1); //vs Oct 16 04:54:31 the unknown year is 2016 for these logs
quote = "\!"";
quotepattern = quote + patrepeat(patbreak(quote||"\") +(("\"+(quote|""))|"")) + quote; // allows \" escaped embedded quotation marks
los dos patrones de fecha coinciden con los dos formatos de fecha. El operador >> coloca el texto coincidente de la izquierda en la variable de la derecha. PatTest(year="2016";1) siempre devuelve verdadero después de asignar 2016 al año; esto es para los archivos de registro sin un año explícito. Utilizo una combinación de patrones y expresiones regulares para crear patrones cada vez más grandes. Aquí está el patrón de Apache access.log:
apachePat = ipPattern>>ip+" "+patbreak(" ")+" "+patbreak(" ")+" "
+datePattern1+" "
+quotePattern>>request+" "
+patbreak(" ")>>code+" "
+patbreak(" ")>>length+" "+patbreak(" ")+" "
+(quotePattern|patbreak("\!n\!r"))>>browser
+pattest(
dt1<<ADDROWS></ADDROWS>
Compare lo que esto hace con una línea del access.log
192.168.1.1 - - [30/Aug/2016:08:51:52 -0400] "GET /tmUnblock.cgi HTTP/1.0" 404 495 "-" "Wget(linux)"
y observe el patrón (...) al final; agrega una fila a la tabla dt1 y completa las columnas con los valores recién analizados.
También hay patrones de autenticación y UFW. Aquí está la función para analizar un archivo:
global:skipPattern = (patbreak("\!n\!r") + patlen(1))>> unknown + pattest(show(filename,unknown);stop();1);
global:whitespace = patregex("\s*");
parser = Function( {filename, pattern, dt1}, {Default Local},
txt = Load Text File( filename );
rc = Pat Match( //
txt, //
Pat Pos( 0 ) //
+ Pat Repeat( ((global:whitespace + pattern + global:whitespace) | global:skipPattern) + Pat Fence() ) //
+ Pat R Pos( 0 ) //
); //
);
La función carga un registro desde el nombre del archivo y usa el parámetro de patrón para analizarlo y almacena los resultados en dt1. La función PatMatch crea un patrón de alternancia que permite omitir líneas no reconocidas. PatFence() es otra optimización para archivos grandes; significa "si llegaste hasta aquí, no tiene sentido retroceder e intentar algo diferente", y puede acelerar la partida y reducir la memoria requerida. Finalmente, aquí está el controlador de la función:
While( N Items( file = Remove From( list, 1 ) ) == 1, //
file = file[1];//
If(//
!Is Missing( Regex( file, "ssl_access.log" ) ), //
parser( path || file, apachePat, global:dt );//
, // else
!Is Missing( Regex( file, "other_vhosts_access.log" ) ), //
0//parser( path || file, apachePat, global:dt );//
, // else
!Is Missing( Regex( file, "access.log" ) ), //
parser( path || file, apachePat, global:dt );//
, // else
!Is Missing( Regex( file, "ufw.log" ) ),
parser( path || file, ufwPat, global:dt );//
, // else
!Is Missing( Regex( file, "auth.log" ) ), //
parser( path || file, authPat, global:dt );//
);//
);//
Puntos de interés
La computadora Raspberry PI parece atraer muchos ataques utilizando el nombre de usuario PI. Los scripts PHP deben estar mal configurados con bastante frecuencia; Hay muchas comprobaciones para ciertos archivos "admin" de PHP. Y encontrar un puerto abierto que tenga una debilidad debe ser realmente valioso; la gran mayoría de los ataques son puertos de prueba. Si administra un servidor web y no utiliza fail2ban ni reglas de firewall (como UFW), debería considerar instalarlas y configurarlas. Puedes ralentizar un ataque de fuerza bruta permitiendo unos pocos intentos por día en lugar de varios intentos por segundo.
Dashboard view of web trafficDashboard view of web trafficDashboard view of web trafficDashboard view of web trafficVista del panel de control del tráfico web