/
sd2nbe
237 lines (193 loc) · 7.47 KB
/
sd2nbe
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
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
#!/usr/bin/perl -wT
#
# ----------------------------------------------------------------------
# sd2nbe
#
# Written by George A. Theall, theall@tifaware.com
#
# Copyright (c) 2004-2016, George A. Theall. All rights reserved.
#
# This module is free software; you can redistribute it and/or modify
# it under the same terms as Perl itself.
# ----------------------------------------------------------------------
=head1 NAME
sd2nbe - convert Nessus session data to NBE format.
=head1 SYNOPSIS
# Convert a session from user 'theall' run on Jan 11 at 16:50 to NBE.
sd2nbe /usr/local/var/nessus/users/theall/sessions/20040111-165004-data > session.nbe
# Convert the same session and display debugging messages.
sd2nbe -d /usr/local/var/nessus/users/theall/sessions/20040111-165004-data > session.nbe
=head1 DESCRIPTION
Nessus offers a optional feature known as session saving. [NB: In
versions of Nessus before 2.2.0, you had to enable this with the
C<--enable_save_sessions> option when running C<nessus-core/configure>.]
It's intended as a means to recover results of interrupted scans (for
example, due to a power outage or client machine crash), although it can
also be used as a more general way of saving results. Provided that the
server was configured to support session saving and that the user
elected to save the session when submitting a scan, session data will be
saved in a directory such as
C</usr/local/var/nessus/users/${user}/sessions>.
To recover results, a user connects to the Nessus server and restores
the session. The server then replays the session, scanning any hosts
that were missed or unfinished from before, and displays the results.
One drawback to restoring a session, though, is the length of time the
client takes to replay it. Even if the scan was not interrupted,
replaying the session may approach the time it took to do the scan
originally.
B<sd2nbe> takes an alternative approach -- it reads session data
directly and outputs results in a format known as C<NBE> (C<Nessus
BackEnd>). While the format may not be especially readable, its use
offers several attractive features:
o It can be fed into the unix-based nessus client and converted
to a variety of other formats; eg, HTML, text, XML, etc.
o It can be easily filtered so as to limit reports, for example,
to certain hosts or plugins.
o It can be merged with other NBE output simply by concatenating
the sources.
B<sd2nbe> is written in Perl. It should work on any system with Perl 5
or better. It also requires the Perl modules C<Carp> and
C<Getopt::Long>. If your system does not have these modules installed
already, visit CPAN (L<http://search.cpan.org/>) for help.
=head1 KNOWN BUGS AND CAVEATS
Currently, I am not aware of any bugs in this script.
Don't convert at the same time multiple session data files that reflect
scans of the same host(s).
Warnings are issued for hosts that were not scanned completely.
However, the script does not check whether the scan itself is complete
(ie, all targets were tested).
=head1 DIAGNOSTICS
If an input can not be parsed properly, a warning message is generated
and the line is skipped.
Warnings are issued for hosts that were not scanned completely.
=head1 SEE ALSO
L<nessus(1)>,
C<nessus-core/doc/nbe_file_format.txt>,
L<https://github.com/gatheall/sd2nbe>
=cut
############################################################################
# Make sure we have access to the required modules.
use 5;
use strict;
use Carp;
use Getopt::Long;
############################################################################
# Initialize variables.
$| = 1;
my $DEBUG = 0;
my %msg_types = (
'NOTE' => 'Security Note',
'INFO' => 'Security Warning',
'HOLE' => 'Security Hole',
);
############################################################################
# Process commandline arguments.
my %options = (
'debug' => \$DEBUG,
);
GetOptions(
\%options,
'debug|d!',
'help|h|?!',
) or $options{help} = 1;
$0 =~ s/^.+\///;
if ($options{help}) {
warn "\n",
"Usage: $0 [options] [session-data-file]\n",
"\n",
"Options:\n",
" -?, -h, --help Display this help and exit.\n",
" -d, --debug Display copious debugging messages while\n",
" converting session data.\n";
exit(9);
}
############################################################################
warn "debug: reading session data.\n" if $DEBUG;
my(@hosts, $line, %scans);
while (<>) {
chomp;
++$line;
warn "debug: reading >>$_<<.\n" if $DEBUG;
# Extract fields in messages from the server and limit attention
# to a few message types.
#
# nb: session data is basically a collection of NTP messages.
next unless (/^SERVER <\|> (.+) <\|> SERVER$/);
my($type, @fields) = split(/ <\|> /, $1);
warn "debug: message type '$type'.\n" if $DEBUG;
next unless (grep($type eq $_, ('TIME', 'PORT', 'NOTE', 'INFO', 'HOLE')));
# Parse and convert messages to category|subnet|host|info
# fields used w/ NBE.
#
my($category, $subnet, $host, $info);
if ($type eq 'TIME') {
$category = 'timestamps';
my($action, $time);
unless ($action = shift(@fields) and $time = pop(@fields)) {
warn "NTP error in line $line - not enough fields!\n";
next;
}
$action = lc($action);
warn "debug: action '$action'.\n" if $DEBUG;
if ($action eq 'host_start' or $action eq 'host_end') {
unless ($host = shift(@fields)) {
warn "NTP error in line $line - no host field!\n";
next;
}
if ($action eq 'host_start') {
$scans{$host}++;
push(@hosts, $host);
}
else {
$scans{$host}--;
}
}
$info = join('|', $action, $time);
}
else {
$category = 'results';
unless ($host = shift(@fields)) {
warn "NTP error in line $line - no host field!\n";
next;
}
# Determine "subnet".
if ($host =~ /^([0-9a-f]{2}(\.|$)){6}$/i) { # MAC address (w/ '.' as delimiter)
# nb: use first three bytes (manufacturer id).
$subnet = substr($host, 0, 8);
}
elsif ($host =~ /^((\d{1,3})(\.|$)){4}$/) { # IP address
# nb: assume /24.
($subnet = $host) =~ s/\.\d{1,3}\.?$//;
}
else { # fqdn
# nb: generally this yields a subdomain / domain.
($subnet = $host) =~ s/^[^.]+\.//;
}
my $port;
unless ($port = shift(@fields)) {
warn "NTP error in line $line - no port field!\n";
next;
}
if ($type eq 'PORT') {
$info = $port;
}
else {
my($plugin, $report);
unless ($plugin = pop(@fields) and $report = shift(@fields)) {
warn "NTP error in line $line - not enough fields!\n";
next;
}
$info = join('|', $port, $plugin, $msg_types{$type}, $report);
}
}
# Print line in NBE format.
print join('|', $category, ($subnet || ''), $host, $info), "\n";
}
# Warn about unscanned hosts.
if (grep($scans{$_}, @hosts)) {
warn "\n" .
"*** Scans of the Following Hosts are Incomplete: ***\n";
foreach (@hosts) {
warn " $_\n" if $scans{$_};
}
}