Following a day recently where Shodan sold lifetime memberships for $1, and inspired by how simple their API was to use as highlighted initially by Kelvin Tegelaar (CyberDrain.com) I decided that it was a perfect opportunity to utilise my AutomateAPI to get decent, actionable information out of the Shodan API. The output of this script includes ports that have been found open on that address and actionable CVEs that were detected.

I would advise against using the results from the Shodan API as a gospel source for this information – it’s better served as an overview based on scans Shodan have done at some point in time (the last time the data was refreshed is actually included in the full output in-case you need it).

For those that aren’t aware of it, AutomateAPI is a Powershell Module I have written that allows interaction with ConnectWise Automate from within Powershell. You will find it here Github > AutomateAPI. I have written extensive documentation on how to use the AutomateAPI, but if you simply want to install it, it’s as simple as running Install-Module AutomateAPI in a Powershell window.

One of many actions this module allows you to do is extract all of the external IP addresses that are currently in Automate for all your agents. The subsequent provided code then groups them so they are unique, ignoring any external IP address that is less than 3 agents (this discounts home PCs etc) then runs and collates results against Shodan’s API.

There are only two required prerequisites to running this code:

  1. You have the AutomateAPI Powershell Module installed (Install-Module AutomateAPI)
  2. You have a Shodan API Key (I believe you can get the API Key for free by simply registering)

To run this code:

  1. Modify and add the $ShodanAPIKey in Line 2
  2. Decide whether you want detected open ports to highlight as red in the output. Set Line 3 to either $False or $True. This is purely cosmetic.
  3. Run the code

You will be prompted initially to connect to the Automate API which will require you to know your Automate Server Address, Username, Password and 2FA code. All output will be sent to the screen during running time and at the end you will be prompted to save the results to a CSV.

I hope you find this code useful, and as always if anyone has any feedback or input I’d love to hear it. If you get blank results for a company this usually means Shodan has not scanned that IP address yet or it has found nothing at that address.

#----------User Variables-----------------
$ShodanAPIKey = ""
$OpenPortsAreErrors = $False
#----------End User Variables-------------

#Check for pre-requisite module
if (Get-Module -ListAvailable -Name AutomateAPI) {
    # No Action needed as the module exists
} 
else {
    Write-Host "The AutomateAPI module is not currently installed. You can install it by running Install-Module AutomateAPI" -BackgroundColor Red -ForegroundColor White
    exit
}

Connect-AutomateAPI

$FinalArray = @()

$AllResults = Get-AutomateComputer -AllComputers
$Test = $AllResults | Sort-Object -Property @{e={$_.Client.Name}} | Group-Object -Property GatewayIPAddress | ? {$_.Count -gt 2}

foreach ($ExternalIP in $Test) {
    if ($($ExternalIP.Name).StartsWith('10.') -or $($ExternalIP.Name).StartsWith('192.168') -or $($ExternalIP.Name).StartsWith('127.') -or ($($ExternalIP.Name).StartsWith('172.') -and ($($ExternalIP.Name).split('.')[1] -as [int] -in 16..31)))
    {
        Write-Host "$($ExternalIP.Group.Client.Name | Select-Object -First 1) has not been tested on IP $($ExternalIP.Name) as this is a private IP address" -ForegroundColor White -BackgroundColor DarkBlue
        continue;
    }

    $Shodan = ""
    try {
        $Shodan = Invoke-RestMethod -uri "https://api.shodan.io/shodan/host/$($ExternalIP.Name)?key=$ShodanAPIKey" -ErrorAction SilentlyContinue
    }
    catch {
        Write-Debug $_.Exception.Message
    }
    
    $ShodanResult = ""
    $ShodanResult = New-Object -TypeName psobject
    $ShodanResult | Add-Member -MemberType NoteProperty -Name ClientName -Value $($ExternalIP.Group.Client.Name | Select-Object -First 1)
    $ShodanResult | Add-Member -MemberType NoteProperty -Name LocationName -Value $($ExternalIP.Group.Location.Name | Select-Object -First 1)
    $ShodanResult | Add-Member -MemberType NoteProperty -Name ExternalIP -Value $ExternalIP.Name
    $ShodanResult | Add-Member -MemberType NoteProperty -Name RawShodanResults -Value $Shodan
    $ShodanResult | Add-Member -MemberType NoteProperty -Name ISP -Value $Shodan.ISP
    $ShodanResult | Add-Member -MemberType NoteProperty -Name VulnerabilitiesFound -Value $($Shodan.Vulns -join "|")
    $ShodanResult | Add-Member -MemberType NoteProperty -Name Ports -Value $($Shodan.Ports -join "|")
    $ShodanResult | Add-Member -MemberType NoteProperty -Name LastUpdate -Value $Shodan.last_update

    $FinalStatus = "Condition detected. "
    $ConditionsDetected = $False

    If($($Shodan.Vulns).Count -gt 0)
    {
        $ConditionsDetected = $True
        $FinalStatus = $FinalStatus += "$(($Shodan.Vulns).Count) Vulnerabilities found. $($Shodan.Vulns)"
    }

    If($($Shodan.Ports).Count -gt 0 -and $OpenPortsAreErrors)
    {
        $ConditionsDetected = $True
        $FinalStatus = $FinalStatus += "$(($Shodan.Ports).Count) ports found open. $($Shodan.Ports)"
    }

    if ($ConditionsDetected) {
        Write-Host "$($ExternalIP.Group.Client.Name | Select-Object -First 1) has been tested on IP $($ExternalIP.Name). $FinalStatus" -BackgroundColor Red -ForegroundColor Black
    }

    if (!$ConditionsDetected) {
        Write-Host "$($ExternalIP.Group.Client.Name | Select-Object -First 1) has been tested on IP $($ExternalIP.Name) and no conditions were found" -BackgroundColor DarkGreen -ForegroundColor White
    }

    $FinalArray += $ShodanResult
    Start-Sleep -Milliseconds 1100
}

$output = Read-Host 'Enter full path where CSV should be saved (IE C:\Temp\Output.csv)'
try {
    $FinalArray | Export-Csv -Path $output -NoClobber -NoTypeInformation
}
catch {
    Write-Host "File Save failed with error $_.Exception.Message. The results are stored in a variable called FinalArray so you may be able to export them manually" -BackgroundColor Red -ForegroundColor Black
}