├── License.md ├── README.md └── VirusTotal.psm1 /License.md: -------------------------------------------------------------------------------- 1 | #Microsoft Public License (Ms-PL) 2 | 3 | This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software. 4 | 5 | ##1. Definitions 6 | 7 | The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law. 8 | 9 | A "contribution" is the original software, or any additions or changes to the software. 10 | 11 | A "contributor" is any person that distributes its contribution under this license. 12 | 13 | "Licensed patents" are a contributor's patent claims that read directly on its contribution. 14 | 15 | ##2. Grant of Rights 16 | 17 | (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create. 18 | 19 | (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software. 20 | 21 | ##3. Conditions and Limitations 22 | 23 | (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks. 24 | 25 | (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically. 26 | 27 | (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software. 28 | 29 | (D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license. 30 | 31 | (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | VirusTotalShell 2 | ============= 3 | 4 | A fork of David B Heise's VirusTotal Powershell Module
5 | http://psvirustotal.codeplex.com/SourceControl/latest#VirusTotal.psm1 6 | 7 | ##Example Usage 8 | ``` 9 | PS E:\hunt\data> Import-Module .\VirusTotal.psm1 10 | PS E:\hunt\data> Get-Command -Module VirusTotal 11 | 12 | CommandType Name ModuleName 13 | ----------- ---- ---------- 14 | Function Get-VTApiKey VirusTotal 15 | Function Get-VTReport VirusTotal 16 | Function Invoke-VTRescan VirusTotal 17 | Function Invoke-VTScan VirusTotal 18 | Function New-VTComment VirusTotal 19 | Function Set-VTApiKey VirusTotal 20 | 21 | PS E:\hunt\data> Set-VTApiKey -VTApiKey yourVTAPIkeyhere 22 | 23 | PS E:\hunt\data> Get-Help Get-VTReport 24 | 25 | NAME 26 | Get-VTReport 27 | 28 | SYNTAX 29 | Get-VTReport [-VTApiKey ] [-hash ] [] 30 | 31 | Get-VTReport [-VTApiKey ] [-file ] [] 32 | 33 | Get-VTReport [-VTApiKey ] [-uri ] [] 34 | 35 | Get-VTReport [-VTApiKey ] [-ip ] [] 36 | 37 | Get-VTReport [-VTApiKey ] [-domain ] [] 38 | 39 | 40 | ALIASES 41 | None 42 | 43 | 44 | REMARKS 45 | None 46 | ``` 47 | You can combine this script with the output from something like https://github.com/davehull/Get-StakRank#get-stakrank or hashes from Autorunsc.exe and do useful things like: 48 | ``` 49 | PS E:\hunt\data> $data = Import-Csv -Delimiter "`t" '.\FIN-Image Path-MD5.tsv' 50 | PS E:\hunt\data> $data | ? { $_.Count -lt 10 -and $_.MD5.length -gt 3 } | select -unique MD5 -ExpandProperty MD5 | % { Get-VTReport -hash $_ | select scan_date, positives, resource, verbose_msg, permalink; sleep 15 } 51 | ``` 52 | This will return something like the following: 53 | ``` 54 | scan_date : 55 | positives : 56 | resource : 06f12e6478246b0f7ef11f2a6735b876 57 | verbose_msg : The requested resource is not among the finished, queued or pending scans 58 | permalink : 59 | 60 | scan_date : 61 | positives : 62 | resource : 04113bb90f3c162ebd961a3065c15fe1 63 | verbose_msg : The requested resource is not among the finished, queued or pending scans 64 | permalink : 65 | 66 | scan_date : 2013-06-10 14:19:55 67 | positives : 0 68 | resource : bf68a382c43a5721eef03ff45faece4a 69 | verbose_msg : Scan finished, scan information embedded in this object 70 | permalink : https://www.virustotal.com/file/09eba33e313cf8f19c5a2d19ada286e9fdd09c6a99f6bf77b65fa55cc6061590/analysis/1370873995/ 71 | 72 | scan_date : 2013-11-06 03:43:51 73 | positives : 0 74 | resource : 5534ed475c61188fffa4168f28a0d893 75 | verbose_msg : Scan finished, scan information embedded in this object 76 | permalink : https://www.virustotal.com/file/10d3f4a431f259164f8abeb158381db92cbb9c02fd56e70addeab9907eb92e91/analysis/1383709431/ 77 | 78 | scan_date : 2014-01-03 21:47:59 79 | positives : 1 80 | resource : a283e768fa12ef33087f07b01f82d6dd 81 | verbose_msg : Scan finished, scan information embedded in this object 82 | permalink : https://www.virustotal.com/file/1d4d787047200fc7bcbfc03a496cafda8e49075d2fbf2ff7feab90a4fdea8f89/analysis/1388785679/ 83 | ... 84 | ``` 85 | And of course, you can pipe this out to a file by running it as follows: 86 | ``` 87 | PS E:\hunt\data> $($data | ? { $_.Count -lt 10 -and $_.MD5.length -gt 3 } | select -unique MD5 -ExpandProperty MD5 | % { Get-VTReport -hash $_ | select scan_date, positives, resource, verbose_msg, permalink | ConvertTo-Csv -Delimiter "`t" -NoTypeInformation; sleep 15 } ) | Add-Content -Encoding Ascii vt-results.tsv 88 | ``` 89 | -------------------------------------------------------------------------------- /VirusTotal.psm1: -------------------------------------------------------------------------------- 1 | #Requires -Version 3 2 | <# 3 | .SYNOPSIS 4 | Virus Total Module 5 | .DESCRIPTION 6 | Powershell Module for interaction with Virus Total's API 7 | .NOTES 8 | File Name : VirusTotal.psm1 9 | Author : David B Heise 10 | .LINK 11 | https://psvirustotal.codeplex.com 12 | #> 13 | Add-Type -AssemblyName System.Security 14 | 15 | function Set-VTApiKey { 16 | [CmdletBinding()] 17 | Param([Parameter(Mandatory=$true)][ValidateNotNull()][String] $VTApiKey, 18 | [String] $vtFileLocation = $(Join-Path $env:APPDATA 'virustotal.bin')) 19 | $inBytes = [System.Text.Encoding]::Unicode.GetBytes($VTApiKey) 20 | $protected = [System.Security.Cryptography.ProtectedData]::Protect($inBytes, $null, [System.Security.Cryptography.DataProtectionScope]::CurrentUser) 21 | [System.IO.File]::WriteAllBytes($vtfileLocation, $protected) 22 | } 23 | 24 | function Get-VTApiKey { 25 | [CmdletBinding()] 26 | Param([String] $vtFileLocation = $(Join-Path $env:APPDATA 'virustotal.bin')) 27 | if (Test-Path $vtfileLocation) { 28 | $protected = [System.IO.File]::ReadAllBytes($vtfileLocation) 29 | $rawKey = [System.Security.Cryptography.ProtectedData]::Unprotect($protected, $null, [System.Security.Cryptography.DataProtectionScope]::CurrentUser) 30 | return [System.Text.Encoding]::Unicode.GetString($rawKey) 31 | } else { 32 | throw "Call Set-VTApiKey first!" 33 | } 34 | } 35 | 36 | function Get-VTReport { 37 | [CmdletBinding()] 38 | Param( 39 | [String] $VTApiKey = (Get-VTApiKey), 40 | [Parameter(ParameterSetName="hash", ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)][String] $hash, 41 | [Parameter(ParameterSetName="file", ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)][System.IO.FileInfo] $file, 42 | [Parameter(ParameterSetName="uri", ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)][Uri] $uri, 43 | [Parameter(ParameterSetName="ipaddress", ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)][String] $ip, 44 | [Parameter(ParameterSetName="domain", ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)][String] $domain 45 | ) 46 | Begin { 47 | $fileUri = 'https://www.virustotal.com/vtapi/v2/file/report' 48 | $UriUri = 'https://www.virustotal.com/vtapi/v2/url/report' 49 | $IPUri = 'http://www.virustotal.com/vtapi/v2/ip-address/report' 50 | $DomainUri = 'http://www.virustotal.com/vtapi/v2/domain/report' 51 | 52 | function Get-Hash( 53 | [System.IO.FileInfo] $file = $(Throw 'Usage: Get-Hash [System.IO.FileInfo]'), 54 | [String] $hashType = 'sha256') 55 | { 56 | $stream = $null; 57 | [string] $result = $null; 58 | $hashAlgorithm = [System.Security.Cryptography.HashAlgorithm]::Create($hashType ) 59 | $stream = $file.OpenRead(); 60 | $hashByteArray = $hashAlgorithm.ComputeHash($stream); 61 | $stream.Close(); 62 | 63 | trap 64 | { 65 | if ($null -ne $stream) { $stream.Close(); } 66 | break; 67 | } 68 | 69 | # Convert the hash to Hex 70 | $hashByteArray | ForEach-Object { $result += $_.ToString("X2") } 71 | return $result 72 | } 73 | } 74 | Process { 75 | [String] $h = $null 76 | [String] $u = $null 77 | [String] $method = $null 78 | $body = @{} 79 | 80 | switch ($PSCmdlet.ParameterSetName) { 81 | "file" { 82 | $h = Get-Hash -file $file 83 | Write-Verbose -Message ("FileHash:" + $h) 84 | $u = $fileUri 85 | $method = 'POST' 86 | $body = @{ resource = $h; apikey = $VTApiKey} 87 | } 88 | "hash" { 89 | $u = $fileUri 90 | $method = 'POST' 91 | $body = @{ resource = $hash; apikey = $VTApiKey} 92 | } 93 | "uri" { 94 | $u = $UriUri 95 | $method = 'POST' 96 | $body = @{ resource = $uri; apikey = $VTApiKey} 97 | } 98 | "ipaddress" { 99 | $u = $IPUri 100 | $method = 'GET' 101 | $body = @{ ip = $ip; apikey = $VTApiKey} 102 | } 103 | "domain" { 104 | $u = $DomainUri 105 | $method = 'GET' 106 | $body = @{ domain = $domain; apikey = $VTApiKey}} 107 | } 108 | 109 | return Invoke-RestMethod -Method $method -Uri $u -Body $body 110 | } 111 | } 112 | 113 | function Invoke-VTScan { 114 | [CmdletBinding()] 115 | Param( 116 | [String] $VTApiKey = (Get-VTApiKey), 117 | [Parameter(ParameterSetName="file", ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] 118 | [System.IO.FileInfo] $file, 119 | [Parameter(ParameterSetName="uri", ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] 120 | [Uri] $uri 121 | ) 122 | Begin { 123 | $fileUri = 'https://www.virustotal.com/vtapi/v2/file/scan' 124 | $UriUri = 'https://www.virustotal.com/vtapi/v2/url/scan' 125 | [byte[]]$CRLF = 13, 10 126 | 127 | function Get-AsciiBytes([String] $str) { 128 | return [System.Text.Encoding]::ASCII.GetBytes($str) 129 | } 130 | } 131 | Process { 132 | [String] $h = $null 133 | [String] $u = $null 134 | [String] $method = $null 135 | $body = New-Object System.IO.MemoryStream 136 | 137 | switch ($PSCmdlet.ParameterSetName) { 138 | "file" { 139 | $u = $fileUri 140 | $method = 'POST' 141 | $boundary = [Guid]::NewGuid().ToString().Replace('-','') 142 | $ContentType = 'multipart/form-data; boundary=' + $boundary 143 | $b2 = Get-AsciiBytes ('--' + $boundary) 144 | $body.Write($b2, 0, $b2.Length) 145 | $body.Write($CRLF, 0, $CRLF.Length) 146 | 147 | $b = (Get-AsciiBytes ('Content-Disposition: form-data; name="apikey"')) 148 | $body.Write($b, 0, $b.Length) 149 | 150 | $body.Write($CRLF, 0, $CRLF.Length) 151 | $body.Write($CRLF, 0, $CRLF.Length) 152 | 153 | $b = (Get-AsciiBytes $VTApiKey) 154 | $body.Write($b, 0, $b.Length) 155 | 156 | $body.Write($CRLF, 0, $CRLF.Length) 157 | $body.Write($b2, 0, $b2.Length) 158 | $body.Write($CRLF, 0, $CRLF.Length) 159 | 160 | $b = (Get-AsciiBytes ('Content-Disposition: form-data; name="file"; filename="' + $file.Name + '";')) 161 | $body.Write($b, 0, $b.Length) 162 | $body.Write($CRLF, 0, $CRLF.Length) 163 | $b = (Get-AsciiBytes 'Content-Type:application/octet-stream') 164 | $body.Write($b, 0, $b.Length) 165 | 166 | $body.Write($CRLF, 0, $CRLF.Length) 167 | $body.Write($CRLF, 0, $CRLF.Length) 168 | 169 | $b = [System.IO.File]::ReadAllBytes($file.FullName) 170 | $body.Write($b, 0, $b.Length) 171 | 172 | $body.Write($CRLF, 0, $CRLF.Length) 173 | $body.Write($b2, 0, $b2.Length) 174 | 175 | $b = (Get-AsciiBytes '--') 176 | $body.Write($b, 0, $b.Length) 177 | 178 | $body.Write($CRLF, 0, $CRLF.Length) 179 | 180 | 181 | Invoke-RestMethod -Method $method -Uri $u -ContentType $ContentType -Body $body.ToArray() 182 | } 183 | "uri" { 184 | $h = $uri 185 | $u = $UriUri 186 | $method = 'POST' 187 | $body = @{ url = $uri; apikey = $VTApiKey} 188 | Invoke-RestMethod -Method $method -Uri $u -Body $body 189 | } 190 | } 191 | } 192 | } 193 | 194 | function New-VTComment { 195 | [CmdletBinding()] 196 | Param( 197 | [String] $VTApiKey = (Get-VTApiKey), 198 | [Parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)][String] $hash, 199 | [Parameter(Mandatory=$true)][ValidateNotNull()][String] $Comment 200 | ) 201 | 202 | Process { 203 | $u = 'https://www.virustotal.com/vtapi/v2/comments/put' 204 | $method = 'POST' 205 | $body = @{ resource = $hash; apikey = $VTApiKey; comment = $Comment} 206 | 207 | return Invoke-RestMethod -Method $method -Uri $u -Body $body 208 | } 209 | } 210 | 211 | function Invoke-VTRescan { 212 | [CmdletBinding()] 213 | Param( 214 | [String] $VTApiKey = (Get-VTApiKey), 215 | [Parameter(Mandatory=$true, ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)][String] $hash 216 | ) 217 | Process { 218 | $u = 'https://www.virustotal.com/vtapi/v2/file/rescan' 219 | $method = 'POST' 220 | $body = @{ resource = $hash; apikey = $VTApiKey} 221 | return Invoke-RestMethod -Method $method -Uri $u -Body $body 222 | } 223 | } 224 | --------------------------------------------------------------------------------