5 use Regexp::IPv6 qw($IPv6_re);
6 # ---------------------------------------------------------------------------
7 # Author: Meir Michanie (meirm@riunx.com)
8 # Co-Author: J.A.Senger (jorge@br10.com.br)
10 # ---------------------------------------------------------------------------
11 # http://www.riunx.com/
12 # ---------------------------------------------------------------------------
14 # ---------------------------------------------------------------------------
16 # ---------------------------------------------------------------------------
18 # "Ossec to Mysql" records the OSSEC HIDS alert logs in MySQL database.
19 # It can run as a daemon (ossec2mysqld.pl), recording in real-time the logs in database or
20 # as a simple script (ossec2mysql.pl).
22 # ---------------------------------------------------------------------------
24 # ---------------------------------------------------------------------------
27 # Perl DBD::mysql module
30 # ---------------------------------------------------------------------------
32 # ---------------------------------------------------------------------------
34 # 1) Create new database
35 # 2a) Run ossec2mysql.sql to create MySQL tables in your database
36 # 2b) Create BASE tables with snort tables extention
37 # 3) Create a user to access the database;
38 # 4) Copy ossec2mysql.conf to /etc/ossec2mysql.conf with 0600 permissions
39 # 3) Edit /etc/ossec2mysql.conf according to your configuration:
50 # ---------------------------------------------------------------------------
52 # ---------------------------------------------------------------------------
54 # This program is free software; you can redistribute it and/or
55 # modify it under the terms of the GNU General Public License
56 # as published by the Free Software Foundation; either version 2
57 # of the License, or (at your option) any later version.
59 # This program is distributed in the hope that it will be useful,
60 # but WITHOUT ANY WARRANTY; without even the implied warranty of
61 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
62 # GNU General Public License for more details.
64 # You should have received a copy of the GNU General Public License
65 # along with this program; if not, write to the Free Software
66 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
68 # ---------------------------------------------------------------------------
70 # ---------------------------------------------------------------------------
72 # OSSEC HIDS is an Open Source Host-based Intrusion Detection System.
73 # It performs log analysis and correlation, integrity checking,
74 # rootkit detection, time-based alerting and active response.
75 # http://www.ossec.net
77 # ---------------------------------------------------------------------------
79 # ---------------------------------------------------------------------------
81 # ---------------------------------------------------------------------------
82 $SIG{TERM} = sub { &gracefulend('TERM')};
83 $SIG{INT} = sub { &gracefulend('INT')};
85 my ($DAEMONLOGFILE)='/var/log/ossec2mysql.log';
86 my ($DAEMONLOGERRORFILE) = '/var/log/ossec2mysql.err';
87 my ($LOGGER)='ossec2mysql';
91 $conf{dbhost}='localhost';
92 $conf{database}='snort';
95 $conf{dbpasswd}='password';
98 $conf{sensor}='sensor';
99 $conf{hids_interface}='ossec';
103 my($OCT) = '(?:25[012345]|2[0-4]\d|1?\d\d?)';
105 my($IP) = $OCT . '\.' . $OCT . '\.' . $OCT . '\.' . $OCT . '\|' . $IPv6_re;
109 &help() unless @ARGV;
111 my ($hids_id,$hids,$hids_interface,$last_cid)=(undef, 'localhost', 'ossec',0);
112 my ($tempvar,$VERBOSE)=(0,0);
113 # ---------------------------------------------------------------------------
115 # ---------------------------------------------------------------------------
118 if (m/^-d$|^--daemon$/){
120 }elsif ( m/^-h$|^--help$/){
122 }elsif ( m/^-n$|^--noname$/){
124 }elsif ( m/^-v$|^--verbose$/){
126 }elsif ( m/^--interface$/){
127 $conf{hids_interface}= shift @ARGV if @ARGV; # ossec-rt/ossec-feed
128 }elsif ( m/^--sensor$/){
129 $conf{sensor}= shift @ARGV if @ARGV; # monitor
130 }elsif ( m/^--conf$/){
131 $conf{conf}= shift @ARGV if @ARGV; # localhost
133 }elsif ( m/^--dbhost$/){
134 $conf{dbhost}= shift @ARGV if @ARGV; # localhost
135 }elsif ( m/^--dbport$/){
136 $conf{dbport}= shift @ARGV if @ARGV; # localhost
137 }elsif ( m/^--dbname$/){
138 $conf{database}= shift @ARGV if @ARGV; # snort
139 }elsif ( m/^--dbuser$/){
140 $conf{dbuser}= shift @ARGV if @ARGV; # root
141 }elsif ( m/^--dbpass$/){
142 $conf{dbpasswd}= shift @ARGV if @ARGV; # monitor
146 if ($conf{dbpasswd}=~ m/^--stdin$/){
149 chomp $conf{dbpasswd};
151 $hids=$conf{sensor} if exists($conf{sensor});
152 $hids_interface=$conf{hids_interface} if exists($conf{hids_interface});
154 &daemonize() if $conf{daemonize};
155 my $dbi= ossecmysql->new(%conf) || die ("Could not connect to $conf{dbhost}:$conf{dbport}:$conf{database} as $conf{dbpasswd}\n");
158 my ($query,$numrows,$row_ref);
161 $query= 'select sid,last_cid from sensor where hostname=? and interface=?';
162 $numrows= $dbi->execute($query,$hids,$hids_interface);
164 $row_ref=$dbi->{sth}->fetchrow_hashref;
165 $hids_id=$row_ref->{sid};
166 $last_cid=$row_ref->{last_cid};
168 $query="INSERT INTO sensor ( sid , hostname , interface , filter , detail , encoding , last_cid )
170 NULL , ?, ? , NULL , ? , ?, ?
172 $numrows= $dbi->execute($query,$hids,$hids_interface,1,2,0);
173 $hids_id=$dbi->lastid();
176 &forceprintlog ("SENSOR:$hids; feed:$hids_interface; id:$hids_id; last cid:$last_cid");
181 my ($timestamp,$sec,$mail,$date,$alerthost,$alerthostip,$datasource,$rule,$level,$description,
182 $srcip,$dstip,$user,$text)=();
185 ########################################################
186 my $datepath=`date "+%Y/%b/ossec-alerts-%d.log"`;
187 my $LOG='/var/ossec/logs/alerts/'. $datepath;
189 &taillog($last_cid,$LOG);
190 ################################################################
200 my ($last_cid,$LOG)=@_;
204 next unless $timestamp;
205 $alerthostip=$alerthost if $alerthost=~ m/^$IP$/;
208 $resolv{$alerthost}=$dstip;
210 if (exists $resolv{$alerthost}){
211 $dstip=$resolv{$alerthost};
213 if ($conf{'resolve'}){
214 $dstip=`host $alerthost 2>/dev/null | grep 'has address\|has IPv6 address' `;
215 if ($dstip =~m/($IP)/ ){
223 $resolv{$alerthost}=$dstip;
228 $last_cid= &prepair2basedata(
245 ($timestamp,$sec,$mail,$date,$alerthost,$alerthostip,$datasource,$rule,$level,$description,
246 $srcip,$dstip,$user,$text)=();
249 if (m/^\*\* Alert ([0-9]+).([0-9]+):(.*)$/){
251 if ( $timestamp == $lasttimestamp){
255 $lasttimestamp=$timestamp;
259 $mail=$mail ? $mail : 'nomail';
260 #2006 Aug 29 17:19:52 firewall -> /var/log/messages
261 #2006 Aug 30 11:52:14 192.168.0.45->/var/log/secure
262 #2006 Sep 12 11:12:16 92382-Snort1 -> 172.16.176.132
264 }elsif ( m/^([0-9]+\s\w+\s[0-9]+\s[0-9]+:[0-9]+:[0-9]+)\s+(\S+)\s*->(.*)$/){
268 if ($datasource=~ m/($IP)/){
270 $datasource="remoted";
274 #2006 Aug 29 17:33:31 (recepcao) 10.0.3.154 -> syscheck
275 }elsif ( m/^([0-9]+\s\w+\s[0-9]+\s[0-9]+:[0-9]+:[0-9]+)\s+\((.*?)\)\s+(\S+)\s*->(.*)$/){
280 }elsif ( m/^([0-9]+\s\w+\s[0-9]+\s[0-9]+:[0-9]+:[0-9]+)\s(.*?)$/){
282 $alerthost='localhost';
284 }elsif ( m/Rule: ([0-9]+) \(level ([0-9]+)\) -> (.*)$/ ){
288 }elsif ( m/Src IP:/){
289 if ( m/Src IP: (\S+)/){
294 }elsif ( m/User: (.*)$/){
301 } # End while read line
305 sub prepair2basedata(){
323 my ($count,$query,$row_ref,$sig_id);
326 # Get/Set signature id
327 $query = "SELECT sig_id FROM signature where sig_name=? and sig_class_id=? and sig_priority=? and sig_rev=? and sig_sid=? and sig_gid is NULL";
328 $dbi->execute($query,$description,1,$level,0,$rule);
329 $count=$dbi->{sth}->rows;
331 $row_ref=$dbi->{sth}->fetchrow_hashref;
332 $sig_id=$row_ref->{sig_id};
333 &printlog ("REUSING SIGNATURE\n");
335 $query="INSERT INTO signature ( sig_id , sig_name , sig_class_id , sig_priority , sig_rev , sig_sid , sig_gid )
337 NULL ,?, ? , ? , ? , ?, NULL
339 $dbi->execute($query,$description,1,$level,0,$rule);
340 $sig_id = $dbi->lastid();
343 &printlog ("SIGNATURE: $sig_id\n");
347 $query="INSERT INTO event ( sid , cid , signature , timestamp )
352 $dbi->execute($query,$hids_id,$last_cid,$sig_id,&fixdate2base($date));
354 &printlog ("EVENT: ($query,$hids_id,$last_cid,$sig_id,&fixdate2base($date)\n");
359 $query=" INSERT INTO acid_event ( sid , cid , signature , sig_name , sig_class_id , sig_priority , timestamp , ip_src , ip_dst , ip_proto , layer4_sport , layer4_dport )
361 ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ?, ?
363 $dbi->execute($query,$hids_id,$last_cid,$sig_id,$description,1,$level,&fixdate2base($date),$srcip,$dstip,undef,undef,undef);
364 &printlog ("ACID_EVENT: ($query,$hids_id,$last_cid,$sig_id,$description,1,$level,&fixdate2base($date),$srcip,$dstip,undef,undef)\n");
371 $text = "** Alert $timestamp.$sec:\t$mail\n$date $alerthost -> $datasource\nRule: $rule (level $level) -> $description\nSrc IP: ($srcip)\nUser: $user\n$text";
372 $query=" INSERT INTO data ( sid , cid , data_payload )
375 $dbi->execute($query,$hids_id,$last_cid,$text);
376 &printlog ("DATA: ($query,$hids_id,$last_cid,$text)\n");
380 $query="UPDATE sensor SET last_cid=? where sid=? limit 1";
381 $numrows= $dbi->execute($query,$last_cid,$hids_id);
389 $date=~ s/ Jan /-01-/;
390 $date=~ s/ Feb /-02-/;
391 $date=~ s/ Mar /-03-/;
392 $date=~ s/ Apr /-04-/;
393 $date=~ s/ May /-05-/;
394 $date=~ s/ Jun /-06-/;
395 $date=~ s/ Jul /-07-/;
396 $date=~ s/ Aug /-08-/;
397 $date=~ s/ Sep /-09-/;
398 $date=~ s/ Oct /-10-/;
399 $date=~ s/ Nov /-11-/;
400 $date=~ s/ Dec /-12-/;
405 print "OSSEC report tool $VERSION\n";
406 print "Licensed under GPL\n";
407 print "Contributor Meir Michanie\n";
412 print "This tool helps you import into base the alerts generated by ossec."
413 . " More info in the doc directory .\n";
415 print "$0 [-h|--help] # This text you read now\n";
417 print "\t--dbhost <hostname>\n";
418 print "\t--dbname <database>\n";
419 print "\t--dbport <[0-9]+>\n";
420 print "\t--dbpass <dbpasswd>\n";
421 print "\t--dbuser <dbuser>\n";
422 print "\t-d|--daemonize\n";
423 print "\t-n|--noname\n";
424 print "\t-v|--verbose\n";
425 print "\t--conf <ossec2based-config>\n";
426 print "\t--sensor <sensor-name>\n";
427 print "\t--interface <ifname>\n";
434 chdir '/' or die "Can't chdir to /: $!";
435 open STDIN, '/dev/null' or die "Can't read /dev/null: $!";
436 open STDOUT, ">>$DAEMONLOGFILE"
437 or die "Can't write to $DAEMONLOGFILE: $!";
438 defined(my $pid = fork) or die "Can't fork: $!";
440 open (PIDFILE , ">/var/run/ossec2base2.pid") ;
441 print PIDFILE "$pid\n";
445 setsid or die "Can't start a new session: $!";
446 open STDERR, ">>$DAEMONLOGERRORFILE" or die "Can't write to $DAEMONLOGERRORFILE: $!";
451 &forceprintlog ("Terminating upon signal $signal");
452 &forceprintlog ("Daemon halted");
459 return unless $VERBOSE;
461 foreach my $line(@lines){
463 my ($date)=scalar localtime;
464 $date=~ s/^\S+\s+(\S+.*\s[0-9]{1,2}:[0-9]{1,2}:[0-9]{1,2}).*$/$1/;
465 print "$date $LOGGER: $line\n";
472 my $conf=$hash_ref->{conf};
473 unless (-f $conf) { &printlog ("ERROR: I can't find config file $conf"); exit 1;}
474 unless (open ( CONF , "$conf")){ &printlog ("ERROR: I can't open file $conf");exit 1;}
477 if ( m/^(\S+)\s?=\s?(.*?)$/) {
478 $hash_ref->{$1} = $2;