Created
January 3, 2019 17:35
-
-
Save mbrownnycnyc/d39027685b033090aec883d72472ca08 to your computer and use it in GitHub Desktop.
powershell script for object comparison for Nexpose/InsightVM discovered open ports custom report. Didn't drink enough coffee during this one, and sprinted to the end. Updates to come: support for 2FA, would like to make more DRY.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# https://help.rapid7.com/insightvm/en-us/api/index.html | |
-- open ports custom report -- | |
SELECT da.ip_address, da.host_name, dos.name AS OS, dos.version AS os_version, das.port, dp.name AS protocol, ds.name AS service, dsf.name AS service_name, dsf.version AS service_version | |
FROM dim_asset_service das | |
JOIN dim_service ds USING (service_id) | |
JOIN dim_protocol dp USING (protocol_id) | |
JOIN dim_asset da USING (asset_id) | |
JOIN dim_operating_system dos USING (operating_system_id) | |
JOIN dim_service_fingerprint dsf USING (service_fingerprint_id) | |
ORDER BY da.ip_address, das.port | |
#> | |
function ignore-certificatevalidation { | |
#ignore SSL/TLS cert errors: | |
if (-not ([System.Management.Automation.PSTypeName]'ServerCertificateValidationCallback').Type) { | |
$certCallback = @" | |
using System; | |
using System.Net; | |
using System.Net.Security; | |
using System.Security.Cryptography.X509Certificates; | |
public class ServerCertificateValidationCallback | |
{ | |
public static void Ignore() | |
{ | |
if(ServicePointManager.ServerCertificateValidationCallback ==null) | |
{ | |
ServicePointManager.ServerCertificateValidationCallback += | |
delegate | |
( | |
Object obj, | |
X509Certificate certificate, | |
X509Chain chain, | |
SslPolicyErrors errors | |
) | |
{ | |
return true; | |
}; | |
} | |
} | |
} | |
"@ | |
Add-Type $certCallback | |
} | |
[ServerCertificateValidationCallback]::Ignore() | |
} | |
function generate-nscauthheaders{ | |
[cmdletbinding()] | |
param ( | |
[string]$nschost, | |
[string]$nscusername, | |
[string]$nscpassword | |
) | |
#https://stackoverflow.com/a/27951845/843000 | |
#build base64 encoded string | |
$authpair = "$($nscusername):$($nscpassword)" | |
$encodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($authpair)) | |
$basicAuthValue = "Basic $encodedCreds" | |
$Headers = @{ | |
Authorization = $basicAuthValue | |
} | |
return $headers | |
} | |
function get-listofreports { | |
[cmdletbinding()] | |
param ( | |
[string]$nschost, | |
$nscauthheader | |
) | |
$resource = "reports" | |
$uri = "https://$($nschost)/api/3/$($resource)" | |
$ret = Invoke-WebRequest -URI $URI -headers $nscauthheader -UseBasicParsing -method GET | |
# Extract data from response | |
$retdata = [object[]](convertfrom-json ($ret.Content)).resources | |
return $retdata | |
} | |
function run-allreportsbyid { | |
[cmdletbinding()] | |
param ( | |
[string]$nschost, | |
$nscauthheader, | |
$reportids | |
) | |
$resource = "reports" | |
$uri = "https://$($nschost)/api/3/$($resource)" | |
$returndata = @() | |
foreach ($reportid in $reportids) { | |
$method = "generate" | |
$uri = $uri + "/$($reportid.id)/$($method)" | |
$postparams = $reportid | |
$data = (convertto-json -compress $postparams) | |
try { | |
$ret = (Invoke-WebRequest -URI $URI -headers $nscauthheader -UseBasicParsing -method POST -body $data) | |
} catch { | |
if ( $($_.exception.response.statuscode).value__ -eq 405 ) { | |
$optionscontent = Invoke-WebRequest -URI $URI -headers $nscauthheader -UseBasicParsing -method OPTIONS | |
$retoptions = [object[]](convertfrom-json ($optionscontent.content)).links | |
return $retoptions | |
} | |
} | |
# Extract data from response | |
$retdata = [object[]]( convertfrom-json ($ret.Content) ).links | where {$_.rel -ne "self" } | |
$returndata += $retdata | |
} | |
return $returndata | |
} | |
function download-reportbyid { | |
[cmdletbinding()] | |
param ( | |
[string]$nschost, | |
$nscauthheader, | |
$reportinstancelink, | |
$downloadlocation, | |
$reportname | |
) | |
$uri = $reportinstancelink | |
$method = "output" | |
$uri = $uri + "/$($method)" | |
$reportname = $reportnames -replace " ","_" | |
#https://lazywinadmin.com/2015/08/powershell-remove-special-characters.html | |
$reportname = $reportname -replace '[^\p{L}\p{Nd}/_]', '' | |
$isolatedreportindex = ($reportinstancelink -split "/")[-1] | |
# or the manly way: ($reportinstancelink.href) -match ".*\/(?!.*\/)(.*)" | out-null; $matches[1] | |
$reportname = $reportname + "_" + $isolatedreportindex | |
$downloadtarget = $downloadlocation + "\" + (get-date).tostring("yyyyMMdd_HHmmmss") + "-" + $reportname + ".xml" | |
try { | |
Invoke-WebRequest -verbose -URI $URI -headers $nscauthheader -outfile $downloadtarget -UseBasicParsing -method GET | |
} catch { | |
$downloadtarget = $_.exception | |
} | |
return $downloadtarget | |
} | |
function getdiffsin-XMLs { | |
[cmdletbinding()] | |
param ( | |
$xmlfilenames | |
) | |
$nscxmlfilesinfo = @() | |
#build new data object to perform comparisons | |
foreach ($xmlfilename in $xmlfilenames) { | |
$xmlcontent = [xml](get-content "$($xmlfilename.fullname)") | |
#each file info object should contain info: | |
$xmlfileinfo = "" | select filename, nodesdata | |
$xmlfileinfo.filename = $xmlfilename | |
#each node info object should contain info: | |
$nodesdataobj = "" | select addresses, nodesinfo | |
$nodesdataobj.addresses = $xmlcontent.NexposeReport.nodes.node | select address | |
$nodesinfo = @() | |
foreach ($address in $nodesdataobj.addresses) { | |
#each nodesdata record should contain the nodes and a node info record structured as follows: | |
$tempobj= "" | select address, endpoints | |
$tempobj.address = $address.address | |
$tempobj.endpoints = ( ($xmlcontent.NexposeReport.nodes.node) | ? { ($_.address) -eq $address.address } ).endpoints.endpoint | |
$nodesinfo += $tempobj | |
} | |
$nodesdataobj.nodesinfo = $nodesinfo | |
$xmlfileinfo.nodesdata = $nodesdataobj | |
$nscxmlfilesinfo += $xmlfileinfo | |
} | |
#comparisons: | |
#questions to answer: | |
#what changes in addresses? | |
#what changes in open ports? | |
#note that $nscxmlfilesinfo[1] should always be _latest, so this is the perspective we're rendering resultant info | |
$olderfileinfo = $nscxmlfilesinfo[0] | |
$currentfileinfo = $nscxmlfilesinfo[1] | |
#addresses | |
$addresscomp = compare-object $olderfileinfo.nodesdata.addresses $currentfileinfo.nodesdata.addresses | |
$addressstatus = @() | |
foreach ($addresscompinfo in $addresscomp) { | |
$tempobj = "" | select address, addedorremoved | |
$tempobj.address = ($addresscompinfo | select address).address | |
if ($addresscompinfo.sideindicator -eq "<=" ) { | |
$tempobj.addedorremoved = "removed from ($currentfileinfo.filename).name" | |
} else { | |
$tempobj.addedorremoved = "added to $(($currentfileinfo.filename).name)" | |
} | |
$addressstatus += $tempobj | |
} | |
#endpoints | |
#obtain all data per node per file | |
$olderfileinfoendpoints = @() | |
foreach ($item in $olderfileinfo.nodesdata.nodesinfo) { | |
foreach ($endpoint in $item.endpoints) { | |
$tempobj = "" | select combined, address, ep_protocol, ep_port, ep_status, ep_service_name | |
$tempobj.combined = $item.address + "," + $endpoint.protocol + "," + $endpoint.port + "," + $endpoint.status + "," + $endpoint.services.service.name | |
$tempobj.address = $item.address | |
$tempobj.ep_protocol = $endpoint.protocol | |
$tempobj.ep_port = $endpoint.port | |
$tempobj.ep_status = $endpoint.status | |
$tempobj.ep_service_name = $endpoint.services.service.name | |
$olderfileinfoendpoints += $tempobj | |
} | |
} | |
$currentfileinfoendpoints = @() | |
foreach ($item in $currentfileinfo.nodesdata.nodesinfo) { | |
foreach ($endpoint in $item.endpoints) { | |
$tempobj = "" | select combined, address, ep_protocol, ep_port, ep_status, ep_service_name | |
$tempobj.combined = $item.address + "," + $endpoint.protocol + "," + $endpoint.port + "," + $endpoint.status + "," + $endpoint.services.service.name | |
$tempobj.address = $item.address | |
$tempobj.ep_protocol = $endpoint.protocol | |
$tempobj.ep_port = $endpoint.port | |
$tempobj.ep_status = $endpoint.status | |
$tempobj.ep_service_name = $endpoint.services.service.name | |
$currentfileinfoendpoints += $tempobj | |
} | |
} | |
$endpointcomp = compare-object $olderfileinfoendpoints.combined $currentfileinfoendpoints.combined | |
$endpointstatus = @() | |
foreach ($endpointcompinfo in $endpointcomp) { | |
$tempobj = "" | select endpoint, addedorremoved | |
$tempobj.endpoint = $endpointcompinfo.inputobject | |
if ($endpointcompinfo.sideindicator -eq "<=" ) { | |
$tempobj.addedorremoved = "removed from $(($currentfileinfo.filename).name)" | |
} else { | |
$tempobj.addedorremoved = "added to $(($currentfileinfo.filename).name)" | |
} | |
$endpointstatus += $tempobj | |
} | |
# $endpointstatus contains all info needed, but $addressstatus contains just the addresses | |
if ( $endpointstatus.endpoint.count -gt 0 ) { | |
return $endpointstatus | |
} else { | |
return "No changes" | |
} | |
} | |
function main { | |
$nschost = "localhost:3780" | |
$nscusername = "admin" | |
$nscpassword = "XXXXXX" | |
$reportnames = "Available ports report*" | |
$downloadlocation = "C:\Users\Matt\docs\Nexpose-InsightVM\reports" | |
ignore-certificatevalidation | |
$nscauthheader = generate-nscauthheaders -nschost $nschost -nscusername $nscusername -nscpassword $nscpassword | |
#find all report IDs | |
$listofreportids = get-listofreports -verbose -nschost $nschost -nscauthheader $nscauthheader | where {$_.name -like $reportnames} | select id | |
#run all reports and get instance uris | |
$reportinstancelinks = run-allreportsbyid -verbose -nschost $nschost -nscauthheader $nscauthheader -reportids $listofreportids | |
#find the links for the latest links and find the links for the previous run | |
$reportinstancedownloadlinks = @() | |
foreach ($reportinstancelink in $reportinstancelinks) { | |
if ($reportinstancelink.rel -eq "Report Instance") { | |
#find the base link | |
$baselink = $($reportinstancelink.href).substring(0,$($reportinstancelink.href).indexof("/history/") + "/history/".length) | |
#populate with latest link | |
$reportinstancedownloadlinks += $baselink + "latest" | |
#populate with previous link | |
$penultimatereportindex = $(($reportinstancelink.href -split "/")[-1]) -1 | |
$reportinstancedownloadlinks += $baselink + $penultimatereportindex | |
} | |
} | |
#download all required reports | |
foreach ($reportinstancedownloadlink in $reportinstancedownloadlinks) { | |
download-reportbyid -verbose -nscauthheader $nscauthheader -reportinstancelink $reportinstancedownloadlink -downloadlocation $downloadlocation -reportname $reportnames | |
} | |
#get diffs in XMLs contents | |
$xmlfilenames = dir -path $downloadlocation | sort lastwritetime | select -first 2 | |
$xmlchanges = getdiffsin-XMLs -xmlfilenames $xmlfilenames | |
#produce a report on the changes | |
if ($xmlchanges -ne "No Changes") { | |
produce-nscxmlchangereport -xmlchanges $xmlchanges | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment