Banner Grabs Powershell
Banner Grabber Script
You can use many tools that see headers, but here is a powershell script I wrote to check banners on multiple devices at once, it’s sort of fancy:
- This script takes a simple 2 column CSV of IP and PORT and then scans them.
- You specify in the source how many threads and how long of a pause between checks (to avoid detection/block)
- It first figures out both the IP and HOST no matter you gave it
- then checks with a ping to see if it’s online
- if it is online, it does the port read to see if it’s open
- if it is open on that port, it tries
tag if port 80/443 - it tries to get stream banner grab (anything) if any other port
- Dumps all to nice CSV
<#
2 threads: about 2-5 results in 3 seconds (some responses take a while)
20 threads: about 25 results in 3 seconds (one response was 10 seconds old)
#>
# the name of your out report
$infile = "input-sample.csv"
$outfile = "output-results.csv"
$tmpfile = "investigate.tmp"
# if ping fails, you want to scan anyway?
# $ALSE = no
# $TRUE = yes
$TestAnyway = $FALSE
# how many threads? 5-20 suggested
# lower than 5 is to avoid scan detection alerts
$threads = 4
# milliseconds between scans
$slow = 500
# want to scroll your screen?
$debug = $TRUE
if ($debug) {write-host "DEBUG ON"} else {clear;}
# just in case you don't want to block your ip
$warning = Invoke-WebRequest -uri "https://ifconfig.me/ip"
write-host "YOUR SCAN FROM IP IS: $($warning.content)" -foregroundcolor yellow
write-host "YOU ARE USING $($threads) THREADS WITH $($slow)ms (ADDITIONAL) PAUSE" -foregroundcolor yellow
$confirmation = Read-Host "Are you sure you want to scan? (Y|N)"
if (($confirmation -eq 'N') -Or ($confirmation -eq 'n')){
exit
}
# required for bypassing self signed error
if (-not("dummy" -as [type])) {
add-type -TypeDefinition @"
using System;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
public static class Dummy {
public static bool ReturnTrue(object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors) { return true; }
public static RemoteCertificateValidationCallback GetDelegate() {
return new RemoteCertificateValidationCallback(Dummy.ReturnTrue);
}
}
"@
}
$TimeTaken = (Measure-Command {
# load our file into memory as array of strings
$Lines = Get-Content $infile
$HowMany = $Lines.count
$Skipped = 0
Foreach ($Line in $Lines) {
if($Line.StartsWith('#')) { $Skipped = $Skipped +1 }
}
$Lines | ForEach-Object -ThrottleLimit $threads -Parallel{
# make a copy of object for use with parallel
$Line = $_
# skip any lines starting with #
if($Line.StartsWith('#')) { continue }
# break up our input line
$Target,$_Port = $Line.split(",")
# Determine Hostname
try {
# maybe they gave us a hostname, so look up IP
$HostName = [System.Net.Dns]::GetHostByAddress($Target).Hostname
} catch {
# No... default to what it was
$HostName = $Target
}
# Determine IP
try {
# Did they give us a hostname? convert to IP if they did
$IP = [System.Net.Dns]::GetHostByName($Target)
$IP = $IP.AddressList[0].IPAddressToString
} catch {
# No... default to what it was
$IP = 'NO REVERSE'
}
function QueryPort ([string]$HostName, [string]$_Port) {
# Mandatory speed limit
Start-Sleep -Milliseconds $($using:slow)
[hashtable]$return = @{}
if ($_Port -eq 80){
$url = "http://$($HostName)"
# if it's port 80, it's probably a web server
try{
$req = Invoke-WebRequest -uri $url
# and if we get a status code 200, then
$content = $req.content
# we can massage data
$content = [string]::join(" ",($content.split("`n")))
$content = $content -replace '\s{2,}',' '
# to extract title, for a clue
$content -match '<title>([\s\S]*?)</title>'
$title += $matches[1]
$return.status = 'OPEN'
$return.message = $title
} catch{
# or we'll get some other fail type code on http like 401,403,404
$return.status = 'OPEN'
$return.message = $_.Exception.Response.StatusCode.Value__
}
} elseif (($_Port -eq 443) -or ($_Port -eq 8080) -or ($_Port -eq 5986) -or ($_Port -eq 5985)){
# fixme: try tls connection as default and if that fails, read stream
$url = "https://$($HostName):$($_Port)"
# if it's port 443, it's probably a webserver
try{
# and even though a good result might work
# it could have a self signed, so ignore that check
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = [dummy]::GetDelegate()
$req = Invoke-WebRequest -Uri $url
$status = $req.StatusCode
$content = $req.content
# but then massage our data
$content = [string]::join(" ",($content.split("`n")))
$content = $content -replace '\s{2,}',' '
# and see if there is a title
$content -match '<title>([\s\S]*?)</title>'
$title += $matches[1]
$return.status = 'OPEN'
$return.message = $title
} catch{
# or we'll get some other fail type code on https like 401,403,404
$return.status = 'OPEN'
$return.message = $_.Exception.Response.StatusCode.Value__
}
} else {
$Socket = New-Object System.Net.Sockets.TCPClient
$Connected = ($Socket.BeginConnect( $HostName, $_Port, $Null, $Null)).AsyncWaitHandle.WaitOne(300)
if ($Connected -eq "True"){
$Stream = $Socket.getStream()
Start-Sleep -m 500;
$Text = ""
# try to read banner
while ($Stream.DataAvailable) { $Text += [char]$Stream.ReadByte() }
# default in case nothing found
if ($Text.Length -eq 0){
$Text = "N/A"
}
$Socket.Close()
# Massage and return
$Text = $Text -replace "`n"
$Text = $Text -replace "`r"
#$Text = $Text -replace "\W"
$return.status = 'OPEN'
$return.message = $Text
} else {
$return.status = 'CLOSED'
$return.message = 'NOT TESTED'
}
}
return $return
}
# if it responds to ping, then do scan
# maybe pings are blocked though?
# todo, or logic for scan anyway
if ((Test-Connection -count 1 -comp $HostName -quiet) -or ($TestAnyway)) {
$Details = [pscustomobject]@{
Date = get-date
Online = "ONLINE"
IPv4 = $IP
Host = $HostName
Port = $_Port
OpCl = ""
Banner = ""
}
# for the ports given, check them
try {
# call function
$result = QueryPort $HostName $_Port
$Details.OpCl = $result.status
$Details.Banner = $result.message
# why this had a "True" in everystring beats me, but this fixes it
#$Details.Port = $Details.Port -replace "True",""
} catch {
# maybe ping works, but all ports closed?
}
} else {
$Details = [pscustomobject]@{
Date = get-date
Online = "OFFLINE"
IPv4 = $IP
Host = $HostName
Port = $_Port
OpCl = "N/A"
Banner = "N/A"
}
}
# convert it to the output format we want, skipping redundant header
$data = $Details| ConvertTo-Csv -NoTypeInformation | select-object -skip 1
# append to our results file
# we are using mutex for file locking
$mtx = New-Object -TypeName System.Threading.Mutex($false, "mtx")
If ($mtx.WaitOne(1000)) {
$data | Out-File -append -FilePath .\$($using:tmpfile) -ErrorAction Stop
[void]$mtx.ReleaseMutex()
} else {
Throw "Timed out acquiring mutex lock"
}
# Let us know where we are
$Date = get-date
#write-host "$($Date): $($Target):$($_Port)" -ForegroundColor Yellow
if ($($using:debug)) { write-host "DEBUG:$($data)" -foregroundcolor blue }
$mtx.Dispose()
}
})
# Add a Header Back
"DATE,ICMP,IPV4,HOSTNAME,PORT,STATUS,BANNER" | Out-File -FilePath .\$($outfile) -ErrorAction Stop
Get-Content .\$($tmpfile) | Out-File -append -FilePath .\$($outfile) -ErrorAction Stop
# delete our temp file
Remove-Item .\$($tmpfile)
# how long did it take?
$timed = [math]::Round($TimeTaken.TotalSeconds,1)
write-host "$HowMany Processed, $Skipped were skipped. $timed seconds. $threads Threads" -ForegroundColor Green