Powershell:API:Part 2

Document created by Busby on Mar 29, 2015
Version 1Show Document
  • View in full screen mode

In the previous post we got the Login/Logout to the Qualys cloud platform working.

This is good work and introduces to some things in Powershell that should help later.  Next we will cover below actually launching a SCAN in the Qualys cloud platform from the command line along with some new options in Powershell such as parameters etc…

We will be passing in three parameters to Powershell:

  • Targetip – our TARGET to Scan
  • Requestor – an email address or other thing; I use this to send a PDF of the scans report to the original requestor
  • ScanProfile – Here we give a default and you will need to look up the profile ID for the scan profile you wish to use.  I suggest you have a list of valid ones so you can write a check for this.

As always use at your own risk but let me know if you have questions.  I will work on the next version of this primarily on storing the information about the scan so that you can make a call to generate a report on the IP Address that was scanned.

All Questions are welcome as my documentation can be sparse.

David

param ([Parameter(Mandatory=$true)][String]$targetip,[Parameter(Mandatory=$true)][String]$Requestor,[Parameter(Mandatory=$true)][int]$ScanProfile=440426)

Set-ExecutionPolicy -Scope Process -ExecutionPolicy Unrestricted

Clear-Host

 

<#

       $CookieJar - this is where we will store the cookie that Qualys will issue so you don't need to relogin everytime.

       The Set-Location function is part of Powershell and should be set to where your running this script; shortcut later.

       Your path to CURL will be relative to this.

#>

 

[String]$CookieJar = "$ScriptName.cookies"

Set-Location -Path "D:\Documents\security\Security Groups\Qualys_Community"

 

function Get-QualysAPIAccountCred {

<#

.SYNOPSIS

Returns a PSObject of Credentials

.DESCRIPTION

Creates a PSObject and puts the Qualys Credentails in it.

.PARAMETER NONE

.NOTES

       Should encrypt later.

.EXAMPLE

[PSObject]$Cred = Get-QualysAPIAccountCred

#>

       $CredentialObj  = New-Object -Type PSObject

       $CredentialObj | Add-Member -MemberType NoteProperty -Name username -Value 'QUALYSUSERID' -Force

       $CredentialObj | Add-Member -MemberType NoteProperty -Name password -Value 'QUALYSPASSWORD' -Force

       return [PSObject]$CredentialObj

}

 

function LoginQualys(){

<#

.SYNOPSIS

Login to the Qualys Platform

.DESCRIPTION

Login to the Qualys Platform.

A custom User Agent string could be used for other things and monitoring so I use custom ones for my tools. hint; so do malware authors.

The Get-QualysAPIAccountCred will call a function to return an Object with the credentials to login into Qualys.

We then create the Session String

And finally Invoke the full command. The results of the command go to the Result Object "xml" which you can parse later for other details.

.PARAMETER NONE

.EXAMPLE

LoginQualys

#>

       [PSObject]$myCred = Get-QualysAPIAccountCred

       [String]$SUrl= 'https://qualysapi.qualys.com:443/api/2.0/fo/session/'

       [String]$UA = """X-Requested-With: Powershell"""

       [String]$CURL = ".\curl.exe"

       [String]$SessionLogin = " $CURL --header $UA --dump-header `""+$CookieJar+"`" --insecure --data `"action=login&username="+$myCred.username+"&password="+$myCred.password+"`" `""+$SUrl+"`" "

       [System.Object]$Result = Invoke-Expression $SessionLogin

}

 

function LogoutQualys(){

<#

.SYNOPSIS

Logout of the Qualys Platform

.DESCRIPTION

Logout of the Qualys Platform

.PARAMETER NONE

.EXAMPLE

LogoutQualys

#>

       [String]$CURL = ".\curl.exe"

       [String]$UA = """X-Requested-With: Powershell"""

       [String]$SUrl= 'https://qualysapi.qualys.com:443/api/2.0/fo/session/'

       [String]$SessionLogout = " $CURL --header $UA --cookie `""+$CookieJar+"`" --insecure --data `"action=logout`" `""+$SUrl+" `""

       [System.Object]$Result = Invoke-Expression $SessionLogout

}

 

function Clean-up {

<#

.SYNOPSIS

Clean up any files used for this script

.DESCRIPTION

Clean up any files used for this script

.PARAMETER NONE

#>

       if(Test-Path $CookieJar){Remove-Item -Force $CookieJar}

}

 

function ValidateIP {

<#

.SYNOPSIS

Validates a parameter as an IP

.DESCRIPTION

Validates a parameter as an IP

.PARAMETER IPAddress

              Mandatory = true

              Type = [String]

.EXAMPLE

[System.Net.IPAddress]$ScanIP = ValidateIP -IPAddress $targetip

#>

       [CmdletBinding(SupportsShouldProcess = $true)]

       param

       (

              [Parameter(Mandatory=$true)][String]$IPAddress

       )

       if(($IPAddress -As [IPAddress]) -As [Bool]){

              return [System.Net.IPAddress]::Parse($IPAddress)

       } else {

              return $false

       }

}

 

function LaunchScan {

<#

.SYNOPSIS

Launch a Scan of an IP Address

.DESCRIPTION

Connects to the Qualys Platform and launchs a scan on behalf of the requestor.

Returns the XML response from Qualys for parsing.

.PARAMETER IPtoscan

              Mandatory = true

              Type = [System.Net.IPAddress]

.EXAMPLE

[xml]$Response = LaunchScan -IPtoscan $ScanIP

#>

[CmdletBinding(SupportsShouldProcess = $true)]

param

(

[Parameter(Mandatory=$true)][System.Net.IPAddress]$IPtoscan,

       [Parameter(Mandatory=$true)][int]$ScanProfile  

)

       [String]$CURL = ".\curl.exe"

      

       #This will use one scanner appliance in particular to execute the scan.

       [String]$ScannerName="CyrusOne_2"

       [String]$UA = """X-Requested-With: Powershell"""

       [String]$SUrl= 'https://qualysapi.qualys.com:443/api/2.0/fo/scan/'

       [PSObject]$CredentialObj = Get-QualysAPIAccountCred

      

       #Just need some title for the scan in Qualys

       [String]$ScanTitle="Self_Scan_$IPtoscan"

      

       #Where to write the results of our command

      

       [String]$ResponseFile = "$IPtoscan.xml"

      

       #Build up our curl command

       [String]$scanurl = "$CURL --header $UA --user `"$($CredentialObj.username):$($CredentialObj.password)`" --cookie `""+$CookieJar+"`" -X `"POST`" --insecure --data `"action=launch&scan_title=$ScanTitle&ip=$IPtoscan&option_id=$ScanProfile&iscanner_name=$ScannerName`" `""+$SUrl+"`" > `""+$ResponseFile+"`""

      

       #Just writes to the console so we can see what was issued.

       Write-Warning $scanurl

      

       #We invoke the command and remember we are writing to the $ResponseFile all of the output from Qualys.

       [System.Object]$Result = Invoke-Expression $scanurl

      

       #Call to Read the XML Response from Qualys back into memory and this is an XML object which will ease some of the parsing.

       [xml]$ResponseData = Read-Response -ResponseFile $ResponseFile

      

       #Remove the Response file we no longer need that file.

       Remove-Item $ResponseFile -Force

       return [xml]$ResponseData

}

 

function Read-Response {

<#

.SYNOPSIS

Reads in a Response File

.DESCRIPTION

Reads in a Response File

.PARAMETER    Response File

              Mandatory = true

              Type = [String]

.EXAMPLE

[xml]$ResponseData = Read-Response -ResponseFile $ResponseFile

#>

       [CmdletBinding(SupportsShouldProcess = $true)]

       param

              (

                     [Parameter(Mandatory=$true)][String]$ResponseFile

              )

       if(Test-Path $ResponseFile){

              [xml]$Response = Get-Content $ResponseFile

       } else {

              Test-Path $ResponseFile -ErrorAction Stop

       }

       return [xml]$Response

}

 

