I'm having a difficult time figuring out where this code is breaking, and why Select-Object is not returning the desired object properties as output. Any input into what is happening here, or any potential fixes are greatly appreciated.
I'm gathering all the unique sha256 hash values from Sysmon logs via a (separate) handler script, then the handler script executes the script/code below against those hash values. This handler script runs the code seen below and then pipes its output (script.ps1 | export-csv) into an Export-Csv function. However I'm getting each object's arrays in my csv output, instead of a formatted CSV file with column headers and the desired object properties.
For completeness, the handler script that runs this (code below) script.ps1 file is Kansa
# VirusTotal API Key
$apikeyVirusTotal = "ABC123MyFreeVirusTotalAPIKey"
$hashArray = @() #Array to hold multiple objects
# VirusTotal Function - API/URI
Function SubmitVirusTotalURL($hash) {
Write-Host "Sleeping to avoid rate control."
Start-Sleep -Seconds 16
Write-Verbose "Checking VirusTotal: $hash"
$response = Invoke-WebRequest -Uri "https://www.virustotal.com/vtapi/v2/file/report?apikey=$apikeyVirusTotal&resource=$hash" | ConvertFrom-Json
return $response
}
# Import the CSV File and assign it to the variable $CSV
$CSV = Import-Csv -Path ".\test.csv"
#For each line in CSV get the column name value
foreach ($line in $CSV){
$hashCount = $line.ct
$hash = $line.sha256
IF($hashCount -lt 2 -and (-not ([string]::IsNullOrEmpty($hash)))){
$httpResponse = SubmitVirusTotalURL($hash)
#create a custom object to hold VT data
$obj = [PSCustomObject]@{
'hash' = $hash
'sha256' = $httpResponse.sha256
'Detections' = $httpResponse.positives
'TotalScanners' = $httpResponse.total
'VTReport' = $httpResponse.permalink
}
$hashArray += $obj
}
}
$hashArray
Sample Input File test.csv:
ct, sha256
3, A3CFFBD12ACDB85D24A13E3976B8795C5611DA05C2017755D87B5ECE38D50806
3, 0CD2F3CF62B768F4036605665E6DD888F431B8FEBDA77D07E852F12523072FFC
4, 405F03534BE8B45185695F68DEB47D4DAF04DCD6DF9D351CA6831D3721B1EFC4
1, FAKEHA534BE8B45185695F68DEB47D4DAF04DCD6DF9D351CA6831D3721B1EFC4
Which results in a csv file containing output like so:
@{Sha256=0cd2f3cf62b768f4036605665e6dd888f431b8febda77d07e852f12523072ffc Detections=0 TotalScanners=72 VTReport=https://www.virustotal.com/gui/file/0cd2f3cf62b768f4036605665e6dd888f431b8febda77d07e852f12523072ffc/detection/f-0cd2f3cf62b768f4036605665e6dd888f431b8febda77d07e852f12523072ffc-1589711909}
@{Sha256=7edc950ecfbbb043a62f31f01be2710892bb34455dd7ea435ce1346873d3f36f Detections=0 TotalScanners=69 VTReport=https://www.virustotal.com/gui/file/7edc950ecfbbb043a62f31f01be2710892bb34455dd7ea435ce1346873d3f36f/detection/f-7edc950ecfbbb043a62f31f01be2710892bb34455dd7ea435ce1346873d3f36f-1572295279}
@{Sha256=8eaa83ed280a3d7d4f8dd3e4f8cbc28cb7fc74947cfca133fb627db0bc767f30 Detections=0 TotalScanners=71 VTReport=https://www.virustotal.com/gui/file/8eaa83ed280a3d7d4f8dd3e4f8cbc28cb7fc74947cfca133fb627db0bc767f30/detection/f-8eaa83ed280a3d7d4f8dd3e4f8cbc28cb7fc74947cfca133fb627db0bc767f30-1569383486}
@{Sha256=e871e48f75b213a51cf13a3a397c1b31b10b516cb4cfb5f0682c85387d3c5ed9 Detections=0 TotalScanners=72 VTReport=https://www.virustotal.com/gui/file/e871e48f75b213a51cf13a3a397c1b31b10b516cb4cfb5f0682c85387d3c5ed9/detection/f-e871e48f75b213a51cf13a3a397c1b31b10b516cb4cfb5f0682c85387d3c5ed9-1587296148}
To add to my confusion, I created a test script which functions almost identically, minus the addition of a sha256Array @() and I'm able to pipe the output to Format-Table without issue.
What is happening here? Why do I get two different types of output with such similar code, and how do I correct these issue?
As all the comments above, avoid constructing the way you;re doing it now:
Remove this line:
$hashArray = @() #Array to hold multiple objects
Now use:
#For each line in CSV get the column name value
$CSV | ForEach-Object {
$hashCount = $_.ct
$hash = $_.sha256
If($hashCount -lt 2 -and (-not ([string]::IsNullOrEmpty($hash)))){
$httpResponse = SubmitVirusTotalURL($hash)
#create a custom object to hold VT data
[PSCustomObject]@{
'hash' = $hash
'sha256' = $httpResponse.sha256
'Detections' = $httpResponse.positives
'TotalScanners' = $httpResponse.total
'VTReport' = $httpResponse.permalink
}
}
}
As mentioned above, if this is turning into string values then the issue is with how you're piping $hasharray
to csv. You can pipe direct to csv or save in a variable to examine the object:
$hasharray = $CSV | ForEach-Object {
$hashCount = $_.ct
$hash = $_.sha256
If($hashCount -lt 2 -and (-not ([string]::IsNullOrEmpty($hash)))){
$httpResponse = SubmitVirusTotalURL($hash)
#create a custom object to hold VT data
[PSCustomObject]@{
'hash' = $hash
'sha256' = $httpResponse.sha256
'Detections' = $httpResponse.positives
'TotalScanners' = $httpResponse.total
'VTReport' = $httpResponse.permalink
}
}
}
$hasharray | Export-Csv -Path 'pathtocsv\csvname.csv' -NoTypeInformation