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 XML::Simple; 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 createEscalator Create an escalator which will trigger an action on the OpenVAS server once the scan will finish =cut sub createEscalator { my ( $this ) = @_; my $logger = Log::Log4perl::get_logger(__PACKAGE__); my $name = $this->{_id}; my $callback = $this->_generateCallback(); my $command = _get_escalator_string($name, $callback); $logger->info("Creating a new scan escalator named $name"); my $cmd = "omp -h $this->{_host} -p $this->{_port} -u $this->{_user} -w $this->{_pass} -X '$command'"; $logger->trace("Scan escalator creation command: $cmd"); my $output = pf_run($cmd); chomp($output); $logger->trace("Scan escalator creation output: $output"); my $xml = new XML::Simple; my $response = $xml->XMLin($output); my $status = $response->{'status'}; my $escalator_id = $response->{'id'}; # Fetch response status and escalator id # Scan escalator successfully created if ( defined($status) && $status eq $RESPONSE_RESOURCE_CREATED ) { $logger->info("Scan escalator named $name successfully created with id: $escalator_id"); $this->{_escalatorId} = $escalator_id; return $TRUE; } $logger->warn("Dennis : Response : $status"); $logger->warn("There was an error creating scan escalator 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 = "$name$target_host"; $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"); my $xml = new XML::Simple; my $response = $xml->XMLin($output); my $status = $response->{'status'}; my $target_id = $response->{'id'}; # Fetch response status and target id # Scan target successfully created if ( defined($status) && $status 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 escalator =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->{_escalatorId} ); 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); $logger->trace("Scan task creation output: $output"); # Fetch response status and task id my $xml = new XML::Simple; my $response = $xml->XMLin($output); my $status = $response->{'status'}; my $task_id = $response->{'id'}; # Scan task successfully created if ( defined($status) && $status 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 = ""; $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"); my $xml = new XML::Simple; my $response = $xml->XMLin($output); my $status = $response->{'status'}; my $raw_report = $response->{'report'}->{'content'}; # Fetch response status and report # Scan report successfully fetched if ( defined($status) && $status 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, '_escalatorId' => 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->createEscalator(); $this->createTask(); $this->startTask(); } =item startTask Start a scanning task with the previously created target and escalator =cut sub startTask { my ( $this ) = @_; my $logger = Log::Log4perl::get_logger(__PACKAGE__); my $name = $this->{_id}; my $task_id = $this->{_taskId}; my $command = ""; $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"); my $xml = new XML::Simple; my $response = $xml->XMLin($output); my $status = $response->{'status'}; my $report_id = $response->{'id'}; # Scan task successfully started if ( defined($status) && $status 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 = "HTTP Get"; if ($this->{'_host'} eq '127.0.0.1') { $callback .= "http://127.0.0.1/scan/report/$name"; } else { $callback .= "https://$Config{general}{hostname}.$Config{general}{domain}:$Config{ports}{admin}/scan/report/$name"; } $callback .= "URL"; $logger->debug("Generated OpenVAS callback is: $callback"); return $callback; } =back =head1 SUBROUTINES =over =item _get_escalator_string create_escalator string creation. =cut sub _get_escalator_string { my ($name, $callback) = @_; return <<"EOF"; $name AlwaysHighlevelchangeddirection Task run status changedDonestatus $callback EOF } =item _get_task_string create_task string creation. =cut sub _get_task_string { my ($name, $config_id, $target_id, $escalator_id) = @_; return <<"EOF"; $name EOF } =back =head1 AUTHOR Inverse inc. =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;