Merge pull request #56 from wuruilong01/master
[prads.git] / tools / prads2snort
blob44c8453d796153b02d0d9c8ca54c004722e21029
1 #!/usr/bin/perl -w
3 # This file is a part of PRADS.
5 # Copyright (C) 2010, Edward Fjellskål <edwardfjellskaal@gmail.com>
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 use strict;
23 use warnings;
25 #use Getopt::Long qw(:config no_ignore_case bundling);
26 use Getopt::Long qw/:config auto_version auto_help/;
27 use XML::Writer;
28 use IO::File;
29 use Switch;
31 =head1 NAME
33 prads2snort.pl - Some one needs to populate the host_attribute.xml file!
35 =head1 VERSION
37 0.1
39 =head1 SYNOPSIS
41 $ prads2snort.pl [options]
43 OPTIONS:
45 -i|--infile : file to feed prads2snort.pl
46 -o|--outfile : file to write host_attribute data to (host_attribute.xml)
47 -d|--default : set Default OS if unknown (linux,bsd,macos,windows)
48 -s|--skip : Skip OSes that has a match less than XX %
49 -v|--verbose : prints out OS, frag, stream and confidence of asset
50 -h|--help : this help message
51 --version : show prads2snort.pl version
53 =cut
55 our $VERSION = 0.11;
56 our $DEBUG = 0;
57 our $VERBOSE = 0;
58 our $SKIPOS = 0;
59 our $FORCE = 0;
60 our $VERS = 0;
61 our $INFILE = qq(/var/log/prads-asset.log);
62 our $OUTFILE = qq(hosts_attribute.xml);
63 our $DEFAULTOS = qq(linux);
64 our %ASSETDB;
65 our %STATS;
67 Getopt::Long::GetOptions(
68 'infile|i=s' => \$INFILE,
69 'outfile|o=s' => \$OUTFILE,
70 'default|d=s' => \$DEFAULTOS,
71 'skip|s=s' => \$SKIPOS,
72 'verbose|v' => \$VERBOSE,
73 'force|f' => \$FORCE,
74 'version' => \$VERS,
77 our $assetcnt = 0;
78 our $knowns = 0;
79 our $unknowns = 0;
80 our $aconfedence = 0;
82 print_header();
83 parse_asset_file();
84 make_attribute_table();
85 print_footer();
86 exit 0;
88 ################################################################################
89 ############# F - U - N - C - T - I - O - N - S - ##############################
90 ################################################################################
92 =head1 FUNCTIONS
94 =head2 parse_asset_file
96 Opens the asset file, parses it, and stor info in a hash
98 =cut
100 sub parse_asset_file {
101 # Open prads asset file
102 open (ASSETFILE, "<$INFILE") or die "[!] ERROR: Unable to open file: $INFILE - $!\n";
104 print "\n*** PARSING OF ASSET FILE ***\n\n" if $DEBUG;
105 while (<ASSETFILE>) {
106 chomp;
107 next if (/^asset,vlan,port,proto/ || /^#/);
109 R_REPORT: {
110 # asset,vlan,port,proto,service,[service-info],distance,discovered
111 /^([\w\.:]+),([\d]{1,4}),([\d]{1,5}),([\d]{1,3}),(\S+?),\[(.*)\],([\d]{1,3}),(\d{10})/ && do {
112 my ($sip, $vlan, $sport, $proto, $service, $s_info, $distance, $discovered) = ($1, $2, $3, $4, $5, $6, $7, $8);
114 my $asset=$_;
115 my $os = my $details = my $services = "unknown";
116 print "[*] $asset\n" if $DEBUG;
118 if ( $service =~ /SYN/ || $service =~ /^ACK$/ || $service =~ /^RST$/ || $service =~ /^FIN$/ ) {
119 if ($s_info =~ /:[\d]{2,4}:\d:.*:.*:.*:(\w+):(.*):link/) {
120 $os = $1;
121 $details = $2;
122 print "[**] SYN(+ACK):$os - $details\n" if $DEBUG;
123 } elsif ($s_info =~ /:[\d]{2,4}:\d:.*:.*:.*:(\w+):(.*):uptime/) {
124 $os = $1;
125 $details = $2;
126 print "[**] RST/ACK/FIN:$os - $details\n" if $DEBUG;
127 } else {
128 switch ($s_info) {
129 case /:Linux:/ {
130 $os = "Linux";
131 $s_info =~ /:Linux:(.*):?/;
132 $details = $1 if defined $1;
134 case /:Windows:/ {
135 $os = "Windows";
136 $s_info =~ /:Windows:(.*):?/;
137 $details = $1 if defined $1;
139 case /:\w+BSD:/ {
140 $s_info =~ /:(\w+BSD):(.*):?/;
141 $os = $1 if defined $1;
142 $details = $2 if defined $2;
144 case /:MacOS:/ {
145 $os = "MacOS";
146 $s_info =~ /:MacOS:(.*):?/;
147 $details = $1 if defined $1;
150 print "[**] FALLBACK: $s_info\n" if $DEBUG;
151 print "[**] FALLBACK: $os - $details\n" if $DEBUG;
153 } elsif ( $service =~ /SERVER/ || $service =~ /CLIENT/ ) {
154 $s_info =~ s/^(\w+):(.*)$/$2/;
155 $services = $1;
157 # Assign this line to the asset data structure.
158 if ($proto == 6) {
159 # TCP
160 if ( $service =~ /SERVER/ ) {
161 push (@{$ASSETDB{$sip}{"TCPSER"}}, [ $sport, $service, $services, $s_info, $discovered ]);
162 } elsif ($service =~ /CLIENT/ ) {
163 push (@{$ASSETDB{$sip}{"TCPCLI"}}, [ $sport, $service, $services, $s_info, $discovered ]);
164 } elsif ( $service =~ /SYN/ || $service =~ /^ACK$/ || $service =~ /^RST$/ || $service =~ /^FIN$/ ) {
165 push (@{$ASSETDB{$sip}{"OS"}}, [ $service, $os, $details, $discovered ]);
167 } elsif ($proto == 17) {
168 # UDP
169 if ( $service =~ /SERVER/ ) {
170 push (@{$ASSETDB{$sip}{"UDPSER"}}, [ $sport, $service, $services, $s_info, $discovered ]);
171 } elsif ($service =~ /CLIENT/ ) {
172 push (@{$ASSETDB{$sip}{"UDPCLI"}}, [ $sport, $service, $services, $s_info, $discovered ]);
175 last R_REPORT;
179 close (ASSETFILE);
182 =head2 make_attribute_table
184 Makes a snort host_attribute xml table of the assets
186 =cut
188 sub make_attribute_table {
189 my $asset;
190 my ($xmlout, $putxml, $feed, $confidence);
192 if (-e $OUTFILE && $FORCE == 0) {
193 print "[*] File Exists! Use -f|--force to force writing...\n";
194 print "[*] Exiting!\n";
195 exit 1;
198 $xmlout = new IO::File($OUTFILE, O_WRONLY | O_TRUNC |O_CREAT) or
199 die "[!] ERROR: Cannot open output file for writing - $!\n";
200 $putxml = new XML::Writer(OUTPUT => $xmlout, NEWLINES => 0, DATA_MODE => 1, DATA_INDENT => 1, UNSAFE => 1);
201 $putxml->startTag('SNORT_ATTRIBUTES');
202 $putxml->startTag('ATTRIBUTE_MAP');
203 $putxml->startTag('ENTRY');
204 $putxml->startTag('ID');
205 $putxml->characters("31337");
206 $putxml->endTag('ID');
207 $putxml->startTag('VALUE');
208 $putxml->characters("Edward Fjellskaal");
209 $putxml->endTag('VALUE');
210 $putxml->endTag('ENTRY');
211 $putxml->endTag('ATTRIBUTE_MAP');
213 $putxml->startTag('ATTRIBUTE_TABLE');
215 print "\n*** PARSING OF ASSET DB ***\n\n" if $DEBUG;
216 foreach $asset (sort (keys (%ASSETDB))) {
217 next if not defined $asset;
218 $assetcnt++;
219 print "[****] Processing $asset\n" if $DEBUG;
220 my ($os,$desc,$confidence,$timestamp,$flux) = guess_asset_os($asset);
221 my $details = normalize_description($os, $desc);
222 my ($frag3, $stream5) = get_policy($os, $desc);
223 $STATS{'OS'}{"$os"}{'count'} ++;
224 $STATS{'OS'}{"$os"}{'confidence'} += $confidence;
225 if ($confidence < $SKIPOS) {
226 print "$asset OS:Unknown - Skipping, confidence:$confidence\n" if $VERBOSE;
227 next;
229 if ($os =~ /unknown/) {
230 $unknowns++;
231 if ($VERBOSE) {
232 print "$asset OS:Unknown - Applying frag3=$frag3, stream5=$stream5 and confidence:$confidence\n";
234 } else {
235 $knowns++;
236 $aconfedence += $confidence;
237 if ($VERBOSE) {
238 print "$asset OS:$os - Applying frag3=$frag3, stream5=$stream5 and confidence:$confidence\n";
241 $putxml->startTag('HOST');
242 $putxml->startTag('IP');
243 $putxml->characters("$asset");
244 $putxml->endTag('IP');
245 $putxml->startTag('OPERATING_SYSTEM');
246 $putxml->startTag('NAME');
247 $putxml->startTag('ATTRIBUTE_VALUE');
248 $putxml->characters("$os");
249 $putxml->endTag('ATTRIBUTE_VALUE');
250 $putxml->startTag('CONFIDENCE');
251 $putxml->characters("$confidence");
252 $putxml->endTag('CONFIDENCE');
253 $putxml->endTag('NAME');
254 $putxml->startTag('VENDOR');
255 $putxml->startTag('ATTRIBUTE_VALUE');
256 $putxml->characters("$os");
257 $putxml->endTag('ATTRIBUTE_VALUE');
258 $putxml->startTag('CONFIDENCE');
259 $putxml->characters("$confidence");
260 $putxml->endTag('CONFIDENCE');
261 $putxml->endTag('VENDOR');
262 $putxml->startTag('VERSION');
263 $putxml->startTag('ATTRIBUTE_VALUE');
264 $putxml->characters("$details");
265 $putxml->endTag('ATTRIBUTE_VALUE');
266 $putxml->startTag('CONFIDENCE');
267 $putxml->characters("$confidence");
268 $putxml->endTag('CONFIDENCE');
269 $putxml->endTag('VERSION');
270 $putxml->startTag('FRAG_POLICY');
271 $putxml->characters("$frag3");
272 $putxml->endTag('FRAG_POLICY');
273 $putxml->startTag('STREAM_POLICY');
274 $putxml->characters("$stream5");
275 $putxml->endTag('STREAM_POLICY');
276 $putxml->endTag('OPERATING_SYSTEM');
278 if ($ASSETDB{$asset}->{"TCPSER"} || $ASSETDB{$asset}->{"UDPSER"}) {
279 # Metadata service tags in rules:
280 # grep "metadata:service" /etc/snort/rules/*.rules|sed "s/.*metadata\:service \(.\+\);.*;)/\1/"|awk '{print $1}'|sort -u
281 $putxml->startTag('SERVICES');
282 if ($ASSETDB{$asset}->{"TCPSER"}) {
283 make_service_attributes($asset, "tcp", "TCPSER", $putxml, $timestamp);
286 if ($ASSETDB{$asset}->{"UDPSER"}) {
287 make_service_attributes($asset, "udp", "UDPSER", $putxml, $timestamp);
289 $putxml->endTag('SERVICES');
291 # Commented out for now !
292 #if ($ASSETDB{$asset}->{"TCPCLI"} || $ASSETDB{$asset}->{"UDPCLI"}) {
293 # $putxml->startTag('CLIENTS');
294 # if ($ASSETDB{$asset}->{"TCPCLI"}) {
295 # make_client_attributes($asset, "tcp", "TCPCLI", $putxml);
297 # if ($ASSETDB{$asset}->{"UDPCLI"}) {
298 # make_client_attributes($asset, "udp", "UDPCLI", $putxml);
300 # $putxml->endTag('CLIENTS');
302 $putxml->endTag('HOST');
303 print "[****] Done\n\n" if $DEBUG;
306 # End it all
307 $putxml->endTag('ATTRIBUTE_TABLE');
308 $putxml->endTag('SNORT_ATTRIBUTES');
309 $putxml->end();
310 $xmlout->close();
313 =head2 get_policy
315 Gets the frag3 and stream5 policy
317 =cut
319 sub get_policy {
320 my ($OS, $DESC) = @_;
321 my ($frag3, $stream5) = ("BSD", "bsd");
322 switch ($OS){
323 case /Cisco/ {$frag3 = "Last"; $stream5 = "last";}
324 case /IOS/ {$frag3 = "Last"; $stream5 = "last";}
325 case /JetDirect/ {$frag3 = "BSD-right"; $stream5 = "bsd";}
326 case /HPUX/ {
327 switch ($DESC){
328 case /10/ {$frag3 = "BSD"; $stream5 = "hpux10";}
329 case /11/ {$frag3 = "First"; $stream5 = "hpux";}
330 else {$frag3 = "First"; $stream5 = "hpux";}
333 case /IRIX/ {$frag3 = "BSD"; $stream5 = "irix";}
334 case /Linux/ {
335 # stream5: Linux 2.4 and 2.6 = linux
336 # Linux 2.2 and earlier = old-linux
337 # frag3: linux for all linuxes
338 $frag3 = "linux";
339 switch ($DESC){
340 case /2\.6/ {$stream5 = "linux";}
341 case /2\.4/ {$stream5 = "linux";}
342 case /2\.2/ {$stream5 = "old-linux";}
343 case /2\.0/ {$stream5 = "old-linux";}
344 else {$stream5 = "linux";}
347 case /NetBSD/ {
348 # Assuming Freebsd properties!
349 # frag3: FreeBSD = BSD
350 # stream5: bsd
351 $frag3 = "BSD"; $stream5 = "bsd";
353 case /FreeBSD/ {
354 # frag3: FreeBSD = BSD
355 # stream5: bsd
356 $frag3 = "BSD"; $stream5 = "bsd";
358 case /OpenBSD/ {
359 # frag3: OpenBSD = linux
360 # stream5: OpenBSD = bsd
361 $frag3 = "linux"; $stream5 = "bsd";
363 case /MacOS/ {$frag3 = "First"; $stream5 = "macos";}
364 case /^SunOS/ {$frag3 = "First"; $stream5 = "first";}
365 case /Solaris/ {$frag3 = "Solaris"; $stream5 = "solaris";}
366 case /Windows/ {
367 # frag3 :Windows (95/98/NT4/W2K/XP) = Windows
368 # stream5: Windows 2003 Server (Guessing also 2008) = win2003
369 # Windows Vista = vista
370 # All other Windows = windows
371 $frag3 = "Windows";
372 switch ($DESC){
373 case /200[3,8]/ {$stream5 = "win2003";}
374 case /Vista/ {$stream5 = "vista";}
375 else {$stream5 = "windows";}
378 case /unknown/ {
379 if ($DEFAULTOS =~ /linux/) {
380 $frag3 = "linux";
381 $stream5 = "linux";
382 } elsif ($DEFAULTOS =~ /bsd/) {
383 $frag3 = "BSD";
384 $stream5 = "bsd";
385 } elsif ($DEFAULTOS =~ /windows/) {
386 $frag3 = "Windows";
387 $stream5 = "windows";
388 } elsif ($DEFAULTOS =~ /macos/) {
389 $frag3 = "First";
390 $stream5 = "macos";
393 #else {$frag3 = "BSD"; $stream5 = "bsd";}
395 push my @policy, ($frag3, $stream5, 90);
396 return @policy;
399 =head2 normalize_description
401 Normalized the description to something more snort lookalike
403 =cut
405 sub normalize_description {
406 my ($OS, $DESC) = @_;
407 switch ($OS) {
408 case /Linux/ {
409 switch ($DESC) {
410 case /2\.6/ { return "2.6"; }
411 case /2\.4/ { return "2.4"; }
412 case /2\.2/ { return "2.2"; }
413 case /2\.0/ { return "2.0"; }
414 else { return $DESC; }
417 case /Windows/ {
418 switch ($DESC) {
419 case /XP/ { return "XP"; }
420 case /2003/ { return "Windows 2003"; }
421 case /2008/ { return "Windows 2008"; }
422 case /Vista/ { return "Vista"; }
423 else { return $DESC; }
426 else { return $DESC; }
430 sub check_last_os_switch {
431 my $asset = shift;
432 my $ctimestamp = 0;
433 my $syn = 0;
434 foreach my $OS (@ {$ASSETDB{$asset}->{"OS"}}) {
435 if ($OS->[0] =~ /^SYN$/ ) {
436 $syn += 1;
437 $ctimestamp = $OS->[3] if ($OS->[3] > $ctimestamp);
441 if ($syn == 0) {
442 foreach my $OS (sort { $a <=> $b } (@ {$ASSETDB{$asset}->{"OS"}})) {
443 if ($OS->[0] =~ /^SYNACK$/ ) {
444 $syn += 1;
445 $ctimestamp = $OS->[3] if ($OS->[3] > $ctimestamp);
450 push my @return, ($ctimestamp, $syn);
451 #print "[--] Returning from check_last_os_switch\n" if $DEBUG;
452 return @return;
455 =head2 guess_asset_os
457 Tries to guess the asset OS in an inteligent way..
458 Snort OSes off importance:
459 bsd, old-linux, linux, first, last, windows, solaris,
460 win2003/win2k3, vista, hpux/hpux11, hpux10, irix, macos
461 Prads OSes:
463 =cut
465 sub guess_asset_os {
466 my $asset = shift;
467 my ($OS, $DETAILS, $CONFIDENCE, $TS, $FLUX) = ("unknown", "unknown", 0, 0, 0);
468 my %countos;
469 my %countdesc;
471 # look for latest os switch...
472 ($TS,$FLUX) = check_last_os_switch($asset);
473 # Lets look back the last 12 hours... Configurable?
474 $TS = $TS - 43200;
476 foreach $OS (@ {$ASSETDB{$asset}->{"OS"}}) {
477 next if ($OS->[3] < $TS);
478 print "[-] " . $OS->[0] . ": " . $OS->[1] . "\n" if $DEBUG;
479 if ($OS->[0] =~ /^SYNACK$/ ) {
480 $countos{ $OS->[1] }{"count"} += 4;
481 } elsif ($OS->[0] =~ /^SYN$/ ) {
482 $countos{$OS->[1]}{"count"} += 6;
483 } elsif ($OS->[0] =~ /^ACK$/ || $OS->[0] =~ /^FIN$/ || $OS->[0] =~ /^RST$/ ) {
484 $countos{$OS->[1]}{"count"} += 1;
488 my $HighestCNT = 0;
489 my $HighestOS = [];
490 for my $os (sort { $countos{$a} <=> $countos{$b} } keys %countos) {
491 next if ($os =~ /unknown/ );
492 print "[-] OS Counts: $os (" . $countos{$os}{"count"} . ")\n" if $DEBUG;
493 if ($countos{$os}{"count"} > $HighestCNT) {
494 $HighestCNT = $countos{$os}{"count"};
495 $HighestOS->[1] = $HighestOS->[0] if defined $HighestOS->[0];
496 $HighestOS->[0] = $os;
497 } elsif ($countos{$os}{"count"} == $HighestCNT) {
498 $HighestOS->[2] = $HighestOS->[1] if defined $HighestOS->[1];
499 $HighestOS->[1] = $os;
503 if (not defined $HighestOS->[0]) {
504 $OS = "unknown";
505 } else {
506 $OS = $HighestOS->[0];
509 push my @midfiltered, ("unknown", "unknown", 0, $TS, $FLUX);
510 return @midfiltered unless $OS;
511 return @midfiltered if ($OS =~ /unknown/);
513 print "[-] Final Guess OS: $OS\n" if $DEBUG;
515 foreach my $DESC (@ {$ASSETDB{$asset}->{"OS"}}) {
516 next if ($DESC->[3] < $TS);
517 next if not $DESC->[1] =~ /$OS/;
518 if ($DESC->[0] =~ /^SYN$/) {
519 $DETAILS = $DESC->[2];
520 last
521 } elsif ($DESC->[0] =~ /^SYNACK$/) {
522 $DETAILS = $DESC->[2];
523 } else {
524 $DETAILS = $DESC->[2];
528 if (not defined $DETAILS) {
529 print "[*] ERR - No details!\n" if $DEBUG;
530 foreach my $DESC (@ {$ASSETDB{$asset}->{"OS"}}) {
531 next if ($DESC->[3] < $TS);
532 next if not $DESC->[1] =~ /$OS/;
533 if ($DESC->[0] =~ /^RST$/) {
534 $DETAILS = $DESC->[2];
535 last
536 } elsif ($DESC->[0] =~ /^ACK$/) {
537 $DETAILS = $DESC->[2];
538 } else {
539 $DETAILS = $DESC->[2];
544 if ( not defined $OS ) {
545 print "[*] ERR - No OS!\n" if $DEBUG;
546 $DETAILS = "unknown";
547 $CONFIDENCE = 0;
548 $OS = "unknown";
549 } else {
550 $CONFIDENCE = 20 + (10 * $countos{$OS}{count});
551 $CONFIDENCE = 100 if $CONFIDENCE > 100;
553 print "[-] Final Guess Details: $DETAILS\n" if $DEBUG;
554 print "[-] Confidence: $CONFIDENCE\n" if $DEBUG;
555 push my @postfiltered, ($OS, $DETAILS, $CONFIDENCE, $TS, $FLUX);
556 return @postfiltered;
559 =head2 make_service_attributes
561 Populates all server attributes
563 =cut
565 sub make_service_attributes {
566 my ($asset, $proto, $key, $putxml, $TS) = @_;
567 #my @unique = @{$ASSETDB{$asset}->{"$key"}};
568 my @servassets = sort {$$a[0] <=> $$b[0]} @{$ASSETDB{$asset}->{"$key"}};
569 #$proto =~ tr/A-Z/a-z/;
570 my $PORTS = [];
571 foreach my $SA (@servassets) {
572 next if not defined $SA;
573 if ($SA->[4] < $TS) { # Skipp old assets
574 print "[##] Skipping old ASSET! " . $SA->[3] . "\n" if $DEBUG;
575 next;
577 my ($port, $service, $services, $details, $discovered) = ($SA->[0], $SA->[1], $SA->[2], $SA->[3], $SA->[4]);
578 if ($service =~ /SERVER/) {
579 print "[#*] SERVER: $services:$port - $details" if $DEBUG;
580 if ($services =~ /unknown/ || $services =~ /^@/) {
581 print " (Skipped)\n" if $DEBUG;
582 next;
584 if (defined $PORTS->[$port]) {
585 if ($PORTS->[$port]->[4] < $discovered) {
586 $PORTS->[$port] = $SA;
587 print " (Updated!)\n" if $DEBUG;
589 } else {
590 $PORTS->[$port] = $SA;
591 print " (New)\n" if $DEBUG;
595 return if not defined $PORTS;
597 my $confidence = 90;
598 my $tservices = "";
599 foreach $_ (@$PORTS) {
600 next if not defined $_;
601 my ($port, $service, $services, $details, $discovered) = ($_->[0], $_->[1], $_->[2], $_->[3], $_->[4]);
602 if ($service =~ /SERVER/) {
603 print "[#+] SERVER: $services:$port - $details (Added)\n" if $DEBUG;
604 if ($tservices eq '') {
605 $tservices = $services;
606 } else {
607 $tservices = "$tservices, $services"
610 my ($serv, $vers) = get_server_and_version($details);
611 $STATS{'SERVICE'}{"$serv"}{'count'} ++;
612 $putxml->startTag('SERVICE');
613 $putxml->startTag('PORT');
614 $putxml->startTag('ATTRIBUTE_VALUE');
615 $putxml->characters("$port"); # 22/80/443...
616 $putxml->endTag('ATTRIBUTE_VALUE');
617 $putxml->startTag('CONFIDENCE');
618 $putxml->characters("$confidence");
619 $putxml->endTag('CONFIDENCE');
620 $putxml->endTag('PORT');
621 $putxml->startTag('IPPROTO');
622 $putxml->startTag('ATTRIBUTE_VALUE');
623 $putxml->characters("$proto"); # tcp/udp/icmp...
624 $putxml->endTag('ATTRIBUTE_VALUE');
625 $putxml->startTag('CONFIDENCE');
626 $putxml->characters("$confidence");
627 $putxml->endTag('CONFIDENCE');
628 $putxml->endTag('IPPROTO');
629 $putxml->startTag('PROTOCOL');
630 $putxml->startTag('ATTRIBUTE_VALUE');
631 $putxml->characters("$services"); # http/ssh/smtp/ssl...
632 $putxml->endTag('ATTRIBUTE_VALUE');
633 $putxml->startTag('CONFIDENCE');
634 $putxml->characters("$confidence");
635 $putxml->endTag('CONFIDENCE');
636 $putxml->endTag('PROTOCOL');
637 $putxml->startTag('APPLICATION');
638 $putxml->startTag('ATTRIBUTE_VALUE');
639 $putxml->characters("$serv");
640 $putxml->endTag('ATTRIBUTE_VALUE');
641 $putxml->startTag('VERSION');
642 $putxml->startTag('ATTRIBUTE_VALUE');
643 $putxml->characters("$vers");
644 $putxml->endTag('ATTRIBUTE_VALUE');
645 $putxml->startTag('CONFIDENCE');
646 $putxml->characters("$confidence");
647 $putxml->endTag('CONFIDENCE');
648 $putxml->endTag('VERSION');
649 $putxml->endTag('APPLICATION');
650 $putxml->endTag('SERVICE');
653 if ($VERBOSE && not $tservices eq '') {
654 print "$asset SERVICES $proto: $tservices\n";
656 #print "[--] Returned from make_server_attributes\n" if $DEBUG;
659 =head2 make_client_attributes
661 Populates all client attributes for an asset
663 =cut
665 sub make_client_attributes {
666 my ($asset, $proto, $key, $putxml) = @_;
667 my @sorted = sort {$$a[0] <=> $$b[0]} @{$ASSETDB{$asset}->{"$key"}};
668 #$proto =~ tr/A-Z/a-z/;
669 my $confidence = 90;
670 foreach $_ (@sorted) {
671 my ($port, $service, $services, $details, $discovered) = ($_->[0], $_->[1], $_->[2], $_->[3], $_->[4]);
672 if ($service =~ /CLIENT/) {
673 next if $services =~ /unknown/;
674 next if $services =~ /^@/;
675 $details =~ s/^(\w+)\d?(.*)/$2/;
676 my $client = $2;
677 $putxml->startTag('CLIENT');
678 $putxml->startTag('PROTOCOL');
679 $putxml->startTag('ATTRIBUTE_VALUE');
680 $putxml->characters("$services"); # tcp/udp/
681 $putxml->endTag('ATTRIBUTE_VALUE');
682 $putxml->endTag('PROTOCOL');
683 $putxml->startTag('APPLICATION');
684 $putxml->startTag('ATTRIBUTE_VALUE');
685 $putxml->characters("$client"); # MS IE/Mozilla FX/..
686 $putxml->endTag('ATTRIBUTE_VALUE');
687 $putxml->startTag('VERSION');
688 $putxml->startTag('ATTRIBUTE_VALUE');
689 $putxml->characters("$details"); # 5.0/8.0/3.3
690 $putxml->endTag('ATTRIBUTE_VALUE');
691 $putxml->endTag('VERSION');
692 $putxml->endTag('APPLICATION');
693 $putxml->endTag('CLIENT');
698 =head2 get_server_and_version
700 Tries to extract server and version from "details"
702 =cut
704 sub get_server_and_version {
705 my $details = shift;
706 my ($serv, $vers) = ("UNKNOWN","UNKNOWN");
708 switch ($details) {
709 case /Apache/ {
710 $serv = "Apache";
711 if ($details =~ /([.\d]+)/) {
712 $vers = $1;
715 case /Microsoft-IIS/ {
716 $serv = "Microsoft-IIS";
717 if ($details =~ /([.\d]+)/) {
718 $vers = $1;
721 case /^Server:/ {
722 $details =~ /Server: (.*)/;
723 $serv = $1;
724 if ($details =~ /([.\d]+)/) {
725 $vers = $1;
728 case /Generic TLS 1\.0 SSL/ {
729 $serv = "SSL";
730 $vers = "TLS 1.0";
732 case /SSH/ {
733 $details =~ /(\w*[Ss]{2}[Hh]{1}\w*)/;
734 $serv = $1;
735 if ($details =~ /([.\d]+)/) {
736 $vers = $1;
739 case /Zope/ {
740 $serv = "Zope";
741 if ($details =~ /([.\d]+).*/) {
742 $vers = $1;
745 case /Remote Desktop Protocol/ {
746 $serv = "RDP";
747 if ($details =~ /\((.*)\)/) {
748 $vers = $1;
751 #case /SMTP|HTTP|.../ {
754 else {
755 if ($details =~ /^(\w+)[\ :](.*)/ ) {
756 $serv = $1;
757 if ($details =~ /([.\d]+).*/) {
758 $vers = $1;
760 } elsif ($details =~ /^(\w+).*/ ) {
761 $serv = $1;
762 if ($details =~ /([.\d]+).*/) {
763 $vers = $1;
765 } else {
766 if ($details =~ /([.\d]+)/) {
767 $vers = $1;
768 $serv = $details;
773 push my @matches, ($serv, $vers);
774 return @matches;
777 =head2 print_header
779 Some info to go on top...
781 =cut
783 sub print_header {
784 print "\n[*] Made by Edward Fjellskaal <edwardfjellskaal\@gmail.com> (c) 2010\n";
785 print "[*] Reading PRADS log file: $INFILE\n";
786 print "[*] Writing to snort attribute file: $OUTFILE\n";
787 if ($VERS) {
788 print "[*] Version: $VERSION\n\n";
789 exit 0;
793 =head2 print_footer
795 Some info to go on bottom...
797 =cut
799 sub print_footer {
800 if ((not defined $assetcnt) || ($assetcnt == 0)) {
801 print "[*] No assets found!\n";
802 exit 0;
804 $STATS{"OS"}{"unknown"}{'count'} = 0 if not defined $STATS{"OS"}{"unknown"}{'count'};
805 my $avg = ($aconfedence / ($assetcnt - $STATS{"OS"}{"unknown"}{'count'} )) ;
806 $avg =~ s/(\d{1,3})\.?.*/$1/;
807 print "[*] Processed $assetcnt hosts...\n";
808 print "[*] Hosts with Indication of a known OS: $knowns\n";
809 foreach my $OS (keys %{$STATS{"OS"}}) {
810 next if not defined $STATS{"OS"}{"$OS"}{'count'};
811 next if $STATS{"OS"}{"$OS"}{'count'} == 0;
812 next if not defined $STATS{"OS"}{"$OS"}{'confidence'};
813 next if $STATS{"OS"}{"$OS"}{'confidence'} == 0;
814 my $conf = $STATS{"OS"}{"$OS"}{'confidence'} / $STATS{"OS"}{"$OS"}{'count'};
815 $conf =~ s/(\d{1,3})\.?.*/$1/;
816 print "[--] $OS: " . $STATS{"OS"}{"$OS"}{'count'} .
817 " ($conf%)\n";
819 print "[*] Overall average OS confidence: $avg%\n";
820 foreach my $SRV (keys %{$STATS{"SERVICE"}}) {
821 print "[--] $SRV: " . $STATS{"SERVICE"}{"$SRV"}{'count'} . "\n";
823 print "[*] Done...\n\n";