Log files
About 25% of the activity is 200-OKCirca il 25% dell'attività è 200-OK
Ho scoperto che il mio server web conserva alcuni file di registro da un anno e altri da un mese. Ho esplorato tre tipi di file di registro che contengono informazioni sui visitatori del server (ci sono altri file che sembrano meno interessanti). Ecco alcuni esempi tipici:
L' auth.log (IP modificati)
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
I tentativi di autorizzazione su un server web rappresentano tentativi di accesso, da parte mia, del software che appartiene al mio server (CRON, sopra) o di un'altra persona o computer (qualcuno che prova un ID utente di admin, sopra). L'amministratore rientra nella categoria "Cattivi".
Access.log (server Apache) (IP modificati)
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)"
Le voci del registro di accesso rappresentano tentativi di ottenere una pagina Web dal server. La prima riga sopra rappresenta una richiesta valida da parte di un web crawler ( Baidu , un motore di ricerca cinese). La richiesta di acquisizione era per / che è la pagina principale. Il 200 significa che la pagina è stata pubblicata correttamente. Le sei righe successive hanno tutte un risultato 404 (non trovato). Questo è un hacker che cerca di trovare un server web configurato in modo errato. Dopo diversi tentativi, il software di monitoraggio (Fail2Ban) ha notato cosa stava succedendo e lo ha bloccato fuori. Pensavo che Fail2Ban fosse davvero importante (lo è ancora!), ma poi ho scoperto l'UFW, il Uncomplicated Fire Wall, e una minaccia ancora più grande.
Ecco il file 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
Ogni riga delle linee UFW BLOCK rappresenta un altro tipo di attacco a un server: ricerca di una porta mal configurata. Tutte queste linee stanno testando DPT=23, che è telnet , un protocollo antico. Nota che questo accade almeno una volta al minuto. Tutto il giorno. Ogni giorno.
Caricare i dati in una tabella dati
Ciascuno dei file è formattato in modo diverso, le date potrebbero non contenere l'anno corrente, i campi potrebbero avere nome=valore o essere semplicemente posizionali. Ho spostato tutti i file dal server Linux al mio computer Windows e ho utilizzato FilesInDirectory per ottenere un elenco di nomi di file in JSL. Quindi ho scritto una serie di modelli per analizzare in modo efficiente i tre tipi di file. Le colonne della tabella dati sono state scelte per rappresentare i dati che volevo acquisire da ciascuno dei file:
Columns in tableColonne nella tabella
ipaddr32 è una colonna formula per trasformare 1.2.3.4 in 1<<24 + 2<<16 + 3<<8 + 4 per un asse continuo su un grafico. il nome IP verrà cercato in seguito. kind2 è una formula per ridurre tutti i diversi ID utente solo in "login".
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" )
);
Analisi efficiente
Per la massima velocità con file di grandi dimensioni, l'analisi JSL deve essere eseguita in una singola chiamata di funzione PatMatch. Ogni chiamata a PatMatch comporta un sovraccarico e, se si effettuano più chiamate, probabilmente si sta anche tagliando la stringa di input man mano che si procede, il che aggiunge ulteriore sovraccarico. Quindi, facciamolo con un PatMatch per file di registro. I tasti sono due funzioni di corrispondenza dei modelli: PatRepeat e PatTest. PatRepeat consentirà la ripetizione del pattern per ogni riga nel file. PatTest lascerà che il pattern esegua un po' di JSL durante la partita (nominalmente per testare una condizione, ma il risultato del nostro test sarà sempre "vero"). Alcuni modelli di supporto e poi passiamo alle cose buone...
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
i due modelli di data corrispondono ai due formati di data. L'operatore >> inserisce il testo corrispondente a sinistra nella variabile a destra. PatTest(year="2016";1) restituisce sempre true dopo aver assegnato 2016 all'anno; questo è per i file di registro senza un anno esplicito. Utilizzo un mix'n'match di pattern ed espressioni regolari per creare pattern sempre più grandi. Ecco il modello 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>
<P>Confronta cosa fa con una riga da access.log</P>
192.168.1.1 - - [30/Aug/2016:08:51:52 -0400] "GET /tmUnblock.cgi HTTP/1.0" 404 495 "-" "Wget(linux)"
e notare il pattest(...) alla fine; aggiunge una riga alla tabella dt1 e popola le colonne con i valori appena analizzati.
Esistono anche modelli UFW e di autenticazione. Ecco la funzione per analizzare un file:
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 funzione carica un log da filename e utilizza il parametro pattern per analizzarlo e memorizza i risultati in dt1. La funzione PatMatch crea uno schema di alternanza che consente di saltare le righe non riconosciute. PatFence() è un'altra ottimizzazione per file di grandi dimensioni; significa "se sei arrivato fin qui, non ha senso fare marcia indietro e provare qualcosa di diverso", e può sia accelerare la partita che ridurre la memoria richiesta. Infine, ecco il driver per la funzione:
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 );//
);//
);//
Punti di interesse
Il computer Raspberry PI sembra attirare molti attacchi utilizzando il nome utente PI. Gli script PHP devono essere configurati in modo errato molto spesso; ci sono molti controlli per alcuni file "admin" PHP. E trovare un porto aperto che abbia un punto debole deve essere davvero prezioso; la stragrande maggioranza degli attacchi mette alla prova i porti. Se gestisci un server web e non utilizzi fail2ban e regole firewall (come UFW), dovresti considerare di installarli e configurarli. Puoi rallentare un attacco di forza bruta consentendo alcuni tentativi al giorno anziché più tentativi al secondo.
Dashboard view of web trafficVisualizzazione dashboard del traffico web