Full Perl script for an API demonstration

Document created by Benjamin Skierlak Employee on Aug 9, 2017
Version 1Show Document
  • View in full screen mode

If you have the Qualys API module activated on your subscription, it will allow you to pull or push data from and to the platform as you wish. This could be useful for a number of reasons, including but not limited to manipulating data more extensively and launching tasks with a user input. 

 

The following script has been written in Perl to demonstrate how you can obtain a full scanning cycle easily enough with the use of API. Here is the logical sequence that it follows: 

  • Asks an IP as user input 
  • Checks if the IP is in your subscription - if not suggests to add it
  • Asks if you want a technical or an executive report 
  • Launches the scan 
  • Generates the report on the platform
  • Download the report to your local folder

 

My goal here is to demonstrate the flexibility you have when working with the API module, but also the kind of work that is required if you have no scripting experience. The script can probably be greatly enhanced and as the Perl saying goes, there is more than one way to do it, but it's working as it is now. If you want to use that script remember that it will interact with your subscription and actually scan IPs so be careful to only target IPs that you have express permission to scan.   

 

#!/usr/bin/perl
# Benjamin Skierlak - QUALYS August 2017 - bskierlak@qualys.com
use LWP;
use strict;
use warnings;
use Data::Validate::IP;
use NetAddr::IP;
use XML::LibXML;
use Net::IP;

#define variables
my $APIURL="https://qualysapi.qualys.eu"; #this is the EU pod
my $username = "********"; #your username and passwd here, can be included in a separate file
my $password = "********";
my @listofips= ();
my $scan_reference="";
my $template_id=0;

#Command to find the list of IPs in your subscription
my $url_find_ips= $APIURL."/api/2.0/fo/asset/ip/?action=list";
#Command to add an IP to your subscription
my $url_add_ip= $APIURL."/api/2.0/fo/asset/ip/?action=add&enable_vm=1&ips=";
#Command to launch a scan on an IP
my $url_scan_ip= $APIURL."/api/2.0/fo/scan/?action=launch";
#Command to retrieve information about a scan
my $url_get_scan= $APIURL."/api/2.0/fo/scan/?action=list&scan_ref=";
#Command to launch a report
my $url_launch_report= $APIURL."/api/2.0/fo/report/?action=launch&";
#Command to fetch a report
my $url_fetch_report= $APIURL."/api/2.0/fo/report/?action=fetch&id=";

#Getting an IP from the customer & validating if the input is an IP
print "\n\n\nEnter the IP you would like to scan:";
my $ip = <STDIN>;
my $is_internal= 1;
chomp $ip;
my $validator=Data::Validate::IP->new;
if ($validator->is_ipv4($ip))
{
     print "Valid IPV4\n";
}
else
{
     print "Not a valid IPV4 address\n";
     exit 0;
}
if ($validator->is_private_ipv4($ip))
{
     print "This is an internal IP address. \n";
}
else
{
     print "/!\\ WARNING - THIS IS A PUBLIC IP ADDRESS /!\\ \n\n";
     print "It is your responsability to verify that you have permission to scan the IP submitted \n\n"; 
     $is_internal=0;
}
################################################################################################
#####################     IS THE IP IN MY SUBSCRIPTION ?    ####################################
################################################################################################

#figuring out if the IP given is in your subscription
     #creating a connexion to the platform through API module
my $browser = LWP::UserAgent->new;
$browser->default_headers->header('X-Requested-With' => 'Sample');
$browser->agent("libwww - script");

     #requesting the list of IPs in the subscription 
my $request = HTTP::Request->new( GET=>$url_find_ips);
$request->authorization_basic($username,$password);
my $response = $browser->request($request);
my $content = $response->content;
open( XMLOUT, ">listofips.xml");
print XMLOUT $content;
close ( XMLOUT );
     #from the file, creating the list of IPs in your subscription
          #getting the IPs from the file
my $list_ips_file= 'listofips.xml';
my $dom = XML::LibXML->load_xml(location => $list_ips_file);

open ( LISTOUT,     ">listofips.txt");
foreach my $address ($dom->findnodes('/IP_LIST_OUTPUT/RESPONSE/IP_SET')){
     print LISTOUT $address->to_literal();
}
close ( LISTOUT );
          #putting the IPs from the document into a list, unscrolling scopes if there is one

open (LISTIN, "<listofips.txt");
while(<LISTIN>){
     if (/\S/){ #if line not blank
          $_=~ s/^\s+|\s+$//g; #stripping unnecessary characters
          if ($_=~ /-/){ #this is a scope
          my $unscroll= new Net::IP($_);
               do {
               push @listofips, $unscroll->ip(); #add unscrolled element to the list
               }while (++$unscroll);
               
          }
          else{ #add element to the list
          push @listofips, $_;
          }
     }
}     
close (LISTIN);

     #compare $myip to the list of IPs in my subscription
