Changeset 1652
- Timestamp:
- 11/12/06 12:54:00 (2 years ago)
- Files:
-
- psad/branches/sigdevel/psad (modified) (32 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
psad/branches/sigdevel/psad
r1648 r1652 82 82 # 83 83 # Sample tcp packet (rejected by Netfilter... --log-prefix = "DROP ") 84 # 84 85 # Mar 11 13:15:52 orthanc kernel: DROP IN=lo OUT= MAC=00:00:00:00:00:00:00:00: 85 86 # 00:00:00:00:08:00 SRC=127.0.0.1 DST=127.0.0.1 LEN=60 TOS=0x00 PREC=0x00 … … 87 88 # 88 89 # Sample icmp packet rejected by Netfilter INPUT chain: 90 # 89 91 # Nov 27 15:45:51 orthanc kernel: DROP IN=eth1 OUT= MAC=00:a0:cc:e2:1f:f2:00: 90 92 # 20:78:10:70:e7:08:00 SRC=192.168.10.20 DST=192.168.10.1 LEN=84 TOS=0x00 … … 92 94 # 93 95 # Sample icmp packet logged through FORWARD chain: 96 # 94 97 # Aug 20 21:23:32 orthanc kernel: SID365 IN=eth2 OUT=eth1 SRC=192.168.20.25 95 98 # DST=192.168.10.15 LEN=84 TOS=0x00 PREC=0x00 TTL=63 ID=0 DF PROTO=ICMP TYPE=8 … … 238 241 my %snort_ref_baseurl = (); 239 242 240 ### cache all scan signatures (initialized by default)243 ### cache all scan signatures from /etc/psad/signatures file 241 244 my %sigs = (); 242 243 ### cache signature messages, danger levels, etc. All of these 244 ### signatures come from the /etc/psad/signatures file. 245 my %sigs_attr = (); 246 247 ### cache all signatures that have been derived from Snort rules 248 ### (these signatures also come from /etc/psad/signatures, but conform 249 ### to the new signature format in psad-1.5.0 250 my %derived_sigs = (); 251 my %derived_sigs_attr = (); 245 my %sig_search = (); 252 246 253 247 ### cache Netfilter prefixes … … 426 420 'T' => $tcp_timestamp_type 427 421 ); 422 423 ### These are not directly support by psad because they 424 ### do not appear in Netfilter logs; however, several of 425 ### these options are supported if fwsnort is also running. 426 my @unsupported_snort_opts = qw( 427 pcre 428 fragbits 429 content-list 430 rpc 431 byte_test 432 byte_jump 433 distance 434 within 435 flowbits 436 rawbytes 437 regex 438 isdataat 439 uricontent 440 content 441 offset 442 replace 443 resp 444 flowbits 445 ); 446 447 ### for Snort signature sp/dp matching 448 my @port_types = ( 449 {'sp' => 'norm', 'dp' => 'norm'}, 450 {'sp' => 'norm', 'dp' => 'neg'}, 451 {'sp' => 'neg', 'dp' => 'norm'}, 452 {'sp' => 'neg', 'dp' => 'neg'}, 453 ); 454 455 ### packet parsing return values 456 my $PKT_ERROR = 0; 457 my $PKT_SUCCESS = 1; 458 my $PKT_IGNORE = 2; 459 460 ### header lengths 461 my $TCP_HEADER_LEN = 20; ### excludes options 462 my $UDP_HEADER_LEN = 8; 463 my $ICMP_HEADER_LEN = 4; 464 my $IP_HEADER_LEN = 20; ### excludes options 428 465 429 466 ### save a copy of the command line arguments … … 962 999 963 1000 my $pkt_ctr = 0; 964 my $print_scale_factor = 0; 965 966 if ($analyze_msgs) { 967 if ($#$fw_packets_aref < 100) { 968 $print_scale_factor = $#$fw_packets_aref; 969 } else { 970 $print_scale_factor = int($#$fw_packets_aref/10); 971 if ($print_scale_factor < 100) { 972 $print_scale_factor -= $print_scale_factor % 10; 973 } elsif ($print_scale_factor < 1000) { 974 $print_scale_factor -= $print_scale_factor % 100; 975 } elsif ($print_scale_factor < 10000) { 976 $print_scale_factor -= $print_scale_factor % 1000; 977 } elsif ($print_scale_factor < 100000) { 978 $print_scale_factor -= $print_scale_factor % 10000; 979 } elsif ($print_scale_factor < 1000000) { 980 $print_scale_factor -= $print_scale_factor % 100000; 981 } else { 982 $print_scale_factor = 50000; 983 } 984 } 985 $print_scale_factor++ if $print_scale_factor == 0; 986 } 987 988 # Mar 11 13:15:52 orthanc kernel: DROP IN=lo OUT= MAC=00:00:00:00:00:00:00:00: 989 # 00:00:00:00:08:00 SRC=127.0.0.1 DST=127.0.0.1 LEN=60 TOS=0x00 PREC=0x00 990 # TTL=64 ID=0 DF PROTO=TCP SPT=44847 DPT=35 WINDOW=32304 RES=0x00 SYN URGP=0 991 992 PKT: for my $pkt (@$fw_packets_aref) { 993 my $src = ''; 994 my $dst = ''; 995 my $len = -1; 996 my $tos = ''; 997 my $ttl = -1; 998 my $id = -1; 999 my $proto = ''; 1000 my $sp = -1; 1001 my $dp = -1; 1002 my $win = -1; 1003 my $type = -1; 1004 my $code = -1; 1005 my $seq = -1; 1006 my $flags = ''; 1007 my $frag_bit = 0; 1008 my $sid = 0; 1009 my $chain = ''; 1010 my $intf = ''; 1011 my $src_mac = ''; 1012 my $dst_mac = ''; 1013 my $tcp_options = ''; 1014 my $dshield_str = ''; 1015 my $syslog_host = ''; 1016 my $log_prefix = ''; 1017 1018 print STDERR $pkt, "\n" if $debug; 1001 1002 my $print_scale_factor = &get_scale_factor($#$fw_packets_aref); 1003 1004 ### loop through all of the packet log messages we have just acquired 1005 ### from Netfilter/iptables 1006 1007 PKT: for my $pkt_str (@$fw_packets_aref) { 1008 1009 ### main packet data structure 1010 my %pkt = ( 1011 1012 ### data link layer 1013 'src_mac' => '', 1014 'dst_mac' => '', 1015 'intf' => '', ### FIXME in and out interfaces? 1016 1017 ### network layer 1018 'src' => '', 1019 'dst' => '', 1020 'proto' => '', 1021 'ip_id' => -1, 1022 'ttl' => -1, 1023 'tos' => '', 1024 'ip_len' => -1, 1025 'itype' => -1, 1026 'icode' => -1, 1027 'icmp_seq' => -1, 1028 'icmp_id' => -1, 1029 'frag_bit' => 0, 1030 1031 ### transport layer 1032 'sp' => -1, 1033 'dp' => -1, 1034 'win' => -1, 1035 'flags' => -1, 1036 'tcp_seq' => -1, 1037 'tcp_ack' => -1, 1038 'tcp_opt' => '', 1039 'udp_len' => -1, 1040 1041 ### extra fields for psad internals (DShield reporting, fwsnort 1042 ### sid matching, Netfilter logging prefixes and chains, etc.) 1043 'fwsnort_sid' => 0, 1044 'nf_chain'=> '', 1045 'nf_log_prefix' => '', 1046 'dshield_str' => '', 1047 'syslog_host' => '', 1048 ); 1049 1019 1050 1020 1051 if ($analyze_msgs) { … … 1025 1056 } 1026 1057 1027 ### see if we need to ignore this packet based on the 1028 ### IGNORE_PROTOCOLS config keyword. 1029 if (%ignore_protocols) { 1030 for my $proto (keys %ignore_protocols) { 1031 next PKT if $pkt =~ /\sPROTO=$proto\s/; 1032 } 1033 } 1034 1035 ### get the in/out interface and Netfilter chain 1036 if ($pkt =~ /IN=(\S+)\s+OUT=\s/) { 1037 $intf = $1; 1038 $chain = 'INPUT'; 1039 } elsif ($pkt =~ /IN=(\S+)\s+OUT=\S/) { 1040 $intf = $1; 1041 $chain = 'FORWARD'; 1042 } elsif ($pkt =~ /IN=\s+OUT=(\S+)/) { 1043 $intf = $1; 1044 $chain = 'OUTPUT'; 1045 } 1046 1047 if ($pkt =~ /\sMAC=(\S+)/) { 1048 my $mac_str = $1; 1049 if ($mac_str =~ /^((?:\w{2}\:){6})((?:\w{2}\:){6})/) { 1050 $dst_mac = $1; 1051 $src_mac = $2; 1052 } 1053 } 1054 if ($src_mac) { 1055 $src_mac =~ s/:$//; 1056 print STDERR "[+] src mac addr: $src_mac\n" if $debug; 1057 } 1058 if ($dst_mac) { 1059 $dst_mac =~ s/:$//; 1060 print STDERR "[+] dst mac addr: $dst_mac\n" if $debug; 1061 } 1062 1063 unless ($intf and $chain) { 1064 print STDERR "[-] err packet: could not determine ", 1065 "interface and chain.\n" if $debug; 1066 push @err_pkts, $pkt; 1058 ### main parsing routine for the Netfilter packet logging message 1059 my $pkt_parse_rv = &parse_NF_pkt_str(\%pkt, $pkt_str); 1060 if ($pkt_parse_rv == $PKT_ERROR) { 1061 push @err_pkts, $pkt_str; 1067 1062 next PKT; 1068 } 1069 1070 if (%ignore_interfaces) { 1071 for my $ignore_intf (keys %ignore_interfaces) { 1072 next PKT if $intf eq $ignore_intf; 1073 } 1074 } 1075 1076 ### get the syslog logging host for this packet 1077 if ($pkt =~ /(\S+)\s+kernel:/) { 1078 $syslog_host = $1; 1079 } elsif ($pkt =~ /^\s*\S+\s+\S+\s+\S+\s+(\S+)/) { 1080 ### parsed packet from the beginning where the time portion 1081 ### of the syslog message is 1082 $syslog_host = $1; 1083 } 1084 1085 ### try to extract a snort sid (generated by fwsnort) from 1086 ### the packet 1087 unless ($no_snort_sids) { 1088 if ($pkt =~ /$config{'SNORT_SID_STR'}(\d+)/) { 1089 $sid = $1; 1090 } 1091 } 1092 1093 unless ($sid or $config{'FW_SEARCH_ALL'} eq 'Y') { 1094 ### note that this is not _too_ strict since people 1095 ### have different ways of writing --log-prefix strings 1096 my $matched = 0; 1097 for my $fw_search_str (@fw_search) { 1098 $matched = 1 if $pkt =~ /$fw_search_str/; 1099 } 1100 next PKT unless $matched; 1101 } 1102 1103 ### see if there is a logging prefix (used for scan email alert even 1104 ### if we are running with FW_SEARCH_ALL = Y). Note that sometimes 1105 ### there is a buffering issue in the kernel ring buffer that is used 1106 ### to hold the Netfilter log message, so we want to get only the 1107 ### very last possible candidate for the log prefix (this is why the 1108 ### "kernel:" string is preceded by .*). 1109 if ($pkt =~ /.*kernel:\s+(.*?)\s*IN=/) { 1110 $log_prefix = $1; 1111 if ($log_prefix =~ /\S/) { 1112 if ($config{'IGNORE_LOG_PREFIXES'} ne 'NONE') { 1113 next PKT if $log_prefix 1114 =~ m|$config{'IGNORE_LOG_PREFIXES'}|; 1115 } 1116 $ipt_prefixes{$log_prefix}++; 1117 } 1118 } 1119 1120 ### May 18 22:21:26 orthanc kernel: DROP IN=eth2 OUT= 1121 ### MAC=00:60:1d:23:d0:01:00:60:1d:23:d3:0e:08:00 SRC=192.168.20.25 1122 ### DST=192.168.20.1 LEN=60 TOS=0x10 PREC=0x00 TTL=64 ID=47300 DF 1123 ### PROTO=TCP SPT=34111 DPT=6345 WINDOW=5840 RES=0x00 SYN URGP=0 1124 if ($pkt =~ /SRC=(\S+)\s+DST=(\S+)\s+LEN=(\d+)\s+TOS=(\S+) 1125 \s*.*\s+TTL=(\d+)\s+ID=(\d+)\s*.*\s+PROTO=TCP\s+ 1126 SPT=(\d+)\s+DPT=(\d+)\s.*\s*WINDOW=(\d+)\s+ 1127 (.*)\s+URGP=/x) { 1128 ($src, $dst, $len, $tos, $ttl, $id, $sp, $dp, $win, $flags) = 1129 ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10); 1130 1131 ### the reserve bits are not reported by ulogd, but normal 1132 ### Netfilter syslog messages contain them. 1133 $flags =~ s/\s*RES=\S+\s*//; 1134 1135 $proto = 'tcp'; 1136 1137 $flags = 'NULL' unless $flags; ### default to NULL 1138 if (!$sid && $config{'IGNORE_CONNTRACK_BUG_PKTS'} eq 'Y' && 1139 ($flags =~ /ACK/ || $flags =~ /RST/)) { 1140 # $dp > 1024 && ($flags =~ /ACK/ || 1141 ### FIXME: ignore TCP packets that have the ACK or RST 1142 ### bits set (unless we matched a snort sid) since 1143 ### _usually_ we see these packets as a result of the 1144 ### Netfilter connection tracking bug. Also, note that 1145 ### no signatures make use of the RST flag. 1146 print STDERR "[-] err packet: matched ACK or RST flag.\n" 1147 if $debug; 1148 next PKT; 1149 } 1150 ### per page 595 of the Camel book, "if /blah1|blah2/" 1151 ### can be slower than "if /blah1/ || /blah2/ 1152 unless ($flags !~ /WIN/ && 1153 $flags =~ /ACK/ || 1154 $flags =~ /SYN/ || 1155 $flags =~ /RST/ || 1156 $flags =~ /URG/ || 1157 $flags =~ /PSH/ || 1158 $flags =~ /FIN/ || 1159 $flags eq 'NULL') { 1160 print STDERR "[-] err packet: bad tcp flags.\n" if $debug; 1161 push @err_pkts, $pkt; 1162 next PKT; 1163 } 1164 $frag_bit = 1 if $pkt =~ /\sDF\s+PROTO/; 1165 ### don't pickup IP options if --log-ip-options is used 1166 ### (they appear before the PROTO= field). 1167 if ($pkt =~ /URGP=\S+\s+OPT\s+\((\S+)\)/) { 1168 $tcp_options = $1; 1169 } 1170 1171 ### make sure we have a "reasonable" packet (note that nmap 1172 ### can scan port 0 and Netfilter can report this fact) 1173 unless ($src and $dst and $len >= 0 and $tos and $ttl >= 0 1174 and $id >= 0 and $proto and $sp >= 0 and $dp >= 0 1175 and $win >= 0 and $flags) { 1176 push @err_pkts, $pkt; 1177 next PKT; 1178 } 1179 1180 ### see if we need to ignore this packet based on the 1181 ### IGNORE_PORTS config keyword 1182 if (%ignore_ports) { 1183 next PKT if &ignore_port($dp, $proto); 1184 } 1185 1063 } elsif ($pkt_parse_rv == $PKT_IGNORE) { 1064 next PKT; 1065 } 1066 1067 if ($pkt{'proto'} eq 'tcp') { 1186 1068 $tcp_ctr++; 1187 1188 if ($config{'ENABLE_DSHIELD_ALERTS'} eq 'Y' 1189 and not $benchmark 1190 and not $analyze_msgs) { 1191 my $dflags = $flags; 1192 $dflags =~ s/\s/,/g; 1193 $dshield_str = "$src\t$sp\t$dst\t$dp\t$proto\t$dflags"; 1194 } 1195 ### May 18 22:21:26 orthanc kernel: DROP IN=eth2 OUT= 1196 ### MAC=00:60:1d:23:d0:01:00:60:1d:23:d3:0e:08:00 1197 ### SRC=192.168.20.25 DST=192.168.20.1 LEN=28 TOS=0x00 PREC=0x00 1198 ### TTL=40 ID=47523 PROTO=UDP SPT=57339 DPT=305 LEN=8 1199 } elsif ($pkt =~ /SRC=(\S+)\s+DST=(\S+)\s+LEN=(\d+)\s+TOS=(\S+) 1200 \s.*TTL=(\d+)\s+ID=(\d+)\s*.*\s+PROTO=UDP\s+ 1201 SPT=(\d+)\s+DPT=(\d+)/x) { 1202 ($src, $dst, $len, $tos, $ttl, $id, $sp, $dp) = 1203 ($1,$2,$3,$4,$5,$6,$7,$8); 1204 $proto = 'udp'; 1205 1206 ### make sure we have a "reasonable" packet (note that nmap 1207 ### can scan port 0 and Netfilter can report this fact) 1208 unless ($src and $dst and $len >= 0 and $tos and $ttl >= 0 1209 and $id >= 0 and $proto and $sp >= 0 and $dp >= 0) { 1210 push @err_pkts, $pkt; 1211 next PKT; 1212 } 1213 1214 ### see if we need to ignore this packet based on the 1215 ### IGNORE_PORTS config keyword 1216 if (%ignore_ports) { 1217 next PKT if &ignore_port($dp, $proto); 1218 } 1219 1069 } elsif ($pkt{'proto'} eq 'udp') { 1220 1070 $udp_ctr++; 1221 1222 if ($config{'ENABLE_DSHIELD_ALERTS'} eq 'Y' 1223 and not $benchmark 1224 and not $analyze_msgs) { 1225 $dshield_str = "$src\t$sp\t$dst\t$dp\t$proto"; 1226 } 1227 } elsif ($pkt =~ /SRC=(\S+)\s+DST=(\S+)\s+LEN=(\d+).* 1228 TTL=(\d+).*PROTO=ICMP\s+TYPE=(\d+)\s+ 1229 CODE=(\d+)\s+ID=(\d+)\s+SEQ=(\d+)/x) { 1230 ($src, $dst, $len, $ttl, $type, $code, $id, $seq) = 1231 ($1,$2,$3,$4,$5,$6,$7,$8); 1232 $proto = 'icmp'; 1233 unless ($src and $dst and $len >= 0 and $ttl >= 0 and $proto 1234 and $type >= 0 and $code >= 0 and $id >= 0 1235 and $seq >= 0) { 1236 push @err_pkts, $pkt; 1237 next PKT; 1238 } 1071 } elsif ($pkt{'proto'} eq 'icmp') { 1239 1072 $icmp_ctr++; 1240 1241 if ($config{'ENABLE_DSHIELD_ALERTS'} eq 'Y'1242 and not $benchmark1243 and not $analyze_msgs) {1244 $dshield_str = "$src\t$type\t$dst\t$code\t$proto";1245 }1246 } else {1247 ### Sometimes the Netfilter log entry gets messed up due to1248 ### buffering issues so we write it to the error log.1249 print STDERR "[-] err packet: no regex match.\n" if $debug;1250 push @err_pkts, $pkt;1251 next PKT;1252 1073 } 1253 1074 1254 1075 ### If we made it here then we correctly matched packets 1255 1076 ### that the firewall logged. 1256 print STDERR "[+] valid packet: $src -> $dst $proto\n" if $debug; 1077 print STDERR "[+] valid packet: $pkt{'src'} -> $pkt{'dst'} ", 1078 "$pkt{'proto'}\n" if $debug; 1257 1079 1258 1080 ### initialize the danger level to 0 if it is not already defined … … 1260 1082 ### different destination IP, so the danger level represents the 1261 1083 ### aggregate danger level). 1262 unless (defined $scan_dl{$ src}) {1263 $scan_dl{$ src} = 0;1264 $scan{$ src}{$dst}{'alerted'} = 01084 unless (defined $scan_dl{$pkt{'src'}}) { 1085 $scan_dl{$pkt{'src'}} = 0; 1086 $scan{$pkt{'src'}}{$pkt{'dst'}}{'alerted'} = 0 1265 1087 if $config{'ALERT_ALL'} eq 'N'; 1266 1088 } … … 1270 1092 ### -1 if there is no auto-assigned danger level. 1271 1093 unless ($no_auto_dl) { 1272 my $rv = &assign_auto_danger_level($src, $proto, $dp); 1273 if ($debug) { 1274 print STDERR "[+] assign_auto_danger_level() returned: $rv\n"; 1275 } 1094 my $rv = &assign_auto_danger_level($pkt{'src'}, $pkt{'proto'}, 1095 $pkt{'dp'}); 1096 1097 print STDERR "[+] assign_auto_danger_level() returned: $rv\n" 1098 if $debug; 1276 1099 if ($rv == 0) { 1277 print STDERR "[+] ignoring $src $proto $dp scan.\n" if $debug; 1100 print STDERR "[+] ignoring $pkt{'src'} $pkt{'proto'} ", 1101 "$pkt{'dp'} scan.\n" if $debug; 1278 1102 next PKT; 1279 1103 } … … 1286 1110 and not $benchmark 1287 1111 and not $analyze_msgs 1288 and $ dshield_str) {1289 if ($pkt =~ /^\s*(\w+)\s+(\d+)\s+(\S+)/) {1112 and $pkt{'dshield_str'}) { 1113 if ($pkt_str =~ /^\s*(\w+)\s+(\d+)\s+(\S+)/) { 1290 1114 my $month = Decode_Month($1); 1291 1115 my $day = sprintf("%.2d", $2); … … 1293 1117 push @dshield_data, "$year-$month-$day $time_24 " . 1294 1118 "$timezone\t$config{'DSHIELD_USER_ID'}\t1" . 1295 "\t$ dshield_str\n";1119 "\t$pkt{'dshield_str'}\n"; 1296 1120 } 1297 1121 } … … 1299 1123 ### see if we need to timeout any old scans 1300 1124 if ($config{'ENABLE_PERSISTENCE'} eq 'N') { 1301 if (defined $scan{$ src}{$dst}{'s_time'}) {1302 if ((time() - $scan{$ src}{$dst}{'s_time'})1125 if (defined $scan{$pkt{'src'}}{$pkt{'dst'}}{'s_time'}) { 1126 if ((time() - $scan{$pkt{'src'}}{$pkt{'dst'}}{'s_time'}) 1303 1127 >= $config{'SCAN_TIMEOUT'}) { 1304 delete $scan{$ src}{$dst};1128 delete $scan{$pkt{'src'}}{$pkt{'dst'}}; 1305 1129 } 1306 1130 } … … 1308 1132 1309 1133 ### record the absolute starting time of the scan 1310 unless (defined $scan{$ src}{$dst}{'s_time'}) {1134 unless (defined $scan{$pkt{'src'}}{$pkt{'dst'}}{'s_time'}) { 1311 1135 if ($analyze_msgs) { 1312 if ($pkt =~ /^(.*?)\s+\S+\s+kernel:/) {1313 $scan{$ src}{$dst}{'s_time'} = $1;1314 } elsif ($pkt =~ /^\s*(\S+\s+\S+\s+\S+)/) {1315 $scan{$ src}{$dst}{'s_time'} = $1;1136 if ($pkt_str =~ /^(.*?)\s+\S+\s+kernel:/) { 1137 $scan{$pkt{'src'}}{$pkt{'dst'}}{'s_time'} = $1; 1138 } elsif ($pkt_str =~ /^\s*(\S+\s+\S+\s+\S+)/) { 1139 $scan{$pkt{'src'}}{$pkt{'dst'}}{'s_time'} = $1; 1316 1140 } else { 1317 die "[*] Could not extract time from packet: $pkt \n",1141 die "[*] Could not extract time from packet: $pkt_str\n", 1318 1142 " Please send a bug report to: ", 1319 1143 "mbr\@cipherdyne.org\n"; 1320 1144 } 1321 1145 } else { 1322 $scan{$ src}{$dst}{'s_time'} = time();1146 $scan{$pkt{'src'}}{$pkt{'dst'}}{'s_time'} = time(); 1323 1147 } 1324 1148 } 1325 1149 1326 1150 ### increment hash values 1327 $scan{$src}{$dst}{'absnum'}++; 1328 $scan{$src}{$dst}{'chain'}{$chain}{$intf}{$proto}++; 1329 $curr_scan{$src}{$dst}{$proto}{'pkts'}++; 1330 $curr_scan{$src}{$dst}{$proto}{'flags'}{$flags}++ 1331 if $flags; 1151 $scan{$pkt{'src'}}{$pkt{'dst'}}{'absnum'}++; 1152 $scan{$pkt{'src'}}{$pkt{'dst'}}{'chain'} 1153 {$pkt{'nf_chain'}}{$pkt{'intf'}}{$pkt{'proto'}}++; 1154 $curr_scan{$pkt{'src'}}{$pkt{'dst'}}{$pkt{'proto'}}{'pkts'}++; 1155 $curr_scan{$pkt{'src'}}{$pkt{'dst'}} 1156 {$pkt{'proto'}}{'flags'}{$pkt{'flags'}}++ if $pkt{'flags'}; 1332 1157 1333 1158 ### keep track of MAC addresses 1334 $curr_scan{$ src}{$dst}{'s_mac'} = $src_mac;1335 $curr_scan{$ src}{$dst}{'d_mac'} = $dst_mac;1159 $curr_scan{$pkt{'src'}}{$pkt{'dst'}}{'s_mac'} = $pkt{'src_mac'}; 1160 $curr_scan{$pkt{'src'}}{$pkt{'dst'}}{'d_mac'} = $pkt{'dst_mac'}; 1336 1161 1337 1162 ### keep track of which syslog daemon reported the message. 1338 $curr_scan{$ src}{$dst}{'syslog_host'}{$syslog_host} = ''1339 if $syslog_host;1340 1341 if ($ log_prefix) {1163 $curr_scan{$pkt{'src'}}{$pkt{'dst'}}{'syslog_host'} 1164 {$pkt{'syslog_host'}} = '' if $pkt{'syslog_host'}; 1165 1166 if ($pkt{'nf_log_prefix'}) { 1342 1167 ### see if the logging prefix matches the blocking 1343 1168 ### regex, and if not the IP will not be blocked … … 1346 1171 and $config{'AUTO_BLOCK_REGEX'} ne 'NONE') { 1347 1172 ### we require a match 1348 if (not defined $auto_block_regex_match{$ src}1349 and $ log_prefix=~ /$config{'AUTO_BLOCK_REGEX'}/) {1350 $auto_block_regex_match{$ src} = '';1173 if (not defined $auto_block_regex_match{$pkt{'src'}} 1174 and $pkt{'nf_log_prefix'} =~ /$config{'AUTO_BLOCK_REGEX'}/) { 1175 $auto_block_regex_match{$pkt{'src'}} = ''; 1351 1176 } 1352 1177 } 1353 1178 } else { 1354 $ log_prefix= '*noprfx*';1179 $pkt{'nf_log_prefix'} = '*noprfx*'; 1355 1180 } 1356 1181 1357 1182 ### keep track of Netfilter chain and logging prefix 1358 $curr_scan{$ src}{$dst}{$proto}{'chain'}1359 {$ chain}{$log_prefix}++;1360 1361 unless ($p rotoeq 'icmp') {1183 $curr_scan{$pkt{'src'}}{$pkt{'dst'}}{$pkt{'proto'}}{'chain'} 1184 {$pkt{'nf_chain'}}{$pkt{'nf_log_prefix'}}++; 1185 1186 unless ($pkt{'proto'} eq 'icmp') { 1362 1187 ### initialize the start and end port for the scanned port range 1363 if (not defined $curr_scan{$ src}{$dst}{$proto}{'strtp'}) {1188 if (not defined $curr_scan{$pkt{'src'}}{$pkt{'dst'}}{$pkt{'proto'}}{'strtp'}) { 1364 1189 ### make sure the initial start port is not too low 1365 $curr_scan{$ src}{$dst}{$proto}{'strtp'} = 65535;1190 $curr_scan{$pkt{'src'}}{$pkt{'dst'}}{$pkt{'proto'}}{'strtp'} = 65535; 1366 1191 ### make sure the initial end port is not too high 1367 $curr_scan{$ src}{$dst}{$proto}{'endp'} = 0;1368 } 1369 if (not defined $scan{$ src}{$dst}{$proto}{'abs_sp'}) {1192 $curr_scan{$pkt{'src'}}{$pkt{'dst'}}{$pkt{'proto'}}{'endp'} = 0; 1193 } 1194 if (not defined $scan{$pkt{'src'}}{$pkt{'dst'}}{$pkt{'proto'}}{'abs_sp'}) { 1370 1195 ### This is the absolute starting port since the 1371 1196 ### first packet was detected. Make sure the initial 1372 1197 ### start port is not too low 1373 $scan{$ src}{$dst}{$proto}{'abs_sp'} = 65535;1198 $scan{$pkt{'src'}}{$pkt{'dst'}}{$pkt{'proto'}}{'abs_sp'} = 65535; 1374 1199 ### make sure the initial end port is not too high 1375 $scan{$ src}{$dst}{$proto}{'abs_ep'} = 0;1200 $scan{$pkt{'src'}}{$pkt{'dst'}}{$pkt{'proto'}}{'abs_ep'} = 0; 1376 1201 } 1377 1202 1378 1203 ### see if the destination port lies outside our current range 1379 1204 ### and change if needed 1380 ($curr_scan{$ src}{$dst}{$proto}{'strtp'},1381 $curr_scan{$ src}{$dst}{$proto}{'endp'}) =1382 &check_range($ dp,1383 $curr_scan{$ src}{$dst}{$proto}{'strtp'},1384 $curr_scan{$ src}{$dst}{$proto}{'endp'});1385 ($scan{$ src}{$dst}{$proto}{'abs_sp'},1386 $scan{$ src}{$dst}{$proto}{'abs_ep'}) =1387 &check_range($ dp,1388 $scan{$ src}{$dst}{$proto}{'abs_sp'},1389 $scan{$ src}{$dst}{$proto}{'abs_ep'});1390 } 1391 1392 print STDERR Dumper $scan{$ src}{$dst} if $debug and $verbose;1205 ($curr_scan{$pkt{'src'}}{$pkt{'dst'}}{$pkt{'proto'}}{'strtp'}, 1206 $curr_scan{$pkt{'src'}}{$pkt{'dst'}}{$pkt{'proto'}}{'endp'}) = 1207 &check_range($pkt{'dp'}, 1208 $curr_scan{$pkt{'src'}}{$pkt{'dst'}}{$pkt{'proto'}}{'strtp'}, 1209 $curr_scan{$pkt{'src'}}{$pkt{'dst'}}{$pkt{'proto'}}{'endp'}); 1210 ($scan{$pkt{'src'}}{$pkt{'dst'}}{$pkt{'proto'}}{'abs_sp'}, 1211 $scan{$pkt{'src'}}{$pkt{'dst'}}{$pkt{'proto'}}{'abs_ep'}) = 1212 &check_range($pkt{'dp'}, 1213 $scan{$pkt{'src'}}{$pkt{'dst'}}{$pkt{'proto'}}{'abs_sp'}, 1214 $scan{$pkt{'src'}}{$pkt{'dst'}}{$pkt{'proto'}}{'abs_ep'}); 1215 } 1216 1217 print STDERR Dumper $scan{$pkt{'src'}}{$pkt{'dst'}} if $debug and $verbose; 1393 1218 1394 1219 ### attempt to passively guess the remote operating … … 1401 1226 ### and if we have been unsuccessful in guessing 1402 1227 ### the OS after 100 packets don't keep trying. 1403 if ($p roto eq 'tcp' and $flags=~ /SYN/) {1404 if ($ tcp_options) { ### got the tcp options portion of the header1228 if ($pkt{'proto'} eq 'tcp' and $pkt{'flags'} =~ /SYN/) { 1229 if ($pkt{'tcp_opt'}) { ### got the tcp options portion of the header 1405 1230 1406 1231 ### p0f based fingerprinting 1407 &p0f($src, $len, $frag_bit, $ttl, $win, $tcp_options); 1408 1409 } elsif (not defined $posf{$src}{'guess'} 1410 and $scan{$src}{$dst}{'absnum'} < 100) { 1411 &posf($src, $len, $tos, $ttl, $id, $win) 1412 } 1413 } 1414 } 1415 1416 if ($sid and not $no_snort_sids) { 1232 &p0f($pkt{'src'}, $pkt{'ip_len'}, $pkt{'frag_bit'}, 1233 $pkt{'ttl'}, $pkt{'win'}, $pkt{'tcp_opt'}); 1234 1235 } elsif (not defined $posf{$pkt{'src'}}{'guess'} 1236 and $scan{$pkt{'src'}}{$pkt{'dst'}}{'absnum'} < 100) { 1237 &posf($pkt{'src'}, $pkt{'ip_len'}, $pkt{'tos'}, 1238 $pkt{'ttl'}, $pkt{'ip_id'}, $pkt{'win'}) 1239 } 1240 } 1241 } 1242 1243 if ($pkt{'fwsnort_sid'} and not $no_snort_sids) { 1417 1244 ### found a snort sid in the packet log message 1418 my $dl = &add_snort_sid($ src, $dst,1419 $ chain, $proto, $sid);1420 $curr_sids_dl{$ src} = $dl if $dl;1245 my $dl = &add_snort_sid($pkt{'src'}, $pkt{'dst'}, 1246 $pkt{'nf_chain'}, $pkt{'proto'}, $pkt{'fwsnort_sid'}); 1247 $curr_sids_dl{$pkt{'src'}} = $dl if $dl; 1421 1248 } else { 1422 1249 ### attempt to match any tcp/udp/icmp signatures in the 1423 1250 ### main signatures hash 1424 1251 unless ($no_signatures) { 1425 my $dl = &match_sigs($src, $dst, $chain, $sp, 1426 $dp, $proto, $flags, $len, $ttl,1427 $type, $code, $id, $seq); 1428 $curr_sigs_dl{$ src} = $dl if $dl;1252 1253 my $dl = &match_sigs(\%pkt); 1254 1255 $curr_sigs_dl{$pkt{'src'}} = $dl if $dl; 1429 1256 } 1430 1257 } … … 1462 1289 } 1463 1290 1291 sub parse_NF_pkt_str() { 1292 my ($pkt_hr, $pkt_str) = @_; 1293 1294 print STDERR $pkt_str, "\n" if $debug; 1295 1296 ### see if there is a logging prefix (used for scan email alert even 1297 ### if we are running with FW_SEARCH_ALL = Y). Note that sometimes 1298 ### there is a buffering issue in the kernel ring buffer that is used 1299 ### to hold the Netfilter log message, so we want to get only the 1300 ### very last possible candidate for the log prefix (this is why the 1301 ### "kernel:" string is preceded by .*). 1302 if ($pkt_str =~ /.*kernel:\s+(.*?)\s*IN=/) { 1303 $pkt_hr->{'nf_log_prefix'} = $1; 1304 if ($pkt_hr->{'nf_log_prefix'} =~ /\S/) { 1305 if ($config{'IGNORE_LOG_PREFIXES'} ne 'NONE') { 1306 return $PKT_IGNORE if $pkt_hr->{'nf_log_prefix'} 1307 =~ m|$config{'IGNORE_LOG_PREFIXES'}|; 1308 } 1309 $ipt_prefixes{$pkt_hr->{'nf_log_prefix'}}++; 1310 } 1311 } 1312 1313 ### get the in/out interface and Netfilter chain 1314 if ($pkt_str =~ /IN=(\S+)\s+OUT=\s/) { 1315 $pkt_hr->{'intf'} = $1; 1316 $pkt_hr->{'chain'} = 'INPUT'; 1317 } elsif ($pkt_str =~ /IN=(\S+)\s+OUT=\S/) { 1318 $pkt_hr->{'intf'} = $1; 1319 $pkt_hr->{'chain'} = 'FORWARD'; 1320 } elsif ($pkt_str =~ /IN=\s+OUT=(\S+)/) { 1321 $pkt_hr->{'intf'} = $1; 1322 $pkt_hr->{'chain'} = 'OUTPUT'; 1323 } 1324 1325 if ($pkt_str =~ /\sMAC=(\S+)/) { 1326 my $mac_str = $1; 1327 if ($mac_str =~ /^((?:\w{2}\:){6})((?:\w{2}\:){6})/) { 1328 $pkt_hr->{'dst_mac'} = $1; 1329 $pkt_hr->{'src_mac'} = $2; 1330 } 1331 } 1332 if ($pkt_hr->{'src_mac'}) { 1333 $pkt_hr->{'src_mac'} =~ s/:$//; 1334 print STDERR "[+] src mac addr: $pkt_hr->{'src_mac'}\n" if $debug; 1335 } 1336 if ($pkt_hr->{'dst_mac'}) { 1337 $pkt_hr->{'dst_mac'} =~ s/:$//; 1338 print STDERR "[+] dst mac addr: $pkt_hr->{'dst_mac'}\n" if $debug; 1339 } 1340 1341 unless ($pkt_hr->{'intf'} and $pkt_hr->{'chain'}) { 1342 print STDERR "[-] err packet: could not determine ", 1343 "interface and chain.\n" if $debug; 1344 return $PKT_ERROR; 1345 } 1346 1347 if (%ignore_interfaces) { 1348 for my $ignore_intf (keys %ignore_interfaces) { 1349 return $PKT_IGNORE if $pkt_hr->{'intf'} eq $ignore_intf; 1350 } 1351 } 1352 1353 ### get the syslog logging host for this packet 1354 if ($pkt_str =~ /(\S+)\s+kernel:/) { 1355 $pkt_hr->{'syslog_host'} = $1; 1356 } elsif ($pkt_str =~ /^\s*\S+\s+\S+\s+\S+\s+(\S+)/) { 1357 ### parsed packet from the beginning where the time portion 1358 ### of the syslog message is 1359 $pkt_hr->{'syslog_host'} = $1; 1360 } 1361 1362 ### try to extract a snort sid (generated by fwsnort) from 1363 ### the packet 1364 unless ($no_snort_sids) { 1365 if ($pkt_str =~ /$config{'SNORT_SID_STR'}(\d+)/) { 1366 $pkt_hr->{'fwsnort_sid'} = $1; 1367 } 1368 } 1369 1370 unless ($pkt_hr->{'fwsnort_sid'} or $config{'FW_SEARCH_ALL'} eq 'Y') { 1371 ### note that this is not _too_ strict since people 1372 ### have different ways of writing --log-prefix strings 1373 my $matched = 0; 1374 for my $fw_search_str (@fw_search) { 1375 $matched = 1 if $pkt_str =~ /$fw_search_str/; 1376 } 1377 return $PKT_IGNORE unless $matched; 1378 } 1379 1380 ### May 18 22:21:26 orthanc kernel: DROP IN=eth2 OUT= 1381 ### MAC=00:60:1d:23:d0:01:00:60:1d:23:d3:0e:08:00 SRC=192.168.20.25 1382 ### DST=192.168.20.1 LEN=60 TOS=0x10 PREC=0x00 TTL=64 ID=47300 DF 1383 ### PROTO=TCP SPT=34111 DPT=6345 WINDOW=5840 RES=0x00 SYN URGP=0 1384 1385 if ($pkt_str =~ /SRC=($ip_re)\s+DST=($ip_re)\s+LEN=(\d+)\s+TOS=(\S+) 1386 \s*.*\s+TTL=(\d+)\s+ID=(\d+)\s*.*\s+PROTO=TCP\s+ 1387 SPT=(\d+)\s+DPT=(\d+)\s.*\s*WINDOW=(\d+)\s+ 1388 (.*)\s+URGP=/x) { 1389 1390 ($pkt_hr->{'src'}, $pkt_hr->{'dst'}, $pkt_hr->{'ip_len'}, 1391 $pkt_hr->{'tos'}, $pkt_hr->{'ttl'}, $pkt_hr->{'ip_id'}, 1392 $pkt_hr->{'sp'}, $pkt_hr->{'dp'}, $pkt_hr->{'win'}, 1393 $pkt_hr->{'flags'}) 1394 = ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10); 1395 1396 ### the reserve bits are not reported by ulogd, but normal 1397 ### Netfilter syslog messages contain them. 1398 $pkt_hr->{'flags'} =~ s/\s*RES=\S+\s*//; 1399 1400 $pkt_hr->{'proto'} = 'tcp'; 1401 1402 ### default to NULL 1403 $pkt_hr->{'flags'} = 'NULL' unless $pkt_hr->{'flags'}; 1404 1405 if (not $pkt_hr->{'fwsnort_sid'} 1406 and $config{'IGNORE_CONNTRACK_BUG_PKTS'} eq 'Y' && 1407 ($pkt_hr->{'flags'} =~ /ACK/ || $pkt_hr->{'flags'} =~ /RST/)) { 1408 1409 ### $dp > 1024 && ($pkt_hr->{'flags'} =~ /ACK/ || 1410 1411 ### FIXME: ignore TCP packets that have the ACK or RST 1412 ### bits set (unless we matched a snort sid) since 1413 ### _usually_ we see these packets as a result of the 1414 ### Netfilter connection tracking bug. Also, note that 1415 ### no signatures make use of the RST flag. 1416 1417 print STDERR "[-] err packet: matched ACK or RST flag.\n" 1418 if $debug; 1419 return $PKT_IGNORE; 1420 } 1421 ### per page 595 of the Camel book, "if /blah1|blah2/" 1422 ### can be slower than "if /blah1/ || /blah2/ 1423 unless ($pkt_hr->{'flags'} !~ /WIN/ && 1424 $pkt_hr->{'flags'} =~ /ACK/ || 1425 $pkt_hr->{'flags'} =~ /SYN/ || 1426 $pkt_hr->{'flags'} =~ /RST/ || 1427 $pkt_hr->{'flags'} =~ /URG/ || 1428 $pkt_hr->{'flags'} =~ /PSH/ || 1429 $pkt_hr->{'flags'} =~ /FIN/ || 1430 $pkt_hr->{'flags'} eq 'NULL') { 1431 1432 print STDERR "[-] err packet: bad tcp flags.\n" if $debug; 1433 return $PKT_ERROR; 1434 } 1435 $pkt_hr->{'frag_bit'} = 1 if $pkt_str =~ /\sDF\s+PROTO/; 1436 1437 ### don't pickup IP options if --log-ip-options is used 1438 ### (they appear before the PROTO= field). 1439 if ($pkt_str =~ /URGP=\S+\s+OPT\s+\((\S+)\)/) { 1440 $pkt_hr->{'tcp_opt'} = $1; 1441 } 1442 1443 ### make sure we have a "reasonable" packet (note that nmap 1444 ### can scan port 0 and Netfilter can report this fact) 1445 unless ($pkt_hr->{'ip_len'} >= 0 and $pkt_hr->{'tos'} 1446 and $pkt_hr->{'ttl'} >= 0 and $pkt_hr->{'ip_id'} >= 0 1447 and $pkt_hr->{'proto'} and $pkt_hr->{'sp'} >= 0 1448 and $pkt_hr->{'dp'} >= 0 and $pkt_hr->{'win'} >= 0 1449 and $pkt_hr->{'flags'}) { 1450 return $PKT_ERROR; 1451 } 1452 1453 ### see if we need to ignore this packet based on the 1454 ### IGNORE_PORTS config keyword 1455 return $PKT_IGNORE if &check_ignore_port($pkt_hr->{'dp'}, 1456 $pkt_hr->{'proto'}); 1457 1458 if ($config{'ENABLE_DSHIELD_ALERTS'} eq 'Y' 1459 and not $benchmark 1460 and not $analyze_msgs) { 1461 1462 my $dflags = $pkt_hr->{'flags'}; 1463 $dflags =~ s/\s/,/g; 1464 1465 $pkt_hr->{'dshield_str'} = "$pkt_hr->{'src'}\t$pkt_hr->{'sp'}\t" . 1466 "$pkt_hr->{'dst'}\t$pkt_hr->{'dp'}\t$pkt_hr->{'proto'}\t" . 1467 "$dflags"; 1468 } 1469 1470 ### May 18 22:21:26 orthanc kernel: DROP IN=eth2 OUT= 1471 ### MAC=00:60:1d:23:d0:01:00:60:1d:23:d3:0e:08:00 1472 ### SRC=192.168.20.25 DST=192.168.20.1 LEN=28 TOS=0x00 PREC=0x00 1473 ### TTL=40 ID=47523 PROTO=UDP SPT=57339 DPT=305 LEN=8 1474 1475 } elsif ($pkt_str =~ /SRC=($ip_re)\s+DST=($ip_re)\s+LEN=(\d+)\s+TOS=(\S+) 1476 \s.*TTL=(\d+)\s+ID=(\d+)\s*.*\s+PROTO=UDP\s+ 1477 SPT=(\d+)\s+DPT=(\d+)\s+LEN=(\d+)/x) { 1478 1479 ($pkt_hr->{'src'}, $pkt_hr->{'dst'}, $pkt_hr->{'ip_len'}, 1480 $pkt_hr->{'tos'}, $pkt_hr->{'ttl'}, $pkt_hr->{'ip_id'}, 1481 $pkt_hr->{'sp'}, $pkt_hr->{'dp'}, $pkt_hr->{'udp_len'}) 1482 = ($1,$2,$3,$4,$5,$6,$7,$8,$9); 1483 1484 $pkt_hr->{'proto'} = 'udp'; 1485 1486 ### make sure we have a "reasonable" packet (note that nmap 1487 ### can scan port 0 and Netfilter can report this fact) 1488 unless ($pkt_hr->{'ip_len'} >= 0 1489 and $pkt_hr->{'tos'} and $pkt_hr->{'ttl'} >= 0 1490 and $pkt_hr->{'ip_id'} >= 0 and $pkt_hr->{'proto'} 1491 and $pkt_hr->{'sp'} >= 0 and $pkt_hr->{'dp'} >= 0 1492 and $pkt_hr->{'udp_len'} >= 0) { 1493 1494 return $PKT_ERROR; 1495 } 1496 1497 ### see if we need to ignore this packet based on the 1498 ### IGNORE_PROTOCOLS config keyword. 1499 return $PKT_IGNORE if &check_ignore_proto($pkt_hr->{'proto'}); 1500 1501 ### see if we need to ignore this packet based on the 1502 ### IGNORE_PORTS config keyword 1503 return $PKT_IGNORE if &check_ignore_port($pkt_hr->{'dp'}, 1504 $pkt_hr->{'proto'}); 1505 1506 if ($config{'ENABLE_DSHIELD_ALERTS'} eq 'Y' 1507 and not $benchmark 1508 and not $analyze_msgs) { 1509 1510 $pkt_hr->{'dshield_str'} = "$pkt_hr->{'src'}\t$pkt_hr->{'sp'}\t" . 1511 "$pkt_hr->{'dst'}\t$pkt_hr->{'dp'}\t$pkt_hr->{'proto'}"; 1512 } 1513 1514 ### Nov 27 15:45:51 orthanc kernel: DROP IN=eth1 OUT= MAC=00:a0:cc:e2:1f:f2:00: 1515 ### 20:78:10:70:e7:08:00 SRC=192.168.10.20 DST=192.168.10.1 LEN=84 TOS=0x00 1516 ### PREC=0x00 TTL=64 ID=0 DF PROTO=ICMP TYPE=8 CODE=0 ID=61055 SEQ=256 1517 1518 } elsif ($pkt_str =~ /SRC=($ip_re)\s+DST=($ip_re)\s+LEN=(\d+).* 1519 TTL=(\d+)\s+ID=(\d+).*PROTO=ICMP\s+TYPE=(\d+)\s+ 1520 CODE=(\d+)\s+ID=(\d+)\s+SEQ=(\d+)/x) { 1521 1522 ($pkt_hr->{'src'}, $pkt_hr->{'dst'}, $pkt_hr->{'ip_len'}, 1523 $pkt_hr->{'ttl'}, $pkt_hr->{'ip_id'}, $pkt_hr->{'itype'}, 1524
