Search code examples
powershellpsobjectselect-object

Custom PSObject is returning Array instead of the Object properties


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?


Solution

  • 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