Hi David,
I attach you my code, you can run the diff between the original and my
code and check the differences ;) I had the same problem as you.
Regards,
El 14/09/2014 9:24, David Uihlein escribió:
I'm having trouble getting OpenVas to work. I've updated the code
changing the escalators to alerts and it's creating the tasks, but the
responses are coming back different than what iPF expects, so I get a
warning on target creation even though it gets created
here's my log
Sep 13 18:09:51 pfcmd.pl(32545) INFO: Instantiate a new vulnerability
scanning engine object of type pf::scan::openvas.
(pf::scan::instantiate_scan_engine)
Sep 13 18:09:51 pfcmd.pl(32545) DEBUG: Instantiating a new
pf::scan::openvas scanning object (pf::scan::openvas::new)
Sep 13 18:09:51 pfcmd.pl(32545) INFO: Creating a new scan target named
1410649791760d84 for host 192.168.70.11 (pf::scan::openvas::createTarget)
Sep 13 18:09:51 pfcmd.pl(32545) TRACE: Scan target creation command:
omp -h 127.0.0.1 -p 9390 -u admin -w pass -X
'<create_target><name>1410649791760d84</name><hosts>192.168.70.11</hosts></create_target>'
(pf::scan::openvas::createTarget)
Sep 13 18:09:53 pfcmd.pl(32545) TRACE: Scan target creation output:
<create_target_response id="6b98a0e0-51bb-406f-8936-e64adafebec6"
status_text="OK, resource created"
status="201"></create_target_response> (pf::scan::openvas::createTarget)
Sep 13 18:09:53 pfcmd.pl(32545) WARN: There was an error creating scan
target named 1410649791760d84, here's the output:
<create_target_response id="6b98a0e0-51bb-406f-8936-e64adafebec6"
status_text="OK, resource created"
status="201"></create_target_response> (pf::scan::openvas::createTarget)
Can you tell me what I'm missing, or send me the changes you made for
scan.pm and openvas.pm? (I assume those are the only 2 files that
need to be modified)
No se encontraron virus en este mensaje.
Comprobado por AVG - www.avg.com <http://www.avg.com>
Versión: 2013.0.3485 / Base de datos de virus: 4015/8207 - Fecha de
publicación: 09/13/14
package pf::scan::openvas;
=head1 NAME
pf::scan::openvas
=cut
=head1 DESCRIPTION
pf::scan::openvas is a module to add OpenVAS scanning option.
=cut
use strict;
use warnings;
use Log::Log4perl;
use MIME::Base64;
use Readonly;
use base ('pf::scan');
use pf::config;
use pf::util;
Readonly our $RESPONSE_OK => 200;
Readonly our $RESPONSE_RESOURCE_CREATED => 201;
Readonly our $RESPONSE_REQUEST_SUBMITTED => 202;
=head1 METHODS
=over
=item createAlert
Create an alert which will trigger an action on the OpenVAS server once the
scan will finish
=cut
sub createAlert {
my ( $this ) = @_;
my $logger = Log::Log4perl::get_logger(__PACKAGE__);
my $name = $this->{_id};
my $callback = $this->_generateCallback();
my $command = _get_alert_string($name, $callback);
$logger->info("Creating a new scan alert named $name");
my $cmd = "omp -h $this->{_host} -p $this->{_port} -u $this->{_user} -w
$this->{_pass} -X '$command'";
$logger->trace("Scan alert creation command: $cmd");
my $output = pf_run($cmd);
chomp($output);
$logger->trace("Scan alert creation output: $output");
# Fetch response status and alert id
my ($alert_id, $response);
if ($output =~ /^<create_alert_response/) {
if ($output =~ /id="([a-zA-Z0-9\-]+)"/) {
$alert_id=$1;
}
if ($output =~ /status="([0-9]+)"/) {
$response=$1
}
}
# Scan alert successfully created
if ( defined($response) && $response eq $RESPONSE_RESOURCE_CREATED ) {
$logger->info("Scan alert named $name successfully created with id:
$alert_id");
$this->{'_alertId'} = $alert_id;
return $TRUE;
}
$logger->warn("There was an error creating scan alert named $name, here's
the output: $output");
return;
}
=item createTarget
Create a target (a target is a host to scan)
=cut
sub createTarget {
my ( $this ) = @_;
my $logger = Log::Log4perl::get_logger(__PACKAGE__);
my $name = $this->{_id};
my $target_host = $this->{_scanIp};
my $command =
"<create_target><name>$name</name><hosts>$target_host</hosts></create_target>";
$logger->info("Creating a new scan target named $name for host
$target_host");
my $cmd = "omp -h $this->{_host} -p $this->{_port} -u $this->{_user} -w
$this->{_pass} -X '$command'";
$logger->trace("Scan target creation command: $cmd");
my $output = pf_run($cmd);
chomp($output);
$logger->trace("Scan target creation output: $output");
# Fetch response status and target id
my ($target_id, $response);
if ($output =~ /^<create_target_response/) {
if ($output =~ /id="([a-zA-Z0-9\-]+)"/) {
$target_id=$1;
}
if ($output =~ /status="([0-9]+)"/) {
$response=$1
}
}
# Scan target successfully created
if ( defined($response) && $response eq $RESPONSE_RESOURCE_CREATED ) {
$logger->info("Scan target named $name successfully created with id:
$target_id");
$this->{'_targetId'} = $target_id;
return $TRUE;
}
$logger->warn("There was an error creating scan target named $name, here's
the output: $output");
return;
}
=item createTask
Create a task (a task is a scan) with the existing config id and previously
created target and alert
=cut
sub createTask {
my ( $this ) = @_;
my $logger = Log::Log4perl::get_logger(__PACKAGE__);
my $name = $this->{_id};
$logger->info("Creating a new scan task named $name");
my $command = _get_task_string(
$name, $Config{'scan'}{'openvas_configid'}, $this->{_targetId},
$this->{_alertId}
);
my $cmd = "omp -h $this->{_host} -p $this->{_port} -u $this->{_user} -w
$this->{_pass} -X '$command'";
$logger->trace("Scan task creation command: $cmd");
my $output = pf_run($cmd);
chomp($output);
# Fetch response status and task id
my ($task_id, $response);
if ($output =~ /^<create_task_response/) {
if ($output =~ /id="([a-zA-Z0-9\-]+)"/) {
$task_id=$1;
}
if ($output =~ /status="([0-9]+)"/) {
$response=$1
}
}
# Scan task successfully created
if ( defined($response) && $response eq $RESPONSE_RESOURCE_CREATED ) {
$logger->info("Scan task named $name successfully created with id:
$task_id");
$this->{'_taskId'} = $task_id;
return $TRUE;
}
$logger->warn("There was an error creating scan task named $name, here's
the output: $output");
return;
}
=item processReport
Retrieve the report associated with a task.
When retrieving a report in other format than XML, we received the report in
base64 encoding.
Report processing's duty is to ensure that the proper violation will be
triggered.
=cut
sub processReport {
my ( $this ) = @_;
my $logger = Log::Log4perl::get_logger(__PACKAGE__);
my $name = $this->{_id};
my $report_id = $this->{_reportId};
my $report_format_id = $Config{'scan'}{'openvas_reportformatid'};
my $command = "<get_reports report_id=\"$report_id\"
format_id=\"$report_format_id\"/>";
$logger->info("Getting the scan report for the finished scan task named
$name");
my $cmd = "omp -h $this->{_host} -p $this->{_port} -u $this->{_user} -w
$this->{_pass} -X '$command'";
$logger->trace("Report fetching command: $cmd");
my $output = pf_run($cmd);
chomp($output);
$logger->trace("Report fetching output: $output");
$logger->warn("Report fetching output: $output");
# Fetch response status and report
my ($raw_report, $response);
if ($output =~ /^<get_reports_response/) {
if ($output =~ /content_type="text\/plain">([a-zA-Z0-9\=]+)</) {
$raw_report=$1;
}
if ($output =~ /status="([0-9]+)"/) {
$response=$1
}
}
# Scan report successfully fetched
if ( defined($response) && $response eq $RESPONSE_OK &&
defined($raw_report) ) {
$logger->info("Report id $report_id successfully fetched for task named
$name");
$this->{_report} = decode_base64($raw_report); # we need to decode
the base64 report
# We need to manipulate the scan report.
# Each line of the scan report is pushed into an arrayref
$this->{'_report'} = [ split("\n", $this->{'_report'}) ];
pf::scan::parse_scan_report($this);
return $TRUE;
}
$logger->warn("There was an error fetching the scan report for the task
named $name, here's the output: $output");
return;
}
=item new
Create a new Openvas scanning object with the required attributes
=cut
sub new {
my ( $class, %data ) = @_;
my $logger = Log::Log4perl::get_logger(__PACKAGE__);
$logger->debug("Instantiating a new pf::scan::openvas scanning object");
my $this = bless {
'_id' => undef,
'_host' => $Config{'scan'}{'host'},
'_port' => undef,
'_user' => $Config{'scan'}{'user'},
'_pass' => $Config{'scan'}{'pass'},
'_scanIp' => undef,
'_scanMac' => undef,
'_report' => undef,
'_configId' => undef,
'_reportFormatId' => undef,
'_targetId' => undef,
'_alertId' => undef,
'_taskId' => undef,
'_reportId' => undef,
'_status' => undef,
'_type' => undef,
}, $class;
foreach my $value ( keys %data ) {
$this->{'_' . $value} = $data{$value};
}
# OpenVAS specific attributes
$this->{_port} = $Config{'scan'}{'openvas_port'};
$this->{_configId} = $Config{'scan'}{'openvas_configid'};
$this->{_reportFormatId} = $Config{'scan'}{'openvas_reportformatid'};
return $this;
}
=item startScan
That's where we use all of these method to run a scan
=cut
sub startScan {
my ( $this ) = @_;
my $logger = Log::Log4perl::get_logger(__PACKAGE__);
$this->createTarget();
$this->createAlert();
$this->createTask();
$this->startTask();
}
=item startTask
Start a scanning task with the previously created target and alert
=cut
sub startTask {
my ( $this ) = @_;
my $logger = Log::Log4perl::get_logger(__PACKAGE__);
my $name = $this->{_id};
my $task_id = $this->{_taskId};
my $command = "<start_task task_id=\"$task_id\"/>";
$logger->info("Starting scan task named $name");
my $cmd = "omp -h $this->{_host} -p $this->{_port} -u $this->{_user} -w
$this->{_pass} -X '$command'";
$logger->trace("Scan task starting command: $cmd");
my $output = pf_run($cmd);
chomp($output);
$logger->trace("Scan task starting output: $output");
# Fetch response status and report id
my ($report_id, $response);
if ($output =~ /^<start_task_response/) {
if ($output =~ /<report_id>([a-zA-Z0-9\-]+)</) {
$report_id=$1;
}
if ($output =~ /status="([0-9]+)"/) {
$response=$1
}
}
# Scan task successfully started
if ( defined($response) && $response eq $RESPONSE_REQUEST_SUBMITTED ) {
$logger->info("Scan task named $name successfully started");
$this->{'_reportId'} = $report_id;
$this->{'_status'} = $pf::scan::STATUS_STARTED;
$this->statusReportSyncToDb();
return $TRUE;
}
$logger->warn("There was an error starting the scan task named $name,
here's the output: $output");
return;
}
=item _generateCallback
Escalator callback needs to be different if we are running OpenVAS locally or
remotely.
Local: plain HTTP on loopback (127.0.0.1)
Remote: HTTPS with fully qualified domain name on admin interface
=cut
sub _generateCallback {
my ( $this ) = @_;
my $logger = Log::Log4perl::get_logger(__PACKAGE__);
my $name = $this->{'_id'};
my $callback = "<method>HTTP Get<data>";
if ($this->{'_host'} eq '127.0.0.1') {
$callback .= "http://127.0.0.1/scan/scan_fetch_report.cgi?scanid=$name";
}
else {
$callback .=
"https://$Config{general}{hostname}.$Config{general}{domain}:$Config{ports}{admin}/scan/scan_fetch_report.cgi?scanid=$name";
}
$callback .= "<name>URL</name></data></method>";
$logger->debug("Generated OpenVAS callback is: $callback");
return $callback;
}
=back
=head1 SUBROUTINES
=over
=item _get_alert_string
create_alert string creation.
=cut
sub _get_alert_string {
my ($name, $callback) = @_;
return <<"EOF";
<create_alert>
<name>$name</name>
<condition>Always</condition>
<event>Task run status changed<data>Done<name>status</name></data></event>
$callback
</create_alert>
EOF
}
=item _get_task_string
create_task string creation.
=cut
sub _get_task_string {
my ($name, $config_id, $target_id, $alert_id) = @_;
return <<"EOF";
<create_task>
<name>$name</name>
<config id=\"$config_id\"/>
<target id=\"$target_id\"/>
<alert id=\"$alert_id\"/>
</create_task>
EOF
}
=back
=head1 AUTHOR
Inverse inc. <i...@inverse.ca>
=head1 COPYRIGHT
Copyright (C) 2005-2013 Inverse inc.
=head1 LICENSE
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
USA.
=cut
1;
package pf::scan;
=head1 NAME
pf::scan - Module that performs the vulnerability scan operations
=cut
=head1 DESCRIPTION
pf::scan contains the general functions required to lauch and complete a
vulnerability scan on a host
=cut
use strict;
use warnings;
use Log::Log4perl;
use Parse::Nessus::NBE;
use Readonly;
use Try::Tiny;
use overload '""' => "toString";
BEGIN {
use Exporter ();
our (@ISA, @EXPORT, @EXPORT_OK);
@ISA = qw(Exporter);
@EXPORT = qw(run_scan $SCAN_VID $scan_db_prepared scan_db_prepare);
@EXPORT_OK = qw(scan_insert_sql scan_select_sql scan_update_status_sql);
}
use pf::config;
use pf::db;
use pf::iplog qw(ip2mac);
use pf::scan::nessus;
use pf::scan::openvas;
use pf::util;
use pf::violation qw(violation_exist_open violation_trigger violation_modify);
Readonly our $SCAN_VID => 1200001;
Readonly our $SEVERITY_HOLE => 1;
Readonly our $SEVERITY_WARNING => 2;
Readonly our $SEVERITY_INFO => 3;
Readonly our $STATUS_NEW => 'new';
Readonly our $STATUS_STARTED => 'started';
Readonly our $STATUS_CLOSED => 'closed';
# DATABASE HANDLING
use constant SCAN => 'scan';
our $scan_db_prepared = 0;
our $scan_statements = {};
sub scan_db_prepare {
my $logger = Log::Log4perl::get_logger(__PACKAGE__);
$logger->debug("Preparing database statements.");
$scan_statements->{'scan_insert_sql'} = get_db_handle()->prepare(qq[
INSERT INTO scan (
id, ip, mac, type, start_date, update_date, status, report_id
) VALUES (
?, ?, ?, ?, ?, ?, ?, ?
)
]);
$scan_statements->{'scan_select_sql'} = get_db_handle()->prepare(qq[
SELECT id, ip, mac, type, start_date, update_date, status, report_id
FROM scan
WHERE id = ?
]);
$scan_statements->{'scan_update_sql'} = get_db_handle()->prepare(qq[
UPDATE scan SET
status = ?, report_id =?
WHERE id = ?
]);
$scan_db_prepared = 1;
return 1;
}
=head1 SUBROUTINES
=over
=item instantiate_scan_engine
Instantiate the correct vulnerability scanning engine with attributes
=cut
sub instantiate_scan_engine {
my ( $type, %scan_attributes ) = @_;
my $logger = Log::Log4perl::get_logger(__PACKAGE__);
my $scan_engine = 'pf::scan::' . $type;
$logger->info("Instantiate a new vulnerability scanning engine object of
type $scan_engine.");
$scan_engine = untaint_chain($scan_engine);
try {
# try to import module and re-throw the error to catch if there's one
eval "$scan_engine->require()";
die($@) if ($@);
} catch {
chomp($_);
$logger->error("Initialization of scan engine $scan_engine failed: $_");
};
return $scan_engine->new(%scan_attributes);
}
=item parse_scan_report
Parse a scan report from the scan object and trigger violations if needed
=cut
sub parse_scan_report {
my ( $scan ) = @_;
my $logger = Log::Log4perl::get_logger(__PACKAGE__);
$logger->debug("Scan report to analyze. Scan id: $scan");
my $scan_report = $scan->getReport();
my @count_vulns = (
Parse::Nessus::NBE::nstatvulns(@$scan_report, $SEVERITY_HOLE),
Parse::Nessus::NBE::nstatvulns(@$scan_report, $SEVERITY_WARNING),
Parse::Nessus::NBE::nstatvulns(@$scan_report, $SEVERITY_INFO),
);
# FIXME we shouldn't poke directly into the scan object, we should rely on
accessors
# we are slicing out the parameters out of the $scan objectified hashref
my ($mac, $ip, $type) = @{$scan}{qw(_scanMac _scanIp _type)};
# Trigger a violation for each vulnerability
my $failed_scan = 0;
foreach my $current_vuln (@count_vulns) {
# Parse nstatvulns format
my ( $trigger_id, $number ) = split(/\|/, $current_vuln);
$logger->info("Calling violation_trigger for ip: $ip, mac: $mac, type:
$type, trigger: $trigger_id");
my $violation_added = violation_trigger($mac, $trigger_id, $type);
# If a violation has been added, consider the scan failed
if ( $violation_added ) {
$failed_scan = 1;
}
}
# If scan is requested because of registration scanning
# Clear scan violation if the host didn't generate any violation
# Otherwise we keep the violation and clear the ticket_ref (so we can
re-scan once he remediates)
# If the scan came from elsewhere
# Do nothing
# The way we accomplish the above workflow is to differentiate by checking
if special violation exists or not
if ( my $violation_id = violation_exist_open($mac, $SCAN_VID) ) {
$logger->trace("Scan is completed and there is an open scan violation.
We have something to do!");
# We passed the scan so we can close the scan violation
if ( !$failed_scan ) {
my $cmd = $bin_dir . "/pfcmd manage vclose $mac $SCAN_VID";
$logger->info("Calling $cmd");
my $grace = pf_run("$cmd");
# FIXME shouldn't we focus on return code instead of output? pretty
sure this is broken
if ( $grace == -1 ) {
$logger->warn("Problem trying to close scan violation");
return;
}
# Scan completed but a violation has been found
# HACK: we empty the violation's ticket_ref field which we use to track
if scan is in progress or not
} else {
$logger->debug("Modifying violation id $violation_id to empty its
ticket_ref field");
violation_modify($violation_id, (ticket_ref => ""));
}
}
$scan->setStatus($STATUS_CLOSED);
$scan->statusReportSyncToDb();
}
=item retrieve_scan
Retrieve a scan object populated from the database using the scan id
=cut
sub retrieve_scan {
my ( $scan_id ) = @_;
my $logger = Log::Log4perl::get_logger(__PACKAGE__);
my $query = db_query_execute(SCAN, $scan_statements, 'scan_select_sql',
$scan_id) || return 0;
my $scan_infos = $query->fetchrow_hashref();
$query->finish();
if (!defined($scan_infos) || $scan_infos->{'id'} ne $scan_id) {
$logger->warn("Invalid scan object requested");
return;
}
my %scan_args;
# here we map parameters expected by the object (left) with fields of the
database (right)
@scan_args{qw(id scanIp scanMac reportId status type)} = @$scan_infos{qw(id
ip mac report_id status type)};
my $scan = instantiate_scan_engine($scan_infos->{'type'}, %scan_args);
return $scan;
}
=item run_scan
Prepare the scan attributes, call the engine instantiation and start the scan
=cut
sub run_scan {
my ( $host_ip ) = @_;
my $logger = Log::Log4perl::get_logger(__PACKAGE__);
$host_ip =~ s/\//\\/g; # escape slashes
$host_ip = clean_ip($host_ip); # untainting ip
# Resolve mac address
my $host_mac = ip2mac($host_ip);
if ( !$host_mac ) {
$logger->warn("Unable to find MAC address for the scanned host
$host_ip. Scan aborted.");
return;
}
# Preparing the scan attributes
my $epoch = time;
my $date = POSIX::strftime("%Y-%m-%d %H:%M:%S", localtime($epoch));
my $id = generate_id($epoch, $host_mac);
my $type = lc($Config{'scan'}{'engine'});
# Check the scan engine
# If set to "none" we abort the scan
if ( $type eq "none" ) {
return;
}
my %scan_attributes = (
id => $id,
scanIp => $host_ip,
scanMac => $host_mac,
type => $type,
);
db_query_execute(SCAN, $scan_statements, 'scan_insert_sql',
$id, $host_ip, $host_mac, $type, $date, '0000-00-00 00:00:00',
$STATUS_NEW, 'NULL'
) || return 0;
# Instantiate the new scan object
my $scan = instantiate_scan_engine($type, %scan_attributes);
# Start the scan
# IBB Patch 2014-08-09 - Juan
my $error_scan = $scan->startScan();
# Hum ... somethings wrong in the scan ?
if ( !$error_scan ) {
my $cmd = $bin_dir . "/pfcmd manage vclose $host_mac $SCAN_VID";
$logger->info("Calling $cmd");
my $grace = pf_run("$cmd");
# FIXME shouldn't we focus on return code instead of output? pretty
sure this is broken
if ( $grace == -1 ) {
$logger->warn("Problem trying to close scan violation");
}
}
}
=back
=head1 METHODS
We are also a lean base class for pf::scan::*.
=over
=item statusReportSyncToDb
Update the status and reportId of the scan in the database.
=cut
sub statusReportSyncToDb {
my ( $self ) = @_;
my $logger = Log::Log4perl::get_logger(__PACKAGE__);
db_query_execute(SCAN, $scan_statements, 'scan_update_sql',
$self->{'_status'}, $self->{'_reportId'}, $self->{'_id'}
) || return 0;
return $TRUE;
}
=item isNotExpired
Returns true or false based on wether scan is considered expired or not.
This basically means can we still apply the result of a scan to a node or was
it already applied.
=cut
sub isNotExpired {
my ($self) = @_;
return ($self->{'_status'} eq $STATUS_STARTED);
}
sub setStatus {
my ($self, $status) = @_;
$self->{'_status'} = $status;
return $TRUE;
}
sub getReport {
my ($self) = @_;
return $self->{'_report'};
}
sub toString {
my ($self) = @_;
return $self->{'_id'};
}
=back
=head1 AUTHOR
Inverse inc. <i...@inverse.ca>
=head1 COPYRIGHT
Copyright (C) 2005-2013 Inverse inc.
=head1 LICENSE
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
USA.
=cut
1;
------------------------------------------------------------------------------
Want excitement?
Manually upgrade your production database.
When you want reliability, choose Perforce
Perforce version control. Predictably reliable.
http://pubads.g.doubleclick.net/gampad/clk?id=157508191&iu=/4140/ostg.clktrk
_______________________________________________
PacketFence-devel mailing list
PacketFence-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/packetfence-devel