if (!($ip ~~ @listofips)){
print "IP not in subscription - would you like to add it (y/n)?\n";     
     my $addition=<STDIN>;
     chomp ($addition);
     if ($addition eq 'y'){
     print "Adding IP to subscription \n";
     my $add_request = HTTP::Request->new( POST=>$url_add_ip.$ip);
     $add_request->authorization_basic($username,$password);
     my $response2 = $browser->request($add_request);
     my $content2 = $response2->content;
     print $content2;
     #print XML output grep result 
     }
     else{
     print "Cancelling procedure. \n";
     exit 0;
     }
}

#At that stage the IP is in the subscription whether we've added it or not
# We also know if the IP is internal or external

#######################################################################################
############################## LAUNCHING A SCAN #######################################
#######################################################################################
print "IP in subscription. \n";
print "Do you want to confirm scan launch on ".$ip." (y/n)? \n";
my $confirmation=<STDIN>;
chomp ($confirmation);
my $launch_arg="";
if ($confirmation eq 'y'){
print "[ --- Launching scan --- ]\n";

print "Title for your scan: ";
my $title=<STDIN>;
chomp($title);
$title=~ s/\s/\+/g; #replacing spaces with +, we'll use this in the report generation
print "\nWhat report would you like: 1) technical 2)Executive: ";
my $report_type=<STDIN>;
chomp ($report_type);

if ($report_type == 2){
$template_id="90782461"; # this is the ID of the template for my subscription - use the API function /msp/report_template_list.php to extract your own template reports IDs
print "Executive report will be sent \n";
}
else {
$template_id="90782460";
print "Technical report will be sent.\n";
}

print "Lauching the scan. Your report will be sent when the scan is finished.\n";      
if ($is_internal == 1){
#prepare internal scan launch request
$launch_arg="&scan_title=".$title."&ip=".$ip."&option_title=Initial+Options&iscanner_name=ScannerBenj";
}
else{
#prepare external scan
$launch_arg="&scan_title=".$title."&ip=".$ip."&option_title=Initial+Options";
}

#actually sending the command
my $launch_request = HTTP::Request->new( POST=>$url_scan_ip.$launch_arg);
$launch_request->authorization_basic($username,$password);
my $response3 = $browser->request($launch_request);
my $content3 = $response3->content;
#printing scan launch results in a file for visibility
open (SCANRES, ">scanreturn.xml");
print SCANRES $content3;
close (SCANRES);

#reopening the same file to extract the scan number.

open (SCANREF, "<scanreturn.xml");
my @list =grep(/scan\//,<SCANREF>);
$scan_reference=$list[0];
$scan_reference=~ s/<\/*VALUE>|\s//g;

}
else {
print "Cancelling procedure. \n";
exit 0;
}

#At that stage the scan is launched, we have the scan reference in $scan_reference.

#########################################################################################
###################### CHECKING IF MY SCAN IS FINISHED ##################################
#########################################################################################

my $scan_status1="Error";
my $scan_status2="Finished";
my $scan_status_final="";

while ($scan_status_final eq ''){
#i.e. while the XML return doesn't indicate that the string contains Error or Finished

my $getscan_request = HTTP::Request->new( POST=>$url_get_scan.$scan_reference);
$getscan_request->authorization_basic($username,$password);
my $response4 = $browser->request($getscan_request);
my $content4 = $response4->content;

if (index($content4,$scan_status1) != -1){ #'Error' has been found
$scan_status_final=$scan_status1;
}
elsif (index($content4,$scan_status2) != -1){# 'Finished has been found'
$scan_status_final=$scan_status2;
}

sleep (30); #waiting 30 seconds before testing if we got a new status

}

print "Scan status: $scan_status_final \n";

##########################################################################################
################### GENERATING A REPORT ONCE MY SCAN IS FINISHED #########################
##########################################################################################

#we will generate the report on the platform with an arbitrary pdf format

if ($scan_status_final eq "Finished"){

my $arg_report="report_title=Report_Results_".$ip."&ips=".$ip."&template_id=".$template_id."&output_format=pdf";
my $report_request = HTTP::Request->new( POST=>$url_launch_report.$arg_report);
$report_request->authorization_basic($username,$password);
my $response5 = $browser->request($report_request);
my $content5 = $response5->content;

open (REPRES, ">reportreturn.xml");
print REPRES $content5;
close (REPRES);

}

else{
print "The scan was not successful. No report will be generated\n"; exit 0;
}

##########################################################################################
################# FETCHING MY REPORT AND SENDING IT THROUGH EMAIL ########################
##########################################################################################

open (REPREF, "<reportreturn.xml");
my @report =grep(/VALUE/,<REPREF>);
my $report_reference=$report[0];
$report_reference=~ s/<\/*VALUE>|\s//g;
close REPREF;

print "Retrieving scan report...\n";
sleep (30);

open (REPORT, ">report_".$ip.".pdf");
my $reportfetch_request = HTTP::Request->new( POST=>$url_fetch_report.$report_reference);
$reportfetch_request->authorization_basic($username,$password);
my $response6 = $browser->request($reportfetch_request);
my $content6 = $response6->content;
print REPORT  $content6;
close REPORT;
3 people found this helpful

Attachments

    Outcomes