Perl - Analizzare un file di log ed estrarne gli indirizzi IP con Perl
June 9, 2021
Guida
In questa guida vedremo come possiamo usare uno dei linguaggio più efficienti e semplici che io conosca.
Questo linguaggio è Perl e personalmente lo uso come linguaggio da uno script e via vista la semplicissima sintassi(decisamente intuitiva) e visto che è davvero veloce e leggero nelle operazioni.
File necessari
Utilizzerò un file di log pubblico accessibile cliccando qui che, come potete leggere, rappresenta un file di log di OpenSSH.
Ovviamente bisogna avere perl sul proprio computer e per questo esistono diverse guide sull’internet su come installarlo.
Competenze necessarie
Non sono richieste grandi competenze di programmazione vista la semplicità di utilizzo ma se non hai mai visto il linguaggio potresti non capire bene l’utilizzo di alcune variabili e costrutti.
E' fortemente consigliato studiare le regex, a proposito di questo vi lascio il link di Regex101 che è un ottimo sito sul quale allenarsi.
Creazione dello script
Potete scaricare lo script direttamente dalla Repository creata appositamente, il file che vi serve è script.pl
Acquisizione del file di log
Perl:
#!/usr/bin/perl
die "Assicurati di aver seguito correttamente la sintassi richiesta\n\nperl script.pl p=<file_path>\n" if($#ARGV < 0 or $#ARGV > 0);
@arg = split("p=", shift);
$path = $arg[1];
- #!/usr/bin/perl Necessario per un corretto funzionamento dello script
- die “Assicurati di aver seguito correttamente la sintassi richiesta\n\nperl script.pl p=<file_path>\n” if($#ARGV < 0 or $#ARGV > 0); Indicare il path del file da voler analizzare tramite una sintassi ben precisa _perl script.pl p=<file_path> _Se non conosci il comando **die **ti lascio questo link per approfondire l’argomento
- **[USER=71873]@arg[/USER] = split(“p=”, shift); **Prendi tutto il comando e dividi laddove rilevi “p=” posizionando le due parti in @ arg ovvero un array
- **$path = $arg[1]; **Prendi la seconda posizione, quindi $arg[1] visto che mettiamo il path a destra da p=
@log = qx{cat $path};
- Leggiamo il file di log salvando ogni riga in una determinata cella dell’array log
Gestione del file e rilevamento degli attacchi
Perl:
@attacchi;
for(@log){
if(m/(POSSIBLE BREAK-IN ATTEMPT)/){
push(@attacchi, $_);
}
}
- @attacchi; Creo l’array attacchi
- for(@log){ Itero l’array log che contiene tutte le righe del file log | Ogni iterazione = 1 riga
- if(m/(POSSIBLE BREAK-IN ATTEMPT)/){ Verifichiamo in ogni riga se riscontriamo la notifica di tentato attacco, ovvero POSSIBLE BREAK-IN ATTEMPT! Per approfondire l’utilizzo di m// ovvero delle Regex in Perl ti lascio questo articolo dove viene spiegato molto bene
- push(@attacchi, $_); Banalmente aggiunge all’array @ attacchi l’elemento relativo all’iterazione, se sono alla quindicesima iterazione aggiungerà tutta la quindicesima riga
Perl:
@tutti_gli_ip;
for(@attacchi){
$_ =~ m/\[(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\]/;
push(@tutti_gli_ip,"$1\n");
}
- @tutti_gli_ip; Creo l’array @tutti_gli_ip che conterrà tutti gli ip rilevabili dai tentativi di attacco, quindi anche i duplicati
- for(@attacchi){ Itero tutte le righe che sono state rilevate come attacchi, solito discorso n-esima iterazione n-esima riga rilevata malevola
- **$_ =~ m/\[(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\]/;** Sull’n-esima riga verifico se c’è un _match_ con la regex che vedete, visto che nel file di log l’indirizzo IP viene indicato tra due parentesi quadre. Come prima, vi consiglio di allenarvi su Regex101, questa è un esempio di riga completa
*Dec 10 06:55:46 LabSZ sshd[24200]: reverse mapping checking getaddrinfo for ns.marryaldkfaczcz.com [173.234.31.186] failed - POSSIBLE BREAK-IN ATTEMPT!* copiate incollate e fate i test necessari :) - push(@tutti_gli_ip,"$1\n"); Banalmente aggiungo all’array @ tutti_gli_ip l’indirizzo IP rilevato dal match con la Regex
Eliminazione dei duplicati
Perl:
%tutti = ();
for (@tutti_gli_ip) {
$tutti{$_}++;
}
@ip = keys %tutti;
- %tutti = (); Creo un hash che avrà determinati valori in base ad una chiave che va indicata tra parentesi graffe, se non hai mai visto una cosa del genere ti consiglio di approfondire l’argomento cliccando qui
- for (@tutti_gli_ip) { Itero l’array **@ tutti_gli_ip **
- $tutti{$_}++; Il valore $_ rappresenta l’n-esimo ip dell’n-esima iterazione del for, se non esiste una chiave nell’hash **%tutti **allora creane una e somma 1, se non ha alcun valore parti da 0 => Tutti gli ip, una volta rilevati, avranno valore 1, se vengono rilevati 3 volte avranno valore 3 ma come vedremo a noi questo non c’interessa
- @ip = keys %tutti; Prendi tutte le chiavi dell’hash %tutti e assegnale all’array **@ ip **=> ogni cella dell’array avrà un ip unico (perché la chiave è unica) fra gli ip rilevati malevoli
Scrittura su file
Se non sai come avviene la gestione dei file in Perl allora ti consiglio di passare prima da questo sito
Perl:
open(BLACKLIST,">","blacklist") or die $!;
print BLACKLIST @ip;
close BLACKLIST;
- **open(BLACKLIST,">",“blacklist”) or die $!; **Apri in modalità sovrascrittura il file blacklist(stessa cartella dello script) se viene rilevato un errore stampalo in output e termina l’esecuzione
- **print BLACKLIST @ip; **Stampa in BLACKLIST tutto il contenuto dell’array **@ ip **se nella creazione dell’array ip non avessi messo \n tutti gli ip sarebbero attaccati e non si capirebbe nulla
- **close BLACKLIST; **MOLTO IMPORTANTE <= DOVETE CHIUDERE UN FILE DOPO AVERLO USATO => MOLTO IMPORTANTE
[/REPLYTHANKS]
Fine
Una volta fatto ciò vi ritroverete un file con tutti gli indirizzi ip che hanno provato a effettuare un attacco.
Extra - Raccolta dati whois
Ho voluto aggiungere una funzionalità secondo me molto interessante, ovvero l’utilizzo del comando whois per recuperare i dati che vogliamo senza dover fare poi una ricerca manuale.
Questa cosa dovrebbe far capire chi non è avvezzo a questo programma la semplicissima dinamicità nella stesura del codice.
Codice
Perl:
@analisi;
for(@ip){
$tmp_ip = $_;
@whois = qx{whois $tmp_ip};
push(@analisi, "#####################################\n");
push(@analisi, "# IP Rilevato: $tmp_ip");
for(@whois){
if($_ =~ m/(inetnum)/ or $_ =~ m/(netname)/){
push(@analisi, "[NET] $_");
}
elsif($_ =~ m/(descr)/ or $_ =~ m/(organisation)/){
push(@analisi, "[DESC] $_");
}
elsif($_ =~ m/(address)/ or $_ =~ m/(country)/){
push(@analisi, "[GEO] $_");
}
elsif($_ =~ m/(mail)/ or $_ =~ m/(phone)/ or $_ =~ m/()/){
push(@analisi, "[DATA] $_");
}
}
}
# Salvo gli ip in un file blacklist_data
open(BLACKLIST,">","blacklist_data") or die $!;
print BLACKLIST @analisi;
close BLACKLIST;
- @analisi; Creo l’array che manterrà tutte le info che vogliamo
- for(@ip){ Itero tutti gli IP precedentemente rilevati
- $tmp_ip = $_; Salvo l’IP corrente in un altra variabile per maggiore chiarezza
- @whois = qx{whois $tmp_ip}; Eseguo il comando whois sull'IP corrente
- push(@analisi, “#####################################\n”); Estetica dell’output
- push(@analisi, “# IP Rilevato: $tmp_ip”); Estetica dell’output
- **for(@whois){**Itero tutto l’output del comando whois
- **if($_ =~ m/(inetnum)/ or $_ =~ m/(netname)/){** Da modificare a piacimento in base a ciò che si cerca _-vedi sotto -_
- push(@analisi, “[NET] $_");
- **elsif($_ =~ m/(descr)/ or $_ =~ m/(organisation)/){**
- push(@analisi, “[DESC] $_");
- **elsif($_ =~ m/(address)/ or $_ =~ m/(country)/){**
- push(@analisi, “[GEO] $_");
- **elsif($_ =~ m/(mail)/ or $_ =~ m/(phone)/ or $_ =~ m/()/){**
- push(@analisi, “[DATA] $_");
- open(BLACKLIST,">”,“blacklist_data”) or die $!; Salvo l’output in un nuovo file blacklist_data nella solita modalità
- print BLACKLIST @analisi;
- close BLACKLIST;
Dati da salvare
Se lanciate un determinato comando come whois $ip avrete un output molto lungo dove ogni riga è anticipata da una parola indicativa allora ammesso che vogliate salvarvi l’address rilevato sapete che avrete qualcosa del genere
address: fu xing men nei da jie 97, Xicheng District
allora, andando ad itirare riga per riga tutto l’output, possiamo verificare se matcha con la parola address, se abbiamo una risposta positiva allora ci salviamo quella riga.
Ovviamente i dati rappresentano cose differenti tra loro quindi per creare qualcosa di professionale dovreste sezionare meglio l’output a seconda di cosa vi serve.
Se non volete cercare organisation allora togliete quel match e così via oppure se volete usare un altro comando tipo nslookup o cose del genere vi basterà studiare l’output… dovete giocarci praticamente…
Esempio di output
Extra - Settare il Firewall per droppare le richieste da IP blacklistati
Avendolo trattato in un commento lascio solo il link -> Clicca qui <-
Conclusione
Questa è, come sempre, una base sulla quale studiare.
Non andate a bannare i primi ip che vengono rilevati perché può capitare che il realtà non era un attacco ma magari è stato solo rilevato negativamente.
Ho preferito sovrascrivere al file e non aggiungere perché questa modalità di scripting la vedo come “C’è un problema -> Lo rilevo -> Lo risolvo -> Butto il codice”.
Detto ciò buon lavoro e buona programmazione :)
Link Utili
[Extra] Sito Ufficiale Perl
[File] File di log usato
[File] Repository
[Tool] Regex101
[Studio] Regex
[Studio] die
[Studio] hash
[Studio] Tipologia di attacco rilevata e gestita nella guida