1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
| #!/usr/bin/perl
use File::Copy;
use Nmap::Parser;
use Net::DNS::Sendmail;
use warnings;
use strict;
#config begin
my $nmap_path = "/usr/bin/nmap"; #path to nmap binary
my $nmap_args = "-T5 -F -sV"; #nmap arguments (accepts multiple), do not specify any output arguments!
my $ips = ""; #networks to scan, accepts standard nmap input
my $save_as = "/root/parser-cache.xml"; #path to storage file - this file is used for comparative scans, so it's best not to place it in /tmp.
my $email_from_address = ''; #from address to use when sending mail - should probably exist to avoid sender callouts
my $email_to_address = ''; #address you want results delivered to
my $email_sender_domain = ''; #domain to masquerade as - useful in cases where remote mta checks helo.
my $email_subject = "Nmap Network Scan Results"; #subject for email.
my $debug = "false"; #enables some output if you need to test auto on the cli - i.e. why mail isn't being sent.
#config end
my $np = new Nmap::Parser;
my $old = new Nmap::Parser;
my $curr = new Nmap::Parser;
my $tempstore = "/tmp/parser-cache.tmp";
my $emailfile = "/tmp/nmap-notify";
my $newmachines = 0;
my $auto = (lc($ARGV[0]) eq "auto");
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time);
sub debug
{
print "@_\n" if($debug eq "true");
}
sub print_twice
{
print "@_";
print EMAIL "@_";
}
sub create_files
{
foreach (@_)
{
if(!-e $_)
{
print "$_ does not exist... creating\n" if(!$auto);
open(TEMP,">$_") or die "Can't create $_: $!";
close(TEMP) or die "Couldn't close $_: $!";
}
}
}
create_files($save_as,$tempstore);
if($auto)
{
open(EMAIL,">$emailfile") or die "Can't create $emailfile: $!";
print EMAIL "Nmap Scan Report. Hosts/Ports changed!\nGenerated on: ";
printf EMAIL "%4d-%02d-%02d %02d:%02d:%02d\n\n",$year+1900,$mon+1,$mday,$hour,$min,$sec;
print EMAIL "--------BEGIN--------\n";
}
if($auto)
{
debug("Running auto mode");
if(-s $save_as)
{
$old->parsefile($save_as); #load previous state
$curr->cache_scan($tempstore); #set temporary file to store xml after we're done
$curr->parsescan($nmap_path, $nmap_args, $ips); #scan current hosts
for my $ip ($curr->get_ips('up'))
{
my $ip_old = $old->get_host($ip);
my $ip_curr = $curr->get_host($ip);
my @alltcp = $ip_curr->tcp_open_ports();
my $numtcp = $#alltcp + 1;
if(!$ip_old)
{
print_twice("$ip is a new host with".$numtcp."ports open/filtered.\n");
++$newmachines;
}
}
print_twice("\n") if($newmachines != 0);
for my $ip ($curr->get_ips('up'))
{
my $ip_old = $old->get_host($ip);
my $ip_curr = $curr->get_host($ip);
my %port = ();
if($ip_old)
{
my @tcpdiff = grep { $port{$_} < 2}
(map {$port{$_}++; $_}
( $ip_curr->tcp_open_ports , $ip_old->tcp_open_ports));
if(scalar @tcpdiff)
{
print_twice("$ip has these new ports open: ".join(',',@tcpdiff)."\n");
for (@tcpdiff)
{
print_twice("\t"."$_ might be ",$ip_curr->tcp_service($_)->name,"\n");
}
print_twice("\n");
}
}else{
my @tcp = (map {$port{$_}++; $_}
( $ip_curr->tcp_ports('open') ));
if(scalar @tcp)
{
print_twice("$ip has these ports open: ".join(',',@tcp)."\n");
for (@tcp)
{
print_twice("\t"."$_ might be ",$ip_curr->tcp_service($_)->name,"\n");
}
print_twice("\n");
}
}
}
}else{
$curr->cache_scan($tempstore); #set temporary file to store xml after we're done
$curr->parsescan($nmap_path, $nmap_args, $ips); #scan current hosts
for my $ip ($curr->get_ips('up'))
{
my $ip_curr = $curr->get_host($ip);
my @alltcp = $ip_curr->tcp_open_ports();
my $numtcp = $#alltcp + 1;
print_twice("$ip is a new host with ".$numtcp." ports open/filtered.\n");
++$newmachines;
}
print_twice("\n") if($newmachines != 0);
for my $ip ($curr->get_ips('up'))
{
my $ip_curr = $curr->get_host($ip);
my %port = ();
my @tcp = (map {$port{$_}++; $_}
( $ip_curr->tcp_ports('open') ));
if(scalar @tcp)
{
print_twice("$ip has these ports open: ".join(',',@tcp)."\n");
for (@tcp)
{
print_twice("\t"."$_ might be ",$ip_curr->tcp_service($_)->name,"\n");
}
print_twice("\n");
}
}
}
move($tempstore, $save_as);
print EMAIL "--------END--------";
close EMAIL;
open(EMAIL,"<$emailfile") or die "Can't read $emailfile: $!";
my $mail = Net::DNS::Sendmail->new();
$mail->verbose() if($debug eq "true");
$mail->senderdomain( "$email_sender_domain" );
$mail->to( "$email_to_address" );
$mail->from( "$email_from_address" );
$mail->subject( "$email_subject" );
while () { $mail->data($_); }
$mail->sendmail() if($newmachines != 0);
debug("mail sent");
close EMAIL;
} else {
#Unsure whether or not to move the output of this into $save_as... it may
#be that someone runs this, and doesn't take care of the output, in which
#case, we still want to be notified that night.
$np->cache_scan($save_as);
$np->parsescan($nmap_path, $nmap_args, $ips);
print "IP,OPEN TCP PORTS\n";
for my $ip ($np->get_ips('up'))
{
my $ip_check = $np->get_host($ip);
my %port = ();
my @tcp = (map {$port{$_}++; $_}
( $ip_check->tcp_ports('open') ));
print "$ip,".join(' ',@tcp)."\n" if(scalar @tcp);
}
} |