function ScanStatus {

<#

.SYNOPSIS

Gets that Status of Scans

.DESCRIPTION

Updates the status of submitted Scans but setting up an array of PSObjects to hold all of the data.

.PARAMETER    ScanResponse

              Mandatory = true

              Type = [xml]

.PARAMETER    TargetIP

              Mandatory = true

              Type = [System.Net.IPAddress]

.PARAMETER    InitiatedBy

              Mandatory = true

              Type = [String]

.EXAMPLE

[PSObject]$myscan = ScanStatus -ScanResponse $Response -TargetIP $ScanIP -InitiatedBy $Requestor

#>

       [CmdletBinding(SupportsShouldProcess = $true)]

       param

              (

                     [Parameter(Mandatory=$true)][xml]$ScanResponse,

                     [Parameter(Mandatory=$true)][System.Net.IPAddress]$TargetIP,

                     [Parameter(Mandatory=$true)][String]$InitiatedBy

              )

       #Create NEW PSOBJECT to store information about our scan in a data structure.

       $ScanObj  = New-Object -Type PSObject

      

       #Now we are going to Parse data out of the XML response that we got from Qualys.  We are not looking for anything yet just putting it all into

       #an easier format to read.

       foreach ($line in $ScanResponse.SIMPLE_RETURN.RESPONSE | Select-Object text){

              Write-Warning $line.text

             

              #Instead of the IF statement this could be better written as a switch.

              if($line.text -eq "New vm scan launched"){

                     #Scan was LAUNCHED so add this to the PSObject

                     $ScanObj | Add-Member -MemberType NoteProperty -Name STATUS -Value "LAUNCHED" -Force

                     foreach ($item in $ScanResponse.SIMPLE_RETURN.RESPONSE.ITEM_LIST.ITEM | `

                           Select-Object key,value){

                           $ScanObj | Add-Member -MemberType NoteProperty -Name $item.KEY -Value $item.VALUE -Force

                     }

              } else {

                     if($line.text -like "*none of the specified IPs are eligible for Vulnerability Management scanning*"){

                           $ScanObj | Add-Member -MemberType NoteProperty -Name STATUS -Value "NOTELIGIBLE" -Force

                     } else {

                           $ScanObj | Add-Member -MemberType NoteProperty -Name STATUS -Value "NOTLAUNCHED" -Force

                     }

              }

              $ScanObj | Add-Member -MemberType NoteProperty -Name TARGET -Value $TargetIP -Force

              $ScanObj | Add-Member -MemberType NoteProperty -Name RPTID -Value "NULL" -Force

              $ScanObj | Add-Member -MemberType NoteProperty -Name REQUESTOR -Value $InitiatedBy -Force

       }

       return [PSObject]$ScanObj

}

 

function Main {

       LoginQualys

      

       #The call to Validate IP will try to validate that the user did pass in a valid IPv4 address but this function can validate IPv6 as well.

       #It will return a boolean value.

       if(ValidateIP -IPAddress $targetip){

              #This is another check to force the TARGETIP into a an IP Address OBJECT

              [System.Net.IPAddress]$ScanIP = ValidateIP -IPAddress $targetip

              [xml]$Response = LaunchScan -IPtoscan $ScanIP -ScanProfile $ScanProfile

             

              #Now we have the Response from Qualys and we can check; did we launch the scan or get an error.

              #In Powershell I am a big fan of the PSObject. PSObjects can contain whatever we want so the next function will build an object for us.

              [PSObject]$myscan = ScanStatus -ScanResponse $Response -TargetIP $ScanIP -InitiatedBy $Requestor

             

              <#

                     Here is the layout of the Object $myscan

                     $myscan->[]={}

                     $myscan->[STATUS]={LAUNCHED}

                     $myscan->[TARGET]={$ScanIP}

                     $myscan->[RPTID]={NULL}

                     $myscan->[REQUESTOR]={$Requestor}

                     $myscan->[REFERENCE]={Reference from Qualys}

                     $myscan->[ID]={ID of the Scan from Qualys}

              #>

              Write-Warning "Scan of TARGET [$($myscan.TARGET)} for user [$($myscan.STATUS)] returned a status of [$($myscan.REQUESTOR)] from Qualys"

              Write-Warning "Scan ID [$($myscan.ID)] with Reference [$($myscan.REFERENCE)]"

       }

       LogoutQualys

       Clean-up

}

 

. Main

Attachments

    Outcomes