├── CHANGELOG.md ├── CONTRIBUTORS.md ├── CyAPI.ps1 ├── CyCLI.psd1 ├── CyCLI.psm1 ├── CyConvenience.ps1 ├── CyCrypto.ps1 ├── CyDATA_ApplicationDefinitions.json ├── CyDevices.ps1 ├── CyGlobalLists.ps1 ├── CyHelper.ps1 ├── CyInstallers.ps1 ├── CyOpticsDetections.ps1 ├── CyOpticsInstaQuery.ps1 ├── CyOpticsPackages.ps1 ├── CyOpticsRules.ps1 ├── CyPolicies.ps1 ├── CyTDR.ps1 ├── CyThreats.ps1 ├── CyUsers.ps1 ├── CyZones.ps1 ├── FAQ.md ├── Invoke-InstallModule.ps1 ├── LICENSE.txt ├── MANUAL-INSTALL-FROM-SOURCE.md ├── README-API.md └── README.md /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## v.0.9.7 4 | * Fixed a bug in Set-CyPolicyForDevice where setting policy would not when the device record was not either a full record, or a shallow record (from devicelist), but a devicelist -perzone record. Thank you to Philip Hohl for reporting the issue. 5 | 6 | ## v.0.9.6 7 | * Fixed a bug in Get-CyZone where retrieving by name would not work when an "API" parameter was present (Scope = None). Thank you to Philip Hohl for reporting the issue. 8 | 9 | ## v.0.9.5 10 | * Reverted an (unreleased-to-powershellgallery) change to Add-CyHashToGlobalList because it had introduced buggy behavior 11 | * Performance improvements to type conversion 12 | 13 | ## v.0.9.4 14 | * Small fixes to threat handling 15 | 16 | ## v.0.9.3 17 | * Added last_found to converted fields for Convert-CyObject 18 | 19 | ## v.0.9.2 20 | * Better safeguards in Set-CyPolicyForDevice 21 | * Get-CyThreatList added 22 | 23 | ## v.0.9.1 24 | * Get-CyDeviceList now uses the now-supported page_size of 10000, which speeds up device record retrieval A LOT; performance enhancements to conversion. 25 | * Updated Read-CyData to accept a "Fields" parameter to restrict datetime conversion to certain fields 26 | * Exposed Convert-CyObject, added a "Fields" parameter to restrict datetime conversion to certain fields 27 | 28 | ## v.0.9.0 29 | * Get-CyDetectionRuleList added 30 | * Get-CyDetectionRuleDetail added. 31 | * Get-CyDetectionExceptionList added 32 | * Get-CyDetectionExceptionDetail added 33 | * Added Optics' "ActivationTime" field datetime conversion support 34 | * Introduced "IsDirty" property to track write operations, in preparation of adding simple caching to the module. 35 | 36 | ## v.0.8.9 37 | * New-CyInstaQuery - "description" is now auto-populated with "Name" when parameter is ommitted, because it is required by the backend API 38 | * Get-CyDeviceDetailByMac updated to support multi-device responses 39 | 40 | ## v.0.8.8 41 | * Remove-CyDetection added 42 | 43 | ## v.0.8.7 44 | * Set-CyPolicyForDevice, when called with improper arguments, would still call the backend API. Backend API would not ignore the call, but execute an improper action. Added code to detect and prevent this from affecting CyCLI module users. 45 | 46 | ## v.0.8.6 47 | * Add-CyDeviceToZone, when called with improper arguments, would still call the backend API. Backend API would not ignore the call, but execute an improper action. Added code to detect and prevent this from affecting CyCLI module users. 48 | 49 | ## v.0.8.5 50 | * Added Get-CyLockdownStatus 51 | 52 | ## v.0.8.4 53 | * Happy New Year 2019! 54 | * Added Update-CyZone API 55 | 56 | ## v.0.8.3 57 | * Added new OPTICS APIs: 58 | * Get-CyDetectionExceptionList 59 | * Get-CyDetectionExceptionDetail 60 | 61 | ## v.0.8.2 62 | * Updated application definitions retrieval logic with autocompletion 63 | * Changed autocompletion implementation across module 64 | * Renamed Get-CyDateFromString to ConvertFrom-CyDateString 65 | * Moved OPTICS APIs into separate files for detections, packages, etc. 66 | * Fixed a few bugs with Add-CyPolicyExclusionsForApplication (dynamic expansion of "Application" parameter name), removed support for custom definitions for now 67 | * Added new OPTICS APIs: 68 | * Get-CyDetectionRecentList 69 | * Get-CyDetectionList updated to support query parameters. NOTE: currently, multi-valued query parameters are not yet supported 70 | * New-CyInstaQuery 71 | * Get-CyInstaQueryResults 72 | * Get-CyInstaQueries 73 | 74 | ## v.0.8.1 75 | * Added more AV definitions for exclusions to data file 76 | 77 | ## v.0.8.0 78 | * Small bug fix in CyPolicies 79 | * Added better error condition comment in CyAPI.ps1 80 | * Incorporated 2Dman's policy assignment patch 81 | 82 | ## v.0.7.9 83 | * Removed redundant file from a published release 84 | 85 | ## v.0.7.8 86 | * Get-CyTDRsForAllConsoles added 87 | * Add-CyPolicyExclusionsForApplication added to add application-specific configuration to policies from templates that are (for now) part of the module 88 | * Added templates for some common application exclusions 89 | 90 | ## v.0.7.7 91 | * Add-CyPolicyExclusionsForApplication added; can add policy exclusions for known AV/EPP applications from JSON definition files. 92 | 93 | ## v.0.7.6 94 | * New-CyUser transaction added 95 | * Invoke-CySendUserInvite transaction added 96 | 97 | ## v.0.7.5 98 | * OPTICS Update-CyDetection now works 99 | * Bugfixes in *-CyPolicy methods to better support referencing users by email when an non-session API token is used 100 | 101 | ## v.0.7.4 102 | * Updated OPTICS Update-CyDetection method 103 | * Added convenience methods: GetUserByEmail 104 | * Updated *-CyPolicy methods to be more robust + comfortable 105 | * Accept "email" to identify the user 106 | * Adding an exclusion already in the set will not add a duplicate but silently skip the action 107 | * Changed policy defaults to have memory protection disabled in empty policy 108 | * Changed policy defaults to have OPTICS disabled in empty policy 109 | * Changed policy defaults to have "Watch for New Files" disabled in empty policy 110 | * Changed policy defaults to have "Auto Upload" disabled in empty policy 111 | * Changed policy defaults to have "Background Threat Detection" disabled in empty policy 112 | 113 | ## v.0.7.3 114 | * Added cmdlets for policy creation, cloning, common list settings changes: New-CyPolicy, Update-CyPolicy, Add-CyPolicyListSetting, Get-CyPolicyScaffold 115 | 116 | ## v.0.7.2 117 | * Packaging change for powershellgallery.com 118 | 119 | ## v.0.7.1 120 | * Added Remove-CyPolicy 121 | * Added License file to module 122 | 123 | ## v.0.7.0 124 | * Release with some OPTICS transactions 125 | 126 | ## v 0.6.7 (development only) 127 | * Added first new OPTICS transactions 128 | * Added date conversion support for OPTICS detections 129 | 130 | ## v 0.6.6 131 | * Added "Create policy" API transaction 132 | * Added auto-renewal of API token after 180s 133 | * Added Clear-CyAPIHandle cmdlet to clear the session API handle 134 | 135 | ## v 0.6.5 136 | * Updated function names 137 | * Prepared auto-renewal for tokens 138 | 139 | ## v 0.6.4 (2018.07.17) 140 | * Added support for first OPTICS APIs 141 | 142 | ## v 0.6.3 (2018.05.30) 143 | * Get-CyAPI supports positional parameter for console selection, allowing for short-hand form "Get-CyAPI " 144 | * Exposed some JWT primitives 145 | * Added more -verbose support to Get-CyAPI 146 | 147 | ## v 0.6.2 (2018.05.29) 148 | * Encapsulated the REST method call function to allow for proxy support 149 | * Updated Get-CyAPI to support proxies with/without credential access 150 | 151 | ## v 0.6.1 (2018.05.20) 152 | * Updated New-CyConsoleConfig code to automatically prompt for region - eliminates the most common issue 153 | * Updated README.md to remove outdated/confusing content 154 | 155 | ## v 0.6.0 (2018.05.09) 156 | * API seems to sometimes return dates as strings like this: "2018-05-09T12:54:27.7711212". Updated date conversion to support these cases. 157 | 158 | ## v 0.5.9 (2018.04.18) 159 | * Bug fix in New-CyConsoleConfig when consoles.json was empty 160 | 161 | ## v 0.5.8 (2018.04.13) 162 | * Bug fix in New-CyConsoleConfig and Get-CyAPI to (a) always check if credentials are valid before saving them, and (b) return error messages that point to the most common root cause (wrong shard URL) 163 | 164 | ## v 0.5.6 (2018.04.03) 165 | * Bug fix in Get-CyDeviceDetailByMac to include date conversion 166 | * Bug fix in Convert-CyObject to fix date conversion - seems like a weird property assignment bug in Powershell. 167 | 168 | ## v 0.5.4 (2018.03.28) 169 | * Minor bug fix for creation of backup `consoles.json` file 170 | 171 | ## v 0.5.3 (2018.03.28) 172 | 173 | * Creates a .bak backup of `consoles.json` before it writes it (useful in case of manual, syntax-breaking edits to the JSON file) 174 | 175 | ## v 0.5.2 (2018.03.28) 176 | 177 | * _Breaking change_: Better credentials handling - only DPAPI/SecureString supported now; *this is a breaking change* and you will need to update your existing consoles.json with encrypted credentials. To migrate, use a command similar to this: ```(Get-CyConsoleConfig) | where APISecret -ne $null | foreach { $pw = ConvertTo-SecureString -Force -String $_.APISecret -AsPlainText ; New-CyConsoleConfig -Console "$($_.ConsoleId)_2" -APISecret $pw -APIId $_.APIId -Token $_.Token -APITenantId $_.APITenantId -APIAuthUrl $_.APIUrl -TDRUrl $_.TDRUrl }```. Check TDRUrl/APIUrl for correctness in consoles.json afterwards. This is only relevant for users of the pre-release version with a pre-existing consoles.json file. 178 | * _Breaking change_: Renamed some verbs and nouns based on PS best practices 179 | * Mild refactoring after running PS Script Analyzer 180 | 181 | ## v 0.5.1 (2018.03.28) 182 | 183 | * First candidate for public release, pending PSScriptAnalyzer fixes -------------------------------------------------------------------------------- /CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | # Contributors 2 | 3 | * Original author: Jan Tietze (github.com/jan-tee/) 4 | * Patches added by: 5 | * 2Dman (github.com/2dman) -------------------------------------------------------------------------------- /CyAPI.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NAME 3 | Cylance-API 4 | 5 | .SYNOPSIS 6 | A collection of verbs to work with the Cylance Console API v2 7 | 8 | .DESCRIPTION 9 | Allows retrieval and manipulation of configuration objects in the Cylance console using API v2. 10 | 11 | .LINK 12 | Blog: http://tietze.io/ 13 | Jan Tietze 14 | #> 15 | 16 | <# 17 | Represents the API handle returned by API after authentication 18 | #> 19 | Class CylanceAPIHandle { 20 | [string]$AccessToken 21 | [string]$BaseUrl 22 | [securestring]$APISecret 23 | [string]$APIId 24 | [string]$APITenantId 25 | [datetime]$ExpirationTime 26 | [string]$Scope 27 | [bool]$IsDirty # $true when there was any write operation attempted at all 28 | } 29 | 30 | Class CylanceGlobalSettings { 31 | [string]$Proxy 32 | [PSCredential]$ProxyCredential 33 | [bool]$ProxyUseDefaultCredentials 34 | } 35 | 36 | # constants 37 | $script:FieldsValidateSet = @("date_first_registered", "date_offline", "date_last_modified", "date_found", "cert_timestamp", "date_last_login", "date_email_confirmed", "date_created", "date_modified", "OccurrenceTime", "ActivationTime", "ReceivedTime", "lockdown_expiration", "lockdown_initiated", "last_found") 38 | 39 | 40 | <# 41 | .TODO 42 | At some point In the future, artifacts for the API will be classes... if I can figure out how to use dynamic properties in Powershell. 43 | Type-safe objects would eliminate some issues people run into (confusion about when to use IDs and when to use objects) 44 | #> 45 | 46 | Class CylanceDevice { 47 | [string]$Id 48 | } 49 | Class CylanceZone { 50 | [string]$Id 51 | } 52 | Class CylanceThreat { 53 | [string]$Id 54 | } 55 | 56 | <# 57 | .SYNOPSIS 58 | Gets an API access token for the authenticated access to the Console API, valid for 30 minutes. 59 | 60 | .PARAMETER APIId 61 | Optional. API ID 62 | 63 | .PARAMETER APISecret 64 | Optional. API Secret 65 | 66 | .PARAMETER APITenantId 67 | Optional. API Tenant ID 68 | 69 | .PARAMETER APIAuthUrl 70 | Optional. URL to obtain token, e.g. "https://protectapi<-region>.cylance.com/auth/v2/token". Defaults to EUC1 region. 71 | Use the value obtained from the API documentation for your console shard. 72 | 73 | .PARAMETER Scope 74 | Optional. If you need to access multiple tenants in parallel, use "None" as scope and collect the API object returned. 75 | 76 | .PARAMETER Console 77 | Optional. The console ID in your consoles.json file. See the README for CyCLI module. 78 | #> 79 | function Get-CyAPI { 80 | Param ( 81 | [parameter(Mandatory=$true, ParameterSetName="Direct")] 82 | [String]$APIId, 83 | [parameter(Mandatory=$true, ParameterSetName="Direct")] 84 | [SecureString]$APISecret, 85 | [parameter(Mandatory=$true, ParameterSetName="Direct")] 86 | [String]$APITenantId, 87 | [parameter(Mandatory=$false, ParameterSetName="Direct")] 88 | [String]$APIAuthUrl = "https://protectapi-euc1.cylance.com/auth/v2/token", 89 | [parameter(Mandatory=$false)] 90 | [ValidateSet ("Session", "None")] 91 | [String]$Scope = "Session" 92 | ) 93 | DynamicParam { 94 | Get-CyConsoleArgumentAutoCompleter -Mandatory -ParameterName "Console" -ParameterSetName "ByReference" -Position 0 95 | } 96 | 97 | Begin { 98 | $expirationTimeout = 1800 99 | switch ($PSCmdlet.ParameterSetName) 100 | { 101 | "Direct" 102 | { 103 | # decrypt DPAPI protected string into SecureString 104 | # https://social.technet.microsoft.com/wiki/contents/articles/4546.working-with-passwords-secure-strings-and-credentials-in-windows-powershell.aspx 105 | $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($APISecret) 106 | $pw = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR) 107 | 108 | $claims = @{} 109 | 110 | $jwtBearerToken = Get-CyJWTToken ` 111 | -claims $claims ` 112 | -expirationSeconds $expirationTimeout ` 113 | -secret $pw ` 114 | -iss "http://cylance.com" ` 115 | -tid $APITenantId ` 116 | -APIid $APIId 117 | 118 | $payload = @{ "auth_token" = $jwtBearerToken } | ConvertTo-Json 119 | 120 | $baseUrl = ([System.Uri]$APIAuthUrl).Scheme + "://" + ([System.Uri]$APIAuthUrl).Host 121 | 122 | $rest = @{ 123 | Method = "POST" 124 | ContentType = "application/json; charset=utf-8" 125 | Uri = "$($baseUrl)/auth/v2/token" 126 | Body = $payload 127 | } 128 | 129 | try { 130 | Write-Verbose "Requesting auth for JWT token: $($jwtBearerToken)" 131 | $result = Invoke-CyRestMethod @rest -DoNotRenewToken 132 | } 133 | catch { 134 | Write-Error $_.Exception 135 | $result = $_.Exception.Response.GetResponseStream() 136 | $reader = New-Object System.IO.StreamReader($result) 137 | $reader.BaseStream.Position = 0 138 | $reader.DiscardBufferedData() 139 | Write-Error "Could not obtain valid API token. This may mean that (a) your API credentials are incorrect or (b) your API auth URL is incorrect (use Get-Help Get-CyAPI to get a list of URLs), and your code is attempting to authenticate to the wrong global API instance, or (c) the time or time zone on your device is set incorrectly." 140 | if ($Scope -eq "Session") { 141 | $script:GlobalCyAPIHandle = $null 142 | } 143 | throw $_.Exception 144 | return 145 | } 146 | 147 | [CylanceAPIHandle]$r = New-Object CylanceAPIHandle 148 | $r.AccessToken = $result.access_token 149 | $r.BaseUrl = $baseUrl 150 | $r.APISecret = $APISecret 151 | $r.APIId = $APIId 152 | $r.APITenantId = $APITenantId 153 | $r.ExpirationTime = (Get-Date).AddSeconds(180) # force token renewal after 3 minutes at all times. 154 | $r.Scope = $Scope 155 | 156 | if ($Scope -eq "Session") { 157 | $script:GlobalCyAPIHandle = $r 158 | } else { 159 | $r 160 | } 161 | } 162 | "ByReference" 163 | { 164 | $ConsoleDetails = (Get-CyConsoleConfig) | Where-Object ConsoleId -eq $PSBoundParameters.Console 165 | 166 | # decrypt DPAPI protected string into SecureString 167 | # https://social.technet.microsoft.com/wiki/contents/articles/4546.working-with-passwords-secure-strings-and-credentials-in-windows-powershell.aspx 168 | $SecureStringPw = $ConsoleDetails.APISecret | ConvertTo-SecureString 169 | 170 | if ($null -ne $ConsoleDetails.APIUrl) { 171 | $APIAuthUrl = $ConsoleDetails.APIUrl 172 | } 173 | $rest = @{ 174 | APIId = $ConsoleDetails.APIId 175 | APISecret = $SecureStringPw 176 | APITenantId = $ConsoleDetails.APITenantId 177 | APIAuthUrl = $APIAuthUrl 178 | Scope = $Scope 179 | } 180 | Get-CyAPI @rest 181 | } 182 | } 183 | } 184 | 185 | Process { 186 | } 187 | } 188 | 189 | <# 190 | .SYNOPSIS 191 | Sets global settings for the API module, e.g. proxy server settings 192 | 193 | .PARAMETER Proxy 194 | Specifies that the module uses a proxy server for the request, rather than connecting directly to the Internet resource. Enter the URI of a network proxy server. 195 | 196 | .PARAMETER ProxyCredential 197 | 198 | Specifies a user account that has permission to use the proxy server that is specified by the Proxy parameter. The default is the current user. 199 | 200 | Type a user name, such as "User01" or "Domain01\User01", or enter a PSCredential object, such as one generated by the Get-Credential cmdlet. 201 | 202 | This parameter is valid only when the Proxy parameter is also used in the command. You cannot use the ProxyCredential and ProxyUseDefaultCredentials parameters in the same command. 203 | 204 | .PARAMETER ProxyUseDefaultCredentials 205 | Indicates that the cmdlet uses the credentials of the current user to access the proxy server that is specified by the Proxy parameter. 206 | 207 | This parameter is valid only when the Proxy parameter is also used in the command. You cannot use the ProxyCredential and ProxyUseDefaultCredentials parameters in the same command. 208 | #> 209 | function Set-CyGlobalSettings { 210 | Param( 211 | [parameter(Mandatory=$false)] 212 | [String]$Proxy = $null, 213 | [parameter(Mandatory=$false)] 214 | [PSCredential]$ProxyCredential = $null, 215 | [parameter(Mandatory=$false)] 216 | [Switch]$ProxyUseDefaultCredentials 217 | ) 218 | 219 | $s = $script:GlobalCyGlobalSettings 220 | 221 | if ($s -eq $null) { 222 | $s = New-Object CylanceGlobalSettings 223 | } 224 | 225 | if (![String]::IsNullOrEmpty($Proxy)) { 226 | $s.Proxy = $Proxy 227 | if ($ProxyCredential -ne $null) { 228 | $s.ProxyCredential = $ProxyCredential 229 | } 230 | } 231 | 232 | if ($ProxyUseDefaultCredentials -eq $true) { 233 | $s.ProxyUseDefaultCredentials = $ProxyUseDefaultCredentials 234 | } else { 235 | $s.ProxyUseDefaultCredentials = $false 236 | } 237 | 238 | $script:GlobalCyGlobalSettings = $s 239 | } 240 | 241 | <# 242 | .SYNOPSIS 243 | Retrieves the current global settings for the module session 244 | #> 245 | function Get-CyGlobalSettings { 246 | if ($script:GlobalCyGlobalSettings -eq $null) { 247 | $script:GlobalCyGlobalSettings = New-Object CylanceGlobalSettings 248 | } 249 | $script:GlobalCyGlobalSettings 250 | } 251 | 252 | <# 253 | .SYNOPSIS 254 | Gets ALL pages for paged query results with maximum page size. 255 | 256 | .PARAMETER API 257 | Optional. API Handle (use only when not using session scope). 258 | 259 | .PARAMETER QueryParams 260 | Optional. If you need to add any query parameters, supply them in a Hashtable. 261 | 262 | .PARAMETER Fields 263 | Optional. If you wish to apply non-standard (non-JSON) value conversion on 264 | only certain fields (typically date/time fields), you can speed up the 265 | processing by explicitly listing these here. 266 | #> 267 | function Read-CyData { 268 | Param ( 269 | [parameter(Mandatory=$true)] 270 | [ValidateNotNullOrEmpty()] 271 | [CylanceAPIHandle]$API, 272 | [parameter(Mandatory=$true)] 273 | [string]$Uri, 274 | [parameter(Mandatory=$false)] 275 | [Hashtable]$QueryParams = @{}, 276 | [parameter(Mandatory=$false)] 277 | [int]$PageSize = 200, 278 | [Parameter(Mandatory=$false)] 279 | # copy from $script:FieldsValidateSet 280 | [ValidateSet("date_first_registered", "date_offline", "date_last_modified", "date_found", "cert_timestamp", "date_last_login", "date_email_confirmed", "date_created", "date_modified", "OccurrenceTime", "ActivationTime", "ReceivedTime", "lockdown_expiration", "lockdown_initiated")] 281 | [String[]]$Fields = @("date_first_registered", "date_offline", "date_last_modified", "date_found", "cert_timestamp", "date_last_login", "date_email_confirmed", "date_created", "date_modified", "OccurrenceTime", "ActivationTime", "ReceivedTime", "lockdown_expiration", "lockdown_initiated") 282 | ) 283 | 284 | $page = 1 285 | $devices = do { 286 | $params = @{ 287 | "page" = $page 288 | "page_size" = $PageSize 289 | } 290 | foreach ($key in $QueryParams.Keys) { 291 | $params.$key = $QueryParams.$key 292 | } 293 | 294 | foreach ($key in $params.Keys) { 295 | Write-Verbose "Read-CyData: GET ${Uri} | $($key) = $($params.$key)" 296 | } 297 | 298 | $rest = @{ 299 | API = $API 300 | Method = "GET" 301 | Uri = $Uri 302 | Body = $params 303 | } 304 | 305 | $resp = Invoke-CyRestMethod @rest 306 | 307 | $resp.page_items 308 | Write-Verbose "Response was page $($resp.page_number) of $($resp.total_pages) pages" 309 | 310 | $page++ 311 | } while ($resp.page_number -lt $resp.total_pages) 312 | 313 | if ($null -ne $devices) { 314 | $devices | Convert-CyObject -Fields $Fields 315 | } 316 | } 317 | 318 | <# 319 | .SYNOPSIS 320 | Returns the currently active global (session) CyAPIHandle, if one is set 321 | #> 322 | Function Get-CyAPIHandle { 323 | $GlobalCyAPIHandle 324 | } 325 | 326 | <# 327 | .SYNOPSIS 328 | Clears the currently active global (session) CyAPIHandle, if one is set 329 | #> 330 | Function Clear-CyAPIHandle { 331 | $script:GlobalCyAPIHandle = $null 332 | } 333 | 334 | <# 335 | .SYNOPSIS 336 | Converts a date string as returned from the API to a DateTime object 337 | 338 | .PARAMETER Date 339 | The date string as returned by the API 340 | #> 341 | function ConvertFrom-CyDateString { 342 | Param ( 343 | [Parameter(Mandatory=$true, Position=1)] 344 | [String]$Date 345 | ) 346 | # convert e.g. 2018-03-07T13:21:07 to Date 347 | # convert e.g. 2018-03-07T13:21:07.123 to Date (date_offline uses fractional seconds) 348 | # convert e.g. 2018-09-01T14:15:47.164Z to Date (OccurenceTime uses Z suffix) 349 | Write-Verbose "Converting date $($Date) to [DateTime]" 350 | if ($Date -match ".*Z") 351 | { 352 | # ends with "Z"; remove the last character 353 | $dt = [DateTime]::ParseExact($Date -replace ".$", "yyyy-MM-ddTHH:mm:ss.FFFFFFF", [Globalization.CultureInfo]::InvariantCulture, [Globalization.DateTimeStyles]::AssumeUniversal) 354 | } else { 355 | $dt = [DateTime]::ParseExact($Date, "yyyy-MM-ddTHH:mm:ss.FFFFFFF", [Globalization.CultureInfo]::InvariantCulture, [Globalization.DateTimeStyles]::AssumeUniversal) 356 | } 357 | Write-Verbose "Conversion result: $($dt)" 358 | return $dt 359 | } 360 | 361 | <# 362 | .SYNOPSIS 363 | Converts a date to a date/time string as expected by the API 364 | 365 | .PARAMETER Date 366 | The date to convert to string 367 | #> 368 | function ConvertTo-CyDateString { 369 | Param ( 370 | [Parameter(Mandatory=$true, Position=1)] 371 | [DateTime]$Date 372 | ) 373 | Write-Verbose "Converting date $($Date) to string" 374 | 375 | $dt = ([DateTime]$Date).ToUniversalTime() 376 | $r = $dt.ToString("yyyy-MM-ddTHH:mm:ss.FFFZ", [Globalization.CultureInfo]::InvariantCulture) 377 | Write-Verbose "Conversion result: $($r)" 378 | return $r 379 | } 380 | 381 | <# 382 | .SYNOPSIS 383 | Converts "date" strings received through the JSON API and turns them into "date" properties. 384 | 385 | .NOTES 386 | When you omit the "Field" parameter and all fields are checked vs. e.g. 2, the performance difference is significant: 387 | 388 | 139451 device records, converting only "date_offline,date_first_registered": 53.8 seconds 389 | 139451 device records, converting all "date_offline,date_first_registered": 203.6 seconds 390 | 391 | .PARAMETER Fields 392 | Optional. The list of field names to attempt to convert to datetime. Will attempt to convert all known datetime fields if not given. Can be used to optimize performance when processing many objects in a pipeline. 393 | #> 394 | function Convert-CyObject { 395 | Param ( 396 | [Parameter(Mandatory=$true, Position=1, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] 397 | [PSCustomObject]$CyObject, 398 | [Parameter(Mandatory=$false)] 399 | # copy from $script:FieldsValidateSet 400 | [ValidateSet("date_first_registered", "date_offline", "date_last_modified", "date_found", "cert_timestamp", "date_last_login", "date_email_confirmed", "date_created", "date_modified", "OccurrenceTime", "ActivationTime", "ReceivedTime", "lockdown_expiration", "lockdown_initiated", "last_found")] 401 | [String[]]$Fields = @("date_first_registered", "date_offline", "date_last_modified", "date_found", "cert_timestamp", "date_last_login", "date_email_confirmed", "date_created", "date_modified", "OccurrenceTime", "ActivationTime", "ReceivedTime", "lockdown_expiration", "lockdown_initiated", "last_found") 402 | ) 403 | Begin { 404 | } 405 | Process { 406 | ForEach ($f in $Fields) { 407 | try { 408 | if (($null -ne $CyObject.$f) -and ($CyObject.$f -isnot [DateTime]) -and (![String]::IsNullOrEmpty($CyObject.$f))) { 409 | Write-Verbose "Converting field $($f) (value: $($CyObject.$f)) to date time value" 410 | $newval = ConvertFrom-CyDateString $CyObject.$f 411 | # I think we hit a bug in PowerShell. Previous code was $CyObject.$f = $newval. 412 | # This would break on date conversion with a PropertyAssignmentException when called from Get-CyDeviceDetailByMac, but not when called by Get-CyDeviceDetail 413 | $CyObject | Add-Member $f $newval -Force 414 | Write-Verbose "Conversion result for field $($f): $($CyObject.$f) $($newval)" 415 | } 416 | } catch [FormatException] { 417 | Write-Error "Problem converting field $($f) to date time (value: $($CyObject.$f))" 418 | } 419 | } 420 | 421 | $CyObject 422 | } 423 | } 424 | 425 | <# 426 | .SYNOPSIS 427 | Invokes a REST method using the proxy configuration stored in the API object. 428 | 429 | .PARAMETER API 430 | API Handle. 431 | 432 | .PARAMETER Uri 433 | The URI for the REST method 434 | 435 | .PARAMETER Body 436 | The request body 437 | 438 | .PARAMETER Headers 439 | Any headers to transmit. 'Accept = "application/json"' is added if no headers are specified. 440 | #> 441 | function Invoke-CyRestMethod { 442 | Param( 443 | [parameter(Mandatory=$false)] 444 | [CylanceAPIHandle]$API = $null, 445 | [parameter(Mandatory=$true)] 446 | [string]$Uri, 447 | [parameter(Mandatory=$true)] 448 | [string]$Method, 449 | [parameter(Mandatory=$false)] 450 | [object]$Body = $null, 451 | [parameter(Mandatory=$false)] 452 | [Hashtable]$Headers = @{}, 453 | [parameter(Mandatory=$false)] 454 | [string]$ContentType = $null, 455 | [parameter(Mandatory=$false)] 456 | [String]$Proxy = $null, 457 | [parameter(Mandatory=$false)] 458 | [PSCredential]$ProxyCredential = $null, 459 | [parameter(Mandatory=$false)] 460 | [string]$OutFile = $null, 461 | [parameter(Mandatory=$false)] 462 | [Switch]$ProxyUseDefaultCredentials, 463 | [Switch]$DoNotRenewToken 464 | ) 465 | 466 | Write-Verbose "Entry into Invoke-CyRestMethod: DoNotRenewToken=$($DoNotRenewToken)" 467 | 468 | if ((!$DoNotRenewToken) -and ($API -ne $null) -and ((Get-Date) -gt $API.ExpirationTime)) { 469 | # renew token automatically 470 | Write-Verbose "Renewing token: $($API | out-string)" 471 | 472 | $APIrenewed = Get-CyAPI ` 473 | -Scope None ` 474 | -APIId $API.APIId ` 475 | -APITenantId $API.APITenantId ` 476 | -APISecret $API.APISecret ` 477 | -APIAuthUrl $API.BaseUrl 478 | 479 | Write-Verbose "New token: $($APIrenewed | out-string)" 480 | 481 | # replace relevant token content in API handle object 482 | $API.ExpirationTime = $APIrenewed.ExpirationTime 483 | $API.AccessToken = $APIrenewed.AccessToken 484 | 485 | $API = $APIrenewed 486 | } 487 | 488 | if (!$Headers.ContainsKey("Accept")) 489 | { 490 | $Headers.Accept = "application/json"; 491 | } 492 | 493 | if (!$Headers.ContainsKey("Authorization")) 494 | { 495 | if ($API -ne $null) 496 | { 497 | $Headers.Authorization = "Bearer $($API.AccessToken)" 498 | } 499 | } 500 | 501 | $rest = @{ 502 | Method = $Method 503 | Uri = $Uri 504 | Headers = $Headers 505 | UserAgent = "PowerShell/CyCLI" 506 | } 507 | 508 | if ($OutFile -ne $null) { 509 | $rest.OutFile = $OutFile 510 | } 511 | 512 | if ($Body -ne $null) { 513 | $rest.Body = $Body 514 | } 515 | 516 | if (![String]::IsNullOrEmpty($ContentType)) { 517 | $rest.ContentType = $ContentType 518 | } 519 | 520 | $settings = Get-CyGlobalSettings 521 | 522 | if (![String]::IsNullOrEmpty($settings.Proxy) -and [String]::IsNullOrEmpty($Proxy)) { 523 | # only use proxy from global settings if no proxy given explicitly, and a proxy is defined in global settings 524 | $Proxy = $settings.Proxy 525 | if ($settings.ProxyCredential -ne $null) { 526 | $ProxyCredential = $settings.ProxyCredential 527 | } 528 | if ($settings.ProxyUseDefaultCredentials -eq $true) { 529 | $ProxyUseDefaultCredentials = $settings.ProxyUseDefaultCredentials 530 | } 531 | } 532 | 533 | # use explicit proxy settings, or proxy settings retrieved from global settings 534 | if (![String]::IsNullOrEmpty($Proxy)) { 535 | $rest.Proxy = $Proxy 536 | if ($ProxyCredential -ne $null) { 537 | $rest.ProxyCredential = $ProxyCredential 538 | } 539 | if ($ProxyUseDefaultCredentials -eq $true) { 540 | $rest.ProxyUseDefaultCredentials = $ProxyUseDefaultCredentials 541 | } 542 | } 543 | 544 | Write-Verbose "Invoking CyREST method using REST params:" 545 | Write-Verbose "$($rest | Select-Object -ExcludeProperty Body | ConvertTo-Json)" 546 | Write-Verbose "Body: $($rest | Select-Object Body)" 547 | 548 | Invoke-RestMethod @rest 549 | 550 | if (($null -ne $API) -and ($Method -match "(PUT|POST|DELETE)")) { 551 | $API.IsDirty = $true 552 | } 553 | } -------------------------------------------------------------------------------- /CyCLI.psd1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jan-tee/cycli/277b61bfc902743ae281240364980630cbad76f0/CyCLI.psd1 -------------------------------------------------------------------------------- /CyCLI.psm1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jan-tee/cycli/277b61bfc902743ae281240364980630cbad76f0/CyCLI.psm1 -------------------------------------------------------------------------------- /CyConvenience.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | A collection of convenience verbs to work with the Cylance Console API v2. 4 | 5 | .DESCRIPTION 6 | Contains methods that are helpful, but do not represent API primitives, or are wrappers around API methods. 7 | 8 | .LINK 9 | Blog: http://tietze.io/ 10 | Jan Tietze 11 | #> 12 | 13 | <# 14 | .SYNOPSIS 15 | Gets a user by email 16 | 17 | .PARAMETER Email 18 | The user's email address 19 | #> 20 | function Get-CyUserByEmail { 21 | [CmdletBinding(DefaultParameterSetName="All")] 22 | Param ( 23 | [parameter(Mandatory=$false)] 24 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 25 | [parameter(Mandatory=$true)] 26 | [string]$Email 27 | ) 28 | 29 | Get-CyUserList -API $API | Where-Object email -eq $Email 30 | } 31 | 32 | <# 33 | .SYNOPSIS 34 | Adds exclusions for a certain application (from an application definition JSON file) to a policy. 35 | 36 | .PARAMETER Definitions 37 | The JSON file with definitions. 38 | 39 | .PARAMETER Application 40 | The application identifier from the JSON file 41 | 42 | .PARAMETER Policy 43 | The policy object to change. 44 | #> 45 | function Add-CyPolicyExclusionsForApplication { 46 | Param ( 47 | [parameter(Mandatory=$false)] 48 | [ValidateNotNullOrEmpty()] 49 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 50 | [Parameter(Mandatory=$true, 51 | ValueFromPipeline=$true, 52 | ValueFromPipelineByPropertyName=$true)] 53 | [pscustomobject]$Policy 54 | ) 55 | DynamicParam { 56 | $Options = (Get-Content "$PSScriptRoot\CyDATA_ApplicationDefinitions.json" | ConvertFrom-Json).Name 57 | Get-CyDynamicParam -Mandatory -AllowMultiple -ParameterName Application -Options $Options 58 | } 59 | 60 | Begin { 61 | $Definitions = (Get-Content "$PSScriptRoot\CyDATA_ApplicationDefinitions.json" | ConvertFrom-Json) 62 | $ApplicationDefinition = $Definitions | Where-Object name -eq $PSBoundParameters.Application 63 | } 64 | 65 | Process { 66 | $ApplicationDefinition.memory_exclusion_list | ForEach-Object { 67 | Add-CyPolicyListSetting -Type MemDefExclusionPath -Value $_ -Policy $Policy 68 | } 69 | 70 | $ApplicationDefinition.scan_exception_list | ForEach-Object { 71 | Add-CyPolicyListSetting -Type ScanExclusion -Value $_ -Policy $Policy 72 | } 73 | 74 | $ApplicationDefinition.script_allowed_folders | ForEach-Object { 75 | Add-CyPolicyListSetting -Type ScriptControlExclusionPath -Value $_ -Policy $Policy 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /CyCrypto.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Calculates a SHA256 HMAC for the given message and secret. Returns a string with the Base64 URL encoded result. 3 | #> 4 | function Get-HMACSHA256 { 5 | Param( 6 | [parameter(Mandatory=$true)] 7 | [String]$message, 8 | [parameter(Mandatory=$true)] 9 | [String]$secret 10 | ) 11 | $hmacsha = New-Object System.Security.Cryptography.HMACSHA256 12 | $hmacsha.key = [Text.Encoding]::UTF8.GetBytes($secret) 13 | $messageBytes = [Text.Encoding]::UTF8.GetBytes($message) 14 | $signatureBytes = $hmacsha.ComputeHash($messageBytes) 15 | ConvertTo-Base64UrlEncoding $signatureBytes 16 | } 17 | 18 | <# 19 | Creates a JWT Token to authenticate to the Cylance console API. 20 | 21 | Can accept additional claims to include in request. 22 | #> 23 | function Get-CyJWTToken { 24 | Param ( 25 | [parameter(Mandatory=$True)] 26 | [Hashtable]$claims = @{}, 27 | [parameter(Mandatory=$true)] 28 | [String]$secret, 29 | [parameter(Mandatory=$true)] 30 | [String]$iss, 31 | [parameter(Mandatory=$true)] 32 | [String]$APIid, 33 | [parameter(Mandatory=$true)] 34 | [String]$tid, 35 | [parameter(Mandatory=$true)] 36 | [int]$expirationSeconds 37 | ) 38 | 39 | # calculate token mandatory fields 40 | $now = [int32](((Get-Date -Date ((Get-Date).ToUniversalTime()) -UFormat %s -Millisecond 0)) -Replace("[,\.]\d*", "")) 41 | $exp = $now + $expirationSeconds 42 | $guid = [guid]::newguid() 43 | $sub = $APIId 44 | $jti = $guid 45 | 46 | [pscustomobject]$registeredClaims = @{ 47 | sub = $sub 48 | iss = $iss 49 | jti = $jti 50 | exp = $exp 51 | tid = $tid 52 | iat = $now 53 | } 54 | $payload = $registeredClaims 55 | 56 | # merge additional private claims into payload 57 | $claims.Keys | ForEach-Object { 58 | Add-Member -InputObject $payload -Name $_ -Value $claims[$_] 59 | # $payload.Add($_ , $claims[$_]) 60 | } 61 | 62 | # header specifies algorithm + token typen 63 | [pscustomobject]$h = @{ 64 | alg = "HS256" 65 | typ = "JWT" 66 | } 67 | 68 | # get compact JSON representation of header + payload 69 | $h_json = $h | ConvertTo-Json -Compress 70 | $p_json = $payload | ConvertTo-Json -Compress 71 | 72 | $h = ConvertTo-Base64UrlEncoding $h_json 73 | $p = ConvertTo-Base64UrlEncoding $p_json 74 | $s = Get-HMACSHA256 -Message "${h}.${p}" -secret $secret 75 | 76 | "${h}.${p}.${s}" 77 | } 78 | 79 | function Get-CyClaimsFromJwtToken { 80 | Param ( 81 | [parameter(Mandatory=$true)] 82 | [String]$token 83 | ) 84 | 85 | $elems = $token.Split(".") 86 | $null = ConvertFrom-Base64UrlEncoding($elems[0]) | ConvertFrom-Json 87 | $o = ConvertFrom-Base64UrlEncoding($elems[1]) | ConvertFrom-Json 88 | $null = $elems[2] 89 | 90 | $o 91 | } -------------------------------------------------------------------------------- /CyDATA_ApplicationDefinitions.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Windows", 4 | "os": "Windows", 5 | "last_reviewed": "20181008", 6 | "memory_exclusion_list": [ 7 | "\\Windows\\System32\\werfault.exe", 8 | "\\Windows\\SysWow64\\werfault.exe" 9 | ] 10 | }, 11 | { 12 | "name": "SCCM", 13 | "memory_exclusion_list": [], 14 | "last_reviewed": "20181008", 15 | "scan_exception_list": [ 16 | "c:\\windows\\ccmcache\\", 17 | "c:\\windows\\ccm\\systemtemp\\" 18 | ], 19 | "script_allowed_folders": [ 20 | "/windows/ccm/systemtemp/*.vbs", 21 | "/windows/ccm/systemtemp/*.ps1" 22 | ] 23 | }, 24 | { 25 | "name": "McAfee_Linux", 26 | "os": "Linux", 27 | "last_reviewed": "20181008", 28 | "memory_exclusion_list": [], 29 | "scan_exception_list": [ 30 | "/opt/isec/ens/threatprevention/bin/", 31 | "/opt/isec/ens/esp/bin/", 32 | "/opt/McAfee/cma/" 33 | ], 34 | "exclusions_in_app": { 35 | "exception_list": [ 36 | "/opt/cylance", 37 | "/usr/lib/systemd/system/cylancesvc.service", 38 | "/etc/sysconfig/modules/cylance.modules", 39 | "/usr/src/CyProtectDrv-1.2", 40 | "/tmp/CylanceDesktopArchive", 41 | "/tmp/CylanceDesktopRemoteFile" 42 | ] 43 | } 44 | }, 45 | { 46 | "name": "Kaspersky_Windows", 47 | "os": "Windows", 48 | "last_reviewed": "20181107", 49 | "kb_link": "https://support.cylance.com/s/article/CylancePROTECT-Exclusions-for-Kaspersky-Anti-Virus", 50 | "memory_exclusion_list": [ 51 | ], 52 | "scan_exception_list": [ 53 | ], 54 | "exclusions_in_app": { 55 | "exception_list": [ 56 | "C:\\Program Files\\Cylance\\Desktop\\CylanceSvc.exe", 57 | "C:\\Program Files\\Cylance\\Desktop\\CylanceUI.exe", 58 | "%WINDIR%\\Temp\\CylanceDesktopRemoteFile\\", 59 | "%WINDIR%\\Temp\\CylanceDesktopArchive\\", 60 | "%WINDIR%\\System32\\Drivers\\CyProtectDrv*.sys", 61 | "%WINDIR%\\System32\\Drivers\\CyDevFlt*.sys", 62 | "%ProgramFiles%\\Cylance\\", 63 | "%ProgramData%\\Cylance\\Desktop\\q\\", 64 | "Outbound Port 443" 65 | ] 66 | } 67 | }, 68 | { 69 | "name": "TrendMicro_Windows", 70 | "os": "Windows", 71 | "last_reviewed": "20181107", 72 | "kb_link": "https://support.cylance.com/s/article/CylancePROTECT-Exclusions-for-Trend-Micro", 73 | "memory_exclusion_list": [ 74 | "\\Program Files (x86)\\Trend Micro\\BM\\TMBMSRV.exe", 75 | "\\Program Files (x86)\\Trend Micro\\OfficeScan Client\\CCSF\\TmCCSF.exe", 76 | "\\Program Files (x86)\\Trend Micro\\OfficeScan Client\\Ntrtscan.exe", 77 | "\\Program Files (x86)\\Trend Micro\\OfficeScan Client\\PccNTMon.exe", 78 | "\\Program Files (x86)\\Trend Micro\\OfficeScan Client\\TmListen.exe", 79 | "\\Program Files (x86)\\Trend Micro\\OfficeScan Client\\TmPfw.exe" 80 | ], 81 | "scan_exception_list": [ 82 | "C:\\Program Files (x86)\\Trend Micro\\", 83 | "C:\\ProgramData\\Trend Micro\\" 84 | ], 85 | "exclusions_in_app": { 86 | "exception_list": [ 87 | "C:\\Program Files\\Cylance\\", 88 | "C:\\Windows\\Temp\\CylanceDesktopArchive", 89 | "C:\\Windows\\Temp\\CylanceDesktopRemoteFile", 90 | "C:\\ProgramData\\Cylance\\Desktop\\q", 91 | "C:\\Documents and Settings\\All Users\\Application Data\\Cylance\\Desktop\\q", 92 | "C:\\Windows\\System32\\Drivers\\CyProtectDrv64.sys", 93 | "C:\\Windows\\System32\\Drivers\\CyProtectDrv32.sys", 94 | "C:\\Windows\\System32\\Drivers\\CyDevFlt64.sys", 95 | "C:\\Windows\\System32\\Drivers\\CyDevFlt32.sys", 96 | "C:\\Windows\\CyProtect.cache", 97 | "C:\\Program Files\\Cylance\\Desktop\\CylanceSvc.exe", 98 | "C:\\Program Files\\Cylance\\Desktop\\CylanceUI.exe", 99 | "C:\\Program Files\\Cylance\\Desktop\\CyUpdate.exe", 100 | "C:\\Program Files\\Cylance\\Desktop\\LocalePkg.exe", 101 | "C:\\Program Files\\Cylance\\Desktop\\CylanceSvc.exe", 102 | "C:\\Program Files\\Cylance\\Desktop\\CylanceUI.exe", 103 | "C:\\Program Files\\Cylance\\Desktop\\CyUpdate.exe", 104 | "C:\\Program Files\\Cylance\\Desktop\\LocalePkg.exe" 105 | ] 106 | } 107 | }, 108 | { 109 | "name": "McAfee_Windows", 110 | "os": "Windows", 111 | "kb_link": "https://support.cylance.com/s/article/CylancePROTECT-Exclusions-for-McAfee-EndPoint-Security", 112 | "goes_beyond_KB": true, 113 | "last_reviewed": "20181008", 114 | "memory_exclusion_list": [ 115 | "\\mcdatrep.exe", 116 | "\\Program Files\\Common Files\\McAfee\\SystemCore\\mfemms.exe", 117 | "\\Program Files\\McAfee\\Agent\\masvc.exe", 118 | "\\Program Files\\McAfee\\Agent\\x86\\macmnsvc.exe", 119 | "\\Program Files\\McAfee\\Agent\\x86\\macompatsvc.exe", 120 | "\\Program Files\\McAfee\\Agent\\x86\\mctray.exe", 121 | "\\Program Files\\McAfee\\Agent\\x86\\mfemactl.exe", 122 | "\\Program Files\\McAfee\\Endpoint Security\\Endpoint Security Platform\\mfeesp.exe", 123 | "\\Program files\\mcafee\\endpoint security\\threat prevention\\mfetp.exe", 124 | "\\Windows\\System32\\mfevtps.exe", 125 | "\\Program Files\\McAfee\\Endpoint Security\\Firewall\\mfefw.exe", 126 | "\\Program Files\\McAfee\\Endpoint Security\\Adaptive threat Protection\\mfeatp.exe", 127 | "\\Program Files\\Common Files\\McAfee\\SystemCore\\MFEFIRE.exe", 128 | "\\Program Files\\Common Files\\McAfee\\SystemCore\\mfecanary.exe", 129 | "\\Program Files (x86)\\McAfee\\Endpoint Security\\Endpoint Security Platform\\mfeconsole.exe", 130 | "\\Program Files (x86)\\McAfee\\Common Framework\\updaterui.exe", 131 | "\\Program Files\\McAfee\\Endpoint Security\\Adaptive threat Protection\\mfeatp.exe", 132 | "\\Program Files\\Common Files\\McAfee\\AVSolution\\mcshield.exe", 133 | "\\Program Files\\McAfee\\Host Intrusion Prevention\\FireSvc.exe" 134 | ], 135 | "scan_exception_list": [ 136 | "C:\\Quarantine" 137 | ] 138 | }, 139 | { 140 | "name": "Defender", 141 | "os": "Windows", 142 | "last_reviewed": "20181008", 143 | "memory_exclusion_list": [ 144 | "\\program files\\windows defender advanced threat protection\\mssense.exe", 145 | "\\program files\\windows defender\\nissrv.exe", 146 | "\\program files\\windows defender\\msmpeng.exe" 147 | ], 148 | "scan_exception_list": [ 149 | "C:\\Program Files\\Windows Defender Advanced Threat Protection\\", 150 | "C:\\ProgramData\\Microsoft\\Windows Defender\\", 151 | "C:\\Program Files\\Windows Defender\\", 152 | "C:\\ProgramData\\Microsoft\\Windows Defender Advanced Threat Protection\\" 153 | ] 154 | }, 155 | { 156 | "name": "SophosAV_Windows", 157 | "os": "Windows", 158 | "kb_link": "https://support.cylance.com/s/article/CylancePROTECT-Exclusions-for-Sophos", 159 | "goes_beyond_KB": true, 160 | "last_reviewed": "20181008", 161 | "memory_exclusion_list": [ 162 | "\\Sophos\\AutoUpdate\\ALMon.exe", 163 | "\\Sophos\\AutoUpdate\\ALsvc.exe", 164 | "\\Sophos\\AutoUpdate\\ALUpdate.exe", 165 | "\\Sophos\\AutoUpdate\\SophosUpdate.exe", 166 | "\\Sophos\\AutoUpdate\\Telemetry\\AUTelem.exe", 167 | "\\Sophos\\AutoUpdate\\Telemetry\\GatherTelem.exe", 168 | "\\Sophos\\AutoUpdate\\Telemetry\\SubmitTelem.exe", 169 | "\\Sophos\\Remote Management System\\ClientMRInit.exe", 170 | "\\Sophos\\Remote Management System\\ManagementAgentNT.exe", 171 | "\\Sophos\\Remote Management System\\RouterNT.exe", 172 | "\\Sophos\\Sophos Anti-Virus\\BackgroundScanClient.exe", 173 | "\\Sophos\\Sophos Anti-Virus\\ForceUpdateAlongSideSGN.exe", 174 | "\\Sophos\\Sophos Anti-Virus\\Native.exe", 175 | "\\Sophos\\Sophos Anti-Virus\\sav32cli.exe", 176 | "\\Sophos\\Sophos Anti-Virus\\SAVAdminService.exe", 177 | "\\Sophos\\Sophos Network Threat Protection\\bin\\SntpService.exe", 178 | "\\Sophos\\Sophos Anti-Virus\\SAVCleanupService.exe", 179 | "\\Sophos\\Sophos Anti-Virus\\SavMain.exe", 180 | "\\Sophos\\Sophos Anti-Virus\\SavProgress.exe", 181 | "\\Sophos\\Sophos Anti-Virus\\SavProxy.exe", 182 | "\\Sophos\\Sophos Anti-Virus\\SavService.exe", 183 | "\\Sophos\\Sophos Anti-Virus\\SAVTelem.exe", 184 | "\\Sophos\\Sophos Anti-Virus\\sdcdevcon.exe", 185 | "\\Sophos\\Sophos Anti-Virus\\sdcservice.exe", 186 | "\\Sophos\\Sophos Anti-Virus\\ssr32.exe", 187 | "\\Sophos\\Sophos Anti-Virus\\ssr64.exe", 188 | "\\Sophos\\Sophos Anti-Virus\\WSCClient.exe", 189 | "\\Sophos\\Sophos Anti-Virus\\Web Control\\swc_service.exe", 190 | "\\Sophos\\Sophos Anti-Virus\\Web Intelligence\\swi_di.exe", 191 | "\\Sophos\\Sophos Anti-Virus\\Web Intelligence\\swi_fc.exe", 192 | "\\Common Files\\Sophos\\Web Intelligence\\swi_fc.exe", 193 | "\\Sophos\\Sophos Anti-Virus\\Web Intelligence\\swi_filter.exe", 194 | "\\Sophos\\Sophos Anti-Virus\\Web Intelligence\\swi_lsp32_util.exe", 195 | "\\Sophos\\Sophos Anti-Virus\\Web Intelligence\\swi_lspdiag.exe", 196 | "\\Sophos\\Sophos Anti-Virus\\Web Intelligence\\swi_lspdiag_64.exe", 197 | "\\Sophos\\Sophos Anti-Virus\\Web Intelligence\\swi_service.exe", 198 | "\\Sophos\\Sophos Anti-Virus\\Web Intelligence\\swi_update_64.exe", 199 | "\\Sophos\\Sophos System Protection\\ssp.exe", 200 | "\\Sophos\\Sophos Patch Agent\\spa.exe", 201 | "\\Sophos\\Health\\health.exe", 202 | "\\Sophos\\Health\\heartbeat.exe", 203 | "\\Sophos\\Sophos Data Recorder\\sdrservice.exe", 204 | "\\Sophos\\Clean\\sophosclean.exe" 205 | ], 206 | "scan_exception_list": [ 207 | "C:\\ProgramData\\Sophos", 208 | "C:\\Program Files\\Sophos", 209 | "C:\\Program Files (x86)\\Sophos" 210 | ] 211 | }, 212 | { 213 | "name": "Symantec_Windows", 214 | "os": "Windows", 215 | "last_reviewed": "20181008", 216 | "scan_exception_list": [ 217 | "C:\\Program Files\\Symantec\\", 218 | "C:\\Program Files (x86)\\Symantec\\", 219 | "C:\\ProgramData\\Symantec\\" 220 | ], 221 | "memory_exclusion_list": [ 222 | "\\ccSvcHost.exe" 223 | ] 224 | }, 225 | { 226 | "name": "Tanium_Windows", 227 | "os": "Windows", 228 | "kb_link": "https://support.cylance.com/s/article/CylancePROTECT-Exclusions-for-Tanium", 229 | "goes_beyond_KB": false, 230 | "last_reviewed": "20181008", 231 | "scan_exception_list": [ 232 | "C:\\Program Files (x86)\\Tanium", 233 | "C:\\Program Files\\Tanium" 234 | ], 235 | "memory_exclusion_list": [ 236 | "\\Program Files (x86)\\Tanium\\Tanium Client\\TaniumClient.exe", 237 | "\\Program Files\\Tanium\\Tanium Client\\TaniumClient.exe" 238 | ], 239 | "script_allowed_folders": [ 240 | "\\Program Files (x86)\\Tanium", 241 | "\\Program Files\\Tanium" 242 | ] 243 | }, 244 | { 245 | "name": "Tanium_macOS", 246 | "os": "macOS", 247 | "kb_link": "https://support.cylance.com/s/article/CylancePROTECT-Exclusions-for-Tanium", 248 | "goes_beyond_KB": false, 249 | "last_reviewed": "20181008", 250 | "scan_exception_list": [ 251 | "C:\\Program Files (x86)\\Tanium", 252 | "C:\\Program Files\\Tanium" 253 | ], 254 | "memory_exclusion_list": [ 255 | "/Library/Tanium/TaniumClient" 256 | ] 257 | }, 258 | { 259 | "name": "Tanium_Linux", 260 | "os": "Linux", 261 | "kb_link": "https://support.cylance.com/s/article/CylancePROTECT-Exclusions-for-Tanium", 262 | "goes_beyond_KB": false, 263 | "last_reviewed": "20181008", 264 | "scan_exception_list": [ 265 | "/opt/Tanium/TaniumClient/taniumclient" 266 | ], 267 | "memory_exclusion_list": [ 268 | "/opt/Tanium/TaniumClient" 269 | ] 270 | } 271 | ] -------------------------------------------------------------------------------- /CyDevices.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Gets a list of all devices in console. 4 | 5 | .PARAMETER API 6 | Optional. API Handle (use only when not using session scope). 7 | 8 | .PARAMETER ZoneName 9 | Optional. The name of a zone to retrieve member devices for. 10 | 11 | .PARAMETER Zone 12 | Optional. A zone object to retrieve member devices for. 13 | #> 14 | function Get-CyDeviceList { 15 | [CmdletBinding(DefaultParameterSetName="All")] 16 | Param ( 17 | [parameter(Mandatory=$false)] 18 | [ValidateNotNullOrEmpty()] 19 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 20 | [parameter(Mandatory=$true,ParameterSetName="ByZoneName")] 21 | [String]$ZoneName, 22 | [parameter(Mandatory=$true,ParameterSetName="ByZone")] 23 | [object]$Zone 24 | ) 25 | 26 | switch ($PSCmdlet.ParameterSetName) { 27 | "ByZoneName" { 28 | $Zone = Get-CyZone -API $API -Name $ZoneName 29 | Get-CyDeviceList -API $API -Zone $Zone 30 | } 31 | "ByZone" { 32 | Read-CyData -API $API -Uri "$($API.BaseUrl)/devices/v2/$($Zone.id)/devices" -PageSize 10000 -Fields date_offline,date_first_registered 33 | } 34 | "All" { 35 | Read-CyData -API $API -Uri "$($API.BaseUrl)/devices/v2" -PageSize 10000 -Fields date_offline,date_first_registered 36 | } 37 | } 38 | } 39 | 40 | 41 | <# 42 | .SYNOPSIS 43 | Deletes a device. 44 | 45 | .PARAMETER API 46 | Optional. API Handle (use only when not using session scope). 47 | 48 | .PARAMETER Device 49 | The device object to apply this threat action to. 50 | #> 51 | function Remove-CyDevice { 52 | Param ( 53 | [parameter(Mandatory=$false)] 54 | [ValidateNotNullOrEmpty()] 55 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 56 | [Parameter( 57 | ParameterSetName="ByDevice", 58 | Mandatory=$true, 59 | ValueFromPipeline=$true, 60 | ValueFromPipelineByPropertyName=$true)] 61 | [object[]]$Device, 62 | [Parameter(Mandatory=$true,ParameterSetName="ByDeviceId")] 63 | [object]$DeviceId, 64 | [Parameter(Mandatory=$false)] 65 | [object]$CallbackUrl 66 | 67 | 68 | ) 69 | 70 | Begin { 71 | $devices = @() 72 | } 73 | 74 | Process { 75 | # remain silent 76 | switch ($PSCmdlet.ParameterSetName) { 77 | "ByDeviceId" { 78 | $devices += $DeviceId 79 | } 80 | "ByDevice" { 81 | $devices += $Device.id 82 | } 83 | } 84 | } 85 | 86 | End { 87 | $updateMap = @{ 88 | "device_ids" = $devices 89 | } 90 | 91 | if (![String]::IsNullOrEmpty($CallbackUrl)) { 92 | $updateMap += @{ "url" = $CallbackUrl } 93 | } 94 | 95 | $json = $updateMap | ConvertTo-Json 96 | Write-verbose "Update Map: $($json)" 97 | # remain silent 98 | $null = Invoke-CyRestMethod -API $API -Method DELETE -Uri "$($API.BaseUrl)/devices/v2" -ContentType "application/json; charset=utf-8" -Body $json 99 | } 100 | } 101 | 102 | <# 103 | .SYNOPSIS 104 | Retrieves the given DEVICE from the console. Gets full data, not a shallow version. 105 | 106 | .PARAMETER API 107 | Optional. API Handle (use only when not using session scope). 108 | 109 | .PARAMETER Device 110 | The device to retrieve the Detail for. 111 | #> 112 | function Get-CyDeviceDetail { 113 | Param ( 114 | [parameter(Mandatory=$false)] 115 | [ValidateNotNullOrEmpty()] 116 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 117 | [Parameter( 118 | Mandatory=$true, 119 | ValueFromPipeline=$true, 120 | ValueFromPipelineByPropertyName=$true)] 121 | [object[]]$Device 122 | ) 123 | 124 | Process { 125 | Invoke-CyRestMethod -API $API -Method GET -Uri "$($API.BaseUrl)/devices/v2/$($Device.id)" | Convert-CyObject 126 | } 127 | } 128 | 129 | <# 130 | .SYNOPSIS 131 | Retrieves the given DEVICE from the console. Gets full data, not a shallow version. 132 | 133 | .PARAMETER API 134 | Optional. API Handle (use only when not using session scope). 135 | 136 | .PARAMETER Device 137 | The device to retrieve the Detail for. 138 | #> 139 | function Get-CyDeviceDetailByMac { 140 | Param ( 141 | [parameter(Mandatory=$false)] 142 | [ValidateNotNullOrEmpty()] 143 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 144 | [Parameter( 145 | Mandatory=$true, 146 | ValueFromPipeline=$true, 147 | ValueFromPipelineByPropertyName=$true)] 148 | [String]$MAC 149 | ) 150 | 151 | Process { 152 | Invoke-CyRestMethod -API $API -Method GET -Uri "$($API.BaseUrl)/devices/v2/macaddress/$($MAC)" | 153 | ForEach-Object { 154 | $_ | Convert-CyObject 155 | } 156 | } 157 | } -------------------------------------------------------------------------------- /CyGlobalLists.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Gets the global list 4 | 5 | .PARAMETER API 6 | Optional. API Handle (use only when not using session scope). 7 | 8 | .PARAMETER List 9 | The type of global list to retrieve. 10 | #> 11 | function Get-CyGlobalList { 12 | Param ( 13 | [parameter(Mandatory=$false)] 14 | [ValidateNotNullOrEmpty()] 15 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 16 | [Parameter(Mandatory=$true)] 17 | [ValidateSet ("GlobalSafeList", "GlobalQuarantineList")] 18 | [String]$List 19 | ) 20 | 21 | $APIListType = 0 22 | 23 | switch ($List) { 24 | "GlobalQuarantine" { 25 | $APIListType = 0 26 | } 27 | "GlobalSafeList" { 28 | $APIListType = 1 29 | } 30 | } 31 | 32 | Read-CyData -API $API -Uri "$($API.BaseUrl)/globallists/v2" -QueryParams @{ "listTypeId" = $APIListType } 33 | } 34 | 35 | <# 36 | .SYNOPSIS 37 | Adds file hash to global list 38 | 39 | .PARAMETER API 40 | Optional. API Handle (use only when not using session scope). 41 | 42 | .PARAMETER List 43 | The global list type to add to 44 | 45 | .PARAMETER SHA256 46 | The file hash to add 47 | 48 | .PARAMETER Category 49 | The category of the file to add to the list 50 | 51 | .PARAMETER Reason 52 | The reason for adding the file. 53 | #> 54 | function Add-CyHashToGlobalList { 55 | Param ( 56 | [parameter(Mandatory=$false)] 57 | [ValidateNotNullOrEmpty()] 58 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 59 | [Parameter(Mandatory=$true)] 60 | [ValidateSet ("GlobalSafeList", "GlobalQuarantineList")] 61 | [String]$List, 62 | [Parameter( 63 | Mandatory=$true, 64 | ValueFromPipeline=$true, 65 | ValueFromPipelineByPropertyName=$true)] 66 | [String]$SHA256, 67 | [Parameter(Mandatory=$false)] 68 | [ValidateSet ("Admin Tool", "Commercial Software", "Drivers", "Internal Application", "Operating System", "Security Software", "None")] 69 | [String]$Category, 70 | [Parameter(Mandatory=$true)] 71 | [String]$Reason 72 | ) 73 | 74 | Begin { 75 | $APIListType = "GlobalQuarantine"; 76 | if ($List -eq "GlobalSafeList") { $APIListType = "GlobalSafe" } 77 | } 78 | 79 | Process { 80 | switch ($APIListType) { 81 | "GlobalQuarantine" { 82 | $updateMap = @{ 83 | "sha256" = $SHA256 84 | "list_type" = $APIListType 85 | "reason" = $Reason 86 | } 87 | } 88 | "GlobalSafe" { 89 | $updateMap = @{ 90 | "sha256" = $SHA256 91 | "list_type" = $APIListType 92 | "reason" = $Reason 93 | "category" = $Category 94 | } 95 | } 96 | } 97 | 98 | $json = $updateMap | ConvertTo-Json 99 | # remain silent 100 | $null = Invoke-CyRestMethod -API $API -Method POST -Uri "$($API.BaseUrl)/globallists/v2" -ContentType "application/json; charset=utf-8" -Body $json 101 | } 102 | } 103 | 104 | <# 105 | .SYNOPSIS 106 | Removes a hash from a global list 107 | 108 | .PARAMETER API 109 | Optional. API Handle (use only when not using session scope). 110 | 111 | .PARAMETER List 112 | The list type 113 | 114 | .PARAMETER SHA256 115 | The file hash to remove. 116 | #> 117 | function Remove-CyHashFromGlobalList { 118 | Param ( 119 | [parameter(Mandatory=$false)] 120 | [ValidateNotNullOrEmpty()] 121 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 122 | [Parameter(Mandatory=$true)] 123 | [ValidateSet ("GlobalSafeList", "GlobalQuarantineList")] 124 | [String]$List, 125 | [Parameter( 126 | Mandatory=$true, 127 | ValueFromPipeline=$true, 128 | ValueFromPipelineByPropertyName=$true)] 129 | [String]$SHA256 130 | ) 131 | 132 | Begin { 133 | $APIListType = "GlobalQuarantine"; 134 | if ($List -eq "GlobalSafeList") { $APIListType = "GlobalSafe" } 135 | } 136 | 137 | Process { 138 | $updateMap = @{ 139 | "sha256" = $SHA256 140 | "list_type" = $APIListType 141 | } 142 | 143 | $json = $updateMap | ConvertTo-Json 144 | $null = Invoke-CyRestMethod -API $API -Method DELETE -Uri "$($API.BaseUrl)/globallists/v2" -ContentType "application/json; charset=utf-8" -Body $json 145 | } 146 | } -------------------------------------------------------------------------------- /CyHelper.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Helper function. 3 | 4 | Converts a given string or byte array to Base64 "urlencoding" format (= replace certain special characters from the Base64 alphabet with URL safe chars) 5 | #> 6 | function ConvertTo-Base64UrlEncoding { 7 | Param( 8 | [parameter(Mandatory=$true)] 9 | [Object]$s 10 | ) 11 | if ($s -is [String]) { 12 | $s = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($s)) # convert to base64 13 | } elseif ($s -is [byte[]]) { 14 | $s = [Convert]::ToBase64String($s) 15 | } 16 | $s.Split("=")[0].Replace("+", "-").Replace("/", "_") # alphabet replacement from regular base64 to base64urlencoded 17 | } 18 | 19 | <# 20 | Helper function. 21 | 22 | Converts a given string from Base64 "urlencoding" format (= replace certain special characters from the Base64 alphabet with URL safe chars) to a string 23 | #> 24 | function ConvertFrom-Base64UrlEncoding { 25 | Param( 26 | [parameter(Mandatory=$true)] 27 | [String]$s 28 | ) 29 | $s = $s.Replace("-", "+").Replace("_", "/") # alphabet replacement from base64urlencoded to regular 30 | while ($s.Length % 4 -ne 0) { $s += "=" } # padding 31 | [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($s)) 32 | } 33 | 34 | 35 | <# 36 | .SYNOPSIS 37 | Converts the named "date" fields from CSV data (in "M/d/yyyy h:mm:ss tt" format) into datetime objects so that they can be exported to Excel. 38 | 39 | .DESCRIPTION 40 | All date/time data is treated as UTC. 41 | 42 | Format defaults to "M/d/yyyy h:mm:ss tt" 43 | 44 | #> 45 | function Convert-FromCSVDate { 46 | Param ( 47 | [parameter(Mandatory=$True)] 48 | [PSObject]$Data, 49 | [Parameter(Mandatory=$True)] 50 | [Array]$Fields, 51 | [Parameter(Mandatory=$False)] 52 | [Array]$Format = "M/d/yyyy h:mm:ss tt" 53 | ) 54 | $Data | ForEach-Object { 55 | # each row 56 | ForEach ($field in $Fields) { 57 | if (![string]::IsNullOrWhiteSpace($_.$field)) { 58 | try { 59 | $t = $_.$field 60 | $_.$field = [datetime]::ParseExact($_.$field, $Format, [cultureinfo]::InvariantCulture) 61 | } catch { 62 | Write-Error "Could not convert date: $($t), exception: $($_.Exception.ItemName)" 63 | } 64 | } 65 | } 66 | $_ 67 | } 68 | } 69 | 70 | <# 71 | .SYNOPSIS 72 | Returns a hash map with the consoles configuration list for API and TDR modules 73 | 74 | .DESCRIPTION 75 | Reads "consoles.json" from $HOME\ as a short-hand way to reference the credentials and settings 76 | to access one or more Cylance consoles. 77 | #> 78 | function Get-CyConsoleConfig { 79 | @("$($HOME)\TDRs\", "$($HOME)" ) | 80 | ForEach-Object { 81 | if (Test-Path -Path "$($_)\consoles.json" -PathType Leaf) { 82 | # PowerShell parser chokes on JS comments... pffft 83 | $script:ConsolesJsonPath = "$($_)\consoles.json" 84 | Write-Verbose "consoles.json path used: $($script:ConsolesJsonPath)" 85 | Get-Content "$($_)\consoles.json" | Select-String -NotMatch -Pattern "^[\s]*//.*$" | ConvertFrom-Json 86 | return 87 | } 88 | } 89 | @() 90 | } 91 | 92 | <# 93 | .SYNOPSIS 94 | Adds a console entry to the consoles.json file. 95 | 96 | .DESCRIPTION 97 | This powershell module stores credentials and settings in a JSON file (consoles.json) for convenient access of 98 | parameters such as URLs to use, API secrets, TDR token etc. The use of the JSON file is not mandatory 99 | but makes using the API via the powershell module much easier, since consoles can be referred to by name 100 | rather than a combination of credentials (e.g. API application ID, API secret, API Auth URL, and API tenant ID 101 | would otherwise be needed everytime the API is used in program code or from a Powershell console). 102 | 103 | You can call New-CyConsoleConfig without any parameters and it will prompt for all details. 104 | 105 | .PARAMETER Console 106 | Mandatory. The console ID you will use to refer to this console entry in your consoles.json file. See the README for CyCLI module. 107 | 108 | .PARAMETER Id 109 | Mandatory. API ID 110 | 111 | .PARAMETER Secret 112 | Mandatory. API Secret 113 | 114 | .PARAMETER TenantId 115 | Mandatory. API Tenant ID 116 | 117 | .PARAMETER Region 118 | Mandatory. The tenant region. Determines the URLs to use for API and TDR access. 119 | 120 | .PARAMETER Token 121 | Optional. If you would like to use TDR access as well, specify TDR token for console. 122 | 123 | #> 124 | function New-CyConsoleConfig { 125 | Param ( 126 | [parameter(Mandatory=$true)] 127 | [String]$Console, 128 | [parameter(Mandatory=$true)] 129 | [String]$APIId, 130 | [parameter(Mandatory=$true)] 131 | [SecureString]$APISecret, 132 | [parameter(Mandatory=$true)] 133 | [String]$APITenantId, 134 | [parameter(Mandatory=$false)] 135 | [String]$Token, 136 | [parameter(Mandatory=$true)] 137 | [ValidateSet ("apne1", "au", "euc1", "sae1", "us-gov", "us")] 138 | [String]$Region 139 | ) 140 | 141 | $Consoles = Get-CyConsoleConfig 142 | if ($null -eq $script:ConsolesJsonPath) { 143 | $script:ConsolesJsonPath = "$($HOME)\consoles.json" 144 | } 145 | try { 146 | # was: $APISecret = Read-Host -AsSecureString 147 | $DPAPISecret = ConvertFrom-SecureString -SecureString $APISecret 148 | 149 | $TDRUrl = "https://protect.cylance.com/Reports/ThreatDataReportV1/"; 150 | $APIAuthUrl = "https://protectapi.cylance.com/auth/v2/token"; 151 | 152 | switch -Regex ($Region) 153 | { 154 | "apne1|au|euc1|sae1" { 155 | $TDRUrl = "https://protect-$($Region).cylance.com/Reports/ThreatDataReportV1/"; 156 | $APIAuthUrl = "https://protectapi-$($Region).cylance.com/auth/v2/token"; 157 | } 158 | "us-gov" { 159 | $TDRUrl = "https://protect.us.cylance.com/Reports/ThreatDataReportV1/"; 160 | $APIAuthUrl = "https://protectapi.us.cylance.com/auth/v2/token"; 161 | } 162 | } 163 | 164 | $null = Get-CyAPI -APIId $APIId -APISecret $APISecret -APITenantId $APITenantId -APIAuthUrl $APIAuthUrl -Scope None 165 | # https://social.technet.microsoft.com/wiki/contents/articles/4546.working-with-passwords-secure-strings-and-credentials-in-windows-powershell.aspx 166 | 167 | $NewConsole = @{ 168 | ConsoleId = $Console 169 | AutoRetrieve = $true 170 | APIId = $APIId 171 | APISecret = $DPAPISecret 172 | APITenantId = $APITenantId 173 | APIUrl = $APIAuthUrl 174 | Token = $Token 175 | TDRUrl = $TDRUrl 176 | } 177 | if ($null -eq $Consoles) { 178 | $Consoles = @( $NewConsole ) 179 | } else { 180 | $Consoles += $NewConsole 181 | } 182 | if (Test-Path -Path $script:ConsolesJsonPath -Type Leaf) { 183 | Copy-Item $script:ConsolesJsonPath "$($script:ConsolesJsonPath).bak" -Force 184 | } 185 | ConvertTo-Json $Consoles | Out-File -FilePath $script:ConsolesJsonPath -Force 186 | } catch { 187 | $_.Exception 188 | return 189 | } 190 | } 191 | 192 | <# 193 | .SYNOPSIS 194 | Removes a console configuration entry from consoles.json 195 | 196 | #> 197 | function Remove-CyConsoleConfig { 198 | [CmdletBinding()] 199 | Param() 200 | DynamicParam { 201 | Get-CyConsoleArgumentAutoCompleter -Mandatory -ParameterName "Console" 202 | } 203 | 204 | Begin { 205 | } 206 | 207 | Process { 208 | $Consoles = (Get-CyConsoleConfig) | Where-Object ConsoleId -ne $PSBoundParameters.Console 209 | ConvertTo-Json $Consoles | Out-File -FilePath $script:ConsolesJsonPath -Force 210 | } 211 | } 212 | 213 | <# 214 | .SYNOPSIS 215 | Gets a DynamicParam definition that enables auto-completion to specify a single or multiple 216 | console IDs as an argument. 217 | 218 | .PARAMETER Mandatory 219 | Makes the dynamic parameter mandatory. 220 | 221 | .PARAMETER AllowMultiple 222 | Allows specification of multiple comma-separated consoles arguments 223 | 224 | .PARAMETER ParameterName 225 | The name of the dynamic parameter (typically "Console" or "Consoles") 226 | 227 | #> 228 | function Get-CyConsoleArgumentAutoCompleter { 229 | [CmdletBinding()] 230 | Param ( 231 | [parameter(Mandatory=$False)] 232 | [Switch]$AllowMultiple = $False, 233 | [parameter(Mandatory=$False)] 234 | [Switch]$Mandatory, 235 | [parameter(Mandatory=$True)] 236 | [String]$ParameterName, 237 | [parameter(Mandatory=$False)] 238 | [String]$ParameterSetName, 239 | [Parameter(Mandatory=$False)] 240 | [Int]$Position 241 | ) 242 | 243 | #$consoleIds = (Get-CyConsoleConfig).ConsoleId 244 | #Get-CyDynamicParam @args -Options $consoleIds 245 | 246 | $consoleIds = (Get-CyConsoleConfig).ConsoleId 247 | 248 | if ($consoleIds.Count -eq 0) { 249 | return $null 250 | } 251 | 252 | $consolesAttributes = new-object -Type System.Collections.ObjectModel.Collection[System.Attribute] 253 | $consolesParam = new-object System.Management.Automation.ParameterAttribute 254 | $consolesParam.Mandatory = $Mandatory 255 | if ($Position -ne $null) { 256 | $consolesParam.Position = $Position 257 | } 258 | $consolesParam.HelpMessage = "Enter one or more console IDs, separated by commas" 259 | if ([String]::Empty -ne $ParameterSetName) { $consolesParam.ParameterSetName = $ParameterSetName } 260 | $consolesAttributes.Add($consolesParam) 261 | 262 | $consoleIdsValidateSetAttribute = New-Object -type System.Management.Automation.ValidateSetAttribute($consoleIds) 263 | $consolesAttributes.Add($consoleIdsValidateSetAttribute) 264 | if ($AllowMultiple) { 265 | $consoleIdsRuntimeDefinedParam = new-object -Type System.Management.Automation.RuntimeDefinedParameter("$($ParameterName)", [String[]], $consolesAttributes) 266 | } else { 267 | $consoleIdsRuntimeDefinedParam = new-object -Type System.Management.Automation.RuntimeDefinedParameter("$($ParameterName)", [String], $consolesAttributes) 268 | } 269 | $paramDictionary = new-object -Type System.Management.Automation.RuntimeDefinedParameterDictionary 270 | $paramDictionary.Add("$($ParameterName)", $consoleIdsRuntimeDefinedParam) 271 | return $paramDictionary 272 | } 273 | 274 | function Get-CyDynamicParam { 275 | [CmdletBinding()] 276 | Param ( 277 | [parameter(Mandatory=$False)] 278 | [Switch]$AllowMultiple = $False, 279 | [parameter(Mandatory=$False)] 280 | [Switch]$Mandatory, 281 | [parameter(Mandatory=$True)] 282 | [String]$ParameterName, 283 | [parameter(Mandatory=$False)] 284 | [String]$ParameterSetName, 285 | [Parameter(Mandatory=$False)] 286 | [Int]$Position, 287 | [Parameter(Mandatory=$False)] 288 | [String]$HelpMessage, 289 | [Parameter(Mandatory=$True)] 290 | [String[]]$Options 291 | ) 292 | 293 | if ($Options.Count -eq 0) { 294 | return $null 295 | } 296 | 297 | $attributes = new-object -Type System.Collections.ObjectModel.Collection[System.Attribute] 298 | 299 | $parameterAttribute = new-object System.Management.Automation.ParameterAttribute 300 | $parameterAttribute.Mandatory = $Mandatory 301 | if ($null -ne $Position) { $parameterAttribute.Position = $Position } 302 | if (![String]::IsNullOrEmpty($HelpMessage)) { $parameterAttribute.HelpMessage = "Enter one or more console IDs, separated by commas" } 303 | if (![String]::IsNullOrEmpty($ParameterSetName)) { $parameterAttribute.ParameterSetName = $ParameterSetName } 304 | $attributes.Add($parameterAttribute) 305 | 306 | $validateSetAttribute = New-Object -type System.Management.Automation.ValidateSetAttribute($Options) 307 | $attributes.Add($validateSetAttribute) 308 | if ($AllowMultiple) { 309 | $OptionsRuntimeDefinedParam = new-object -Type System.Management.Automation.RuntimeDefinedParameter("$($ParameterName)", [String[]], $attributes) 310 | } else { 311 | $OptionsRuntimeDefinedParam = new-object -Type System.Management.Automation.RuntimeDefinedParameter("$($ParameterName)", [String], $attributes) 312 | } 313 | $paramDictionary = new-object -Type System.Management.Automation.RuntimeDefinedParameterDictionary 314 | $paramDictionary.Add("$($ParameterName)", $OptionsRuntimeDefinedParam) 315 | return $paramDictionary 316 | } 317 | -------------------------------------------------------------------------------- /CyInstallers.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Gets a link for the agent installer. 4 | 5 | .PARAMETER Product 6 | Protect or Optics 7 | 8 | .PARAMETER OS 9 | Operating System 10 | 11 | .PARAMETER Architecture 12 | Architecture. Contains some unexpected choices like CentOS6 base/UI etc. 13 | 14 | .PARAMETER Package 15 | Format. Does not apply to Linux. Required for all other OSes. 16 | #> 17 | function Get-CyAgentInstallerLink { 18 | Param ( 19 | [parameter(Mandatory=$false)] 20 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 21 | [parameter(Mandatory=$true)] 22 | [ValidateSet("Protect", "Optics")] 23 | [String]$Product, 24 | [parameter(Mandatory=$true)] 25 | [ValidateSet("CentOS7", "Linux", "Mac", "Ubuntu1404", "Ubuntu1604", "Windows")] 26 | [String]$OS, 27 | [parameter(Mandatory=$true)] 28 | [ValidateSet("X86", "X64", "CentOS6", "CentOS6UI", "CentOS7", "CentOS7UI", "Ubuntu1404", "Ubuntu1404UI", "Ubuntu1604", "Ubuntu1604UI")] 29 | [String]$Architecture, 30 | [parameter(Mandatory=$false)] 31 | [ValidateSet("Exe", "Msi", "Dmg", "Pkg")] 32 | [String]$Package 33 | ) 34 | 35 | if (($OS -notmatch "CentOS.*|Linux|Ubuntu.*") -and ([String]::IsNullOrEmpty($Package))) { 36 | Throw "For non-Linux OS, parameter 'Package' must be set." 37 | } 38 | 39 | Invoke-CyRestMethod -API $API -Method GET -Uri "$($API.BaseUrl)/devices/v2/installer?product=$($Product)&os=$($OS)&package=$($Package)&architecture=$($Architecture)" 40 | } -------------------------------------------------------------------------------- /CyOpticsDetections.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Gets a list of all detections from the console. 4 | 5 | Note that this does 6 | 7 | .PARAMETER API 8 | Optional. API Handle (use only when not using session scope). 9 | #> 10 | function Get-CyDetectionList { 11 | Param ( 12 | [parameter(Mandatory=$false)] 13 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 14 | [parameter(Mandatory=$false)] 15 | [DateTime]$Start, 16 | [parameter(Mandatory=$false)] 17 | [DateTime]$End, 18 | [parameter(Mandatory=$false)] 19 | [ValidateSet ("Informational", "Low", "Medium", "High")] 20 | [String]$Severity, 21 | [parameter(Mandatory=$false)] 22 | [String]$DetectionType, 23 | [parameter(Mandatory=$false)] 24 | [String]$DetectedOn, 25 | [parameter(Mandatory=$false)] 26 | [String]$EventNumber, 27 | [parameter(Mandatory=$false)] 28 | [String]$Device, 29 | [parameter(Mandatory=$false)] 30 | [ValidateSet ("New", "False Positive", "Follow Up", "In Progress", "Reviewed", "Done")] 31 | [string]$Status, 32 | [parameter(Mandatory=$false)] 33 | [ValidateSet ("Severity", "OccurrenceTime", "Status", "Device", "PhoneticId", "Description")] 34 | [string]$Sort 35 | ) 36 | 37 | $params = @{} 38 | 39 | if ($null -ne $Start) { 40 | $params.start = ConvertTo-CyDateString -Date $Start 41 | 42 | } 43 | if ($null -ne $End) { 44 | $params.end = ConvertTo-CyDateString -Date $End 45 | } 46 | if (![String]::IsNullOrEmpty($Severity)) { 47 | $params.severity = $Severity 48 | } 49 | 50 | if (![String]::IsNullOrEmpty($Status)) { 51 | $params.status = $Status 52 | } 53 | 54 | if (![String]::IsNullOrEmpty($Device)) { 55 | $params.device = $Device 56 | } 57 | 58 | if (![String]::IsNullOrEmpty($DetectionType)) { 59 | $params.detection_type = $DetectionType 60 | } 61 | 62 | if (![String]::IsNullOrEmpty($DetectedOn)) { 63 | $params.detected_on = $DetectedOn 64 | } 65 | 66 | if (![String]::IsNullOrEmpty($EventNumber)) { 67 | $params.event_number = $EventNumber 68 | } 69 | 70 | if (![String]::IsNullOrEmpty($Sort)) { 71 | $params.sort = $Sort 72 | } 73 | 74 | Read-CyData -API $API -Uri "$($API.BaseUrl)/detections/v2" -QueryParams $params -Fields "OccurrenceTime" 75 | } 76 | 77 | <# 78 | .SYNOPSIS 79 | Gets a list of all detections from the console. 80 | 81 | .PARAMETER API 82 | Optional. API Handle (use only when not using session scope). 83 | #> 84 | function Get-CyDetectionRecentList { 85 | Param ( 86 | [parameter(Mandatory=$false)] 87 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 88 | [parameter(Mandatory=$true)] 89 | [DateTime]$Since 90 | ) 91 | 92 | $params = @{ 93 | since = ConvertTo-CyDateString -Date $Since 94 | } 95 | 96 | Read-CyData -API $API -Uri "$($API.BaseUrl)/detections/v2/recent" -QueryParams $params -Fields OccurrenceTime 97 | } 98 | 99 | 100 | <# 101 | .SYNOPSIS 102 | Gets a list of all detections from the console. 103 | 104 | .PARAMETER API 105 | Optional. API Handle (use only when not using session scope). 106 | #> 107 | function Get-CyDetectionDetail { 108 | Param ( 109 | [parameter(Mandatory=$false)] 110 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 111 | [parameter(Mandatory=$true, 112 | ValueFromPipeline=$true, 113 | ValueFromPipelineByPropertyName=$true)] 114 | [object]$Detection 115 | ) 116 | 117 | Begin 118 | { 119 | 120 | } 121 | 122 | Process 123 | { 124 | Invoke-CyRestMethod -API $API -Method GET -Uri "$($API.BaseUrl)/detections/v2/$($Detection.id)/details" | Convert-CyObject -Fields "OccurrenceTime", "ActivationTime", "ReceivedTime" 125 | } 126 | } 127 | 128 | <# 129 | .SYNOPSIS 130 | Deletes a detection. 131 | 132 | .PARAMETER API 133 | Optional. API Handle (use only when not using session scope). 134 | 135 | .PARAMETER Detection 136 | The detection object to delete. 137 | #> 138 | function Remove-CyDetection { 139 | Param ( 140 | [parameter(Mandatory=$false)] 141 | [ValidateNotNullOrEmpty()] 142 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 143 | [Parameter( 144 | Mandatory=$true, 145 | ValueFromPipeline=$true, 146 | ValueFromPipelineByPropertyName=$true)] 147 | [object]$Detection 148 | ) 149 | 150 | Begin { 151 | } 152 | 153 | Process { 154 | if (($null -eq $Detection) -or ($null -eq $Detection.id) -or ([String]::IsNullOrEmpty($Detection.id))) { 155 | throw "Remove-CyDetection: Detection ID cannot be null or empty." 156 | } 157 | $null = Invoke-CyRestMethod -API $API -Method DELETE -Uri "$($API.BaseUrl)/detections/v2/$($Detection.id)" -ContentType "application/json; charset=utf-8" 158 | } 159 | } 160 | 161 | 162 | <# 163 | .SYNOPSIS 164 | Updates a detection's fields. 165 | 166 | .PARAMETER API 167 | Optional. API Handle (use only when not using session scope). 168 | #> 169 | function Update-CyDetection { 170 | Param ( 171 | [parameter(Mandatory=$false)] 172 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 173 | [parameter(Mandatory=$false)] 174 | [ValidateSet ("New", "False Positive", "Follow Up", "In Progress", "Reviewed", "Done")] 175 | [string]$Status, 176 | [parameter(Mandatory=$false)] 177 | [string]$Comment, 178 | [parameter(Mandatory=$true, 179 | ValueFromPipeline=$true, 180 | ValueFromPipelineByPropertyName=$true)] 181 | [object]$Detection 182 | ) 183 | 184 | Begin { 185 | $updateFields = @{} 186 | if (![String]::IsNullOrEmpty($Status)) { 187 | $updateFields.status = $Status 188 | } 189 | if (![String]::IsNullOrEmpty($Comment)) { 190 | $updateFields.comment = $Comment 191 | } 192 | } 193 | 194 | Process { 195 | $transaction = @( 196 | @{ 197 | "detection_id" = $Detection.Id; 198 | "field_to_update" = $updateFields; 199 | } 200 | ) 201 | $json = ConvertTo-Json $transaction 202 | Write-Verbose "$($json)" 203 | Invoke-CyRestMethod -API $API -Method POST -Uri "$($API.BaseUrl)/detections/v2/update/" -Body $json 204 | } 205 | } 206 | 207 | <# 208 | .SYNOPSIS 209 | Gets a list of all detection exceptions from the console. 210 | 211 | .PARAMETER API 212 | Optional. API Handle (use only when not using session scope). 213 | #> 214 | 215 | function Get-CyDetectionExceptionList { 216 | Param ( 217 | [parameter(Mandatory=$false)] 218 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle 219 | ) 220 | Read-CyData -API $API -Uri "$($API.BaseUrl)/exceptions/v2" -QueryParams $params 221 | } 222 | 223 | 224 | <# 225 | .SYNOPSIS 226 | Gets a definition for a detection exception 227 | 228 | .PARAMETER API 229 | Optional. API Handle (use only when not using session scope). 230 | 231 | .PARAMETER Exception 232 | The exception to retrieve (use object obtained with "Get-CyDetectionExceptionList") 233 | #> 234 | function Get-CyDetectionExceptionDetail { 235 | Param ( 236 | [parameter(Mandatory=$false)] 237 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 238 | [parameter(Mandatory=$true, ValueFromPipeline=$true)] 239 | [object[]]$Exception 240 | ) 241 | 242 | Begin { 243 | 244 | } 245 | 246 | Process { 247 | Invoke-CyRestMethod -API $API -Method GET -Uri "$($API.BaseUrl)/exceptions/v2/$($Exception.id)" | Convert-CyObject 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /CyOpticsInstaQuery.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Creates a new InstaQuery. 4 | 5 | .PARAMETER API 6 | Optional. API Handle (use only when not using session scope). 7 | #> 8 | function New-CyInstaQuery { 9 | Param ( 10 | [parameter(Mandatory=$false)] 11 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 12 | [parameter(Mandatory=$true)] 13 | [String]$Name, 14 | [parameter(Mandatory=$false)] 15 | [String]$Description, 16 | [parameter(Mandatory=$true)] 17 | [ValidateSet ("File_Path", 18 | "File_MD5", 19 | "File_SHA256", 20 | "File_Owner", 21 | "File_CreationDateTime", 22 | "Process_Name", 23 | "Process_CommandLine", 24 | "Process_PrimaryImagePath", 25 | "Process_PrimaryImageMd5", 26 | "Process_StartDateTime", 27 | "NetworkConnection_DestAddr", 28 | "NetworkConnection_DestPort", 29 | "RegistryKey_ProcessName", 30 | "RegistryKey_ProcessPrimaryImagePath", 31 | "RegistryKey_ValueName", 32 | "RegistryKey_FilePath", 33 | "RegistryKey_FileMd5", 34 | "RegistryKey_IsPersistencePoint")] 35 | [String]$QueryType, 36 | [parameter(Mandatory=$false)] 37 | [bool]$CaseSensitive = $false, 38 | [parameter(Mandatory=$false)] 39 | [ValidateSet ("Fuzzy", "Exact")] 40 | [String]$MatchType = "Fuzzy", 41 | [parameter(Mandatory=$true)] 42 | [String[]]$Value, 43 | [parameter(Mandatory=$true)] 44 | [object[]]$Zones 45 | ) 46 | 47 | Begin { 48 | } 49 | 50 | Process { 51 | $qt = $QueryType.Split("_") 52 | $params = @{ 53 | name = $Name; 54 | case_sensitive = $CaseSensitive; 55 | match_type = $MatchType; 56 | match_values = @( $Value ); 57 | artifact = $qt[0]; 58 | match_value_type = $qt[1]; 59 | zones = @( $Zones.id | ForEach-Object { $_.ToUpper() -replace "-" } ) 60 | # filters = @{ 61 | #aspect = "OS"; 62 | #value = "Windows" 63 | #} 64 | } 65 | 66 | if (![String]::IsNullOrEmpty($Description)) { 67 | $params.description = $Description 68 | } else { 69 | # cannot be empty. 70 | $params.description = $Name 71 | } 72 | 73 | $json = '{"name":"powershe- Proc Name","description":"","artifact":"Process","match_value_type":"Name","match_values":["powershell.exe"],"case_sensitive":false,"match_type":"Fuzzy","zones":["979951FC8E724A51B31105AC19BC1C8B","2D567BB4B1144F77BD4EB4D2D111AB70"]}' | ConvertFrom-Json 74 | $json = ConvertTo-Json $json 75 | 76 | $json = ConvertTo-Json $params 77 | $json 78 | 79 | Invoke-CyRestMethod -Method POST -API $API -Uri "$($API.BaseUrl)/instaqueries/v2" -Body $json 80 | } 81 | } 82 | 83 | <# 84 | .SYNOPSIS 85 | Gets an InstaQuery status 86 | 87 | .PARAMETER API 88 | Optional. API Handle (use only when not using session scope). 89 | #> 90 | function Get-CyInstaQueryResults { 91 | Param ( 92 | [parameter(Mandatory=$false)] 93 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 94 | [parameter(Mandatory=$false)] 95 | [Object]$InstaQuery 96 | ) 97 | 98 | $queryId = $InstaQuery.id 99 | 100 | Invoke-CyRestMethod -API $API -Method GET -Uri "$($API.BaseUrl)/instaqueries/v2/$($queryId)/results" 101 | } 102 | 103 | <# 104 | .SYNOPSIS 105 | Gets all InstaQuery queries in the tenant 106 | 107 | .PARAMETER API 108 | Optional. API Handle (use only when not using session scope). 109 | #> 110 | function Get-CyInstaQueries { 111 | Param ( 112 | [parameter(Mandatory=$false)] 113 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 114 | [parameter(Mandatory=$false)] 115 | [bool]$IncludeArchived = $false, 116 | [parameter(Mandatory=$false)] 117 | [String]$Query = "", 118 | [parameter(Mandatory=$false)] 119 | [ValidateSet ("name", "description", "artifact", "match_value_type")] 120 | [string]$Sort 121 | ) 122 | 123 | $params = @{ 124 | archived = $IncludeArchived; 125 | q = $Query 126 | } 127 | 128 | if (![String]::IsNullOrEmpty($Sort)) { 129 | $params.sort = $Sort 130 | } 131 | 132 | Read-CyData -API $API -Uri "$($API.BaseUrl)/instaqueries/v2" -QueryParams $params 133 | } 134 | 135 | <# 136 | .SYNOPSIS 137 | Gets the device lockdown status and history. 138 | 139 | .PARAMETER API 140 | Optional. API Handle (use only when not using session scope). 141 | 142 | .PARAMETER Device 143 | The device object to query for. 144 | #> 145 | function Get-CyLockdownStatus { 146 | Param ( 147 | [parameter(Mandatory=$false)] 148 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 149 | [parameter(Mandatory=$false)] 150 | [object]$Device 151 | ) 152 | Invoke-CyRestMethod -API $API -Method GET -Uri "$($API.BaseUrl)/devicecommands/v2/$($Device.id.ToUpper() -replace "-" )/lockdown" | Convert-CyObject 153 | } -------------------------------------------------------------------------------- /CyOpticsPackages.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Gets a list of all packages from the console. 4 | 5 | .PARAMETER API 6 | Optional. API Handle (use only when not using session scope). 7 | #> 8 | function Get-CyPackagesList { 9 | Param ( 10 | [parameter(Mandatory=$false)] 11 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 12 | [parameter(Mandatory=$false)] 13 | [String]$UpdatedByLogin, 14 | [parameter(Mandatory=$false)] 15 | [int]$Timeout, 16 | [parameter(Mandatory=$false)] 17 | [ValidateSet ("custom", "cylance")] 18 | [String]$Category, 19 | [parameter(Mandatory=$false)] 20 | [ValidateSet ("started", "success", "failed", "timeout")] 21 | [string]$Status, 22 | [parameter(Mandatory=$false)] 23 | [ValidateSet ("packageId", "uploadedOn", "uploadedBy.id", "uploadedBy.login", "size", "status", "timeout", "packageDescriptor.name")] 24 | [string]$Sort 25 | ) 26 | 27 | $params = @{} 28 | 29 | if (![String]::IsNullOrEmpty($Status)) { 30 | $params.status = $Status 31 | } 32 | 33 | if (![String]::IsNullOrEmpty($UpdatedByLogin)) { 34 | $params.'updatedBy.Login' = $UpdatedByLogin 35 | } 36 | 37 | if (![String]::IsNullOrEmpty($Category)) { 38 | $params.category = $Category 39 | } 40 | 41 | if (![String]::IsNullOrEmpty($DetectedOn)) { 42 | $params.detected_on = $DetectedOn 43 | } 44 | 45 | if (0 -ne $Timeout) { 46 | $params.timeout = $Timeout 47 | } 48 | 49 | if (![String]::IsNullOrEmpty($Sort)) { 50 | $params.sort = $Sort 51 | } 52 | 53 | Read-CyData -API $API -Uri "$($API.BaseUrl)/packages/v2" -QueryParams $params 54 | } 55 | -------------------------------------------------------------------------------- /CyOpticsRules.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Gets a list of all detection rules from the console. 4 | 5 | .PARAMETER API 6 | Optional. API Handle (use only when not using session scope). 7 | #> 8 | function Get-CyDetectionRuleList { 9 | Param ( 10 | [parameter(Mandatory=$false)] 11 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle 12 | ) 13 | 14 | Read-CyData -API $API -Uri "$($API.BaseUrl)/rules/v2" 15 | } 16 | 17 | <# 18 | .SYNOPSIS 19 | Retrieves the given detection rule native JSON structure 20 | 21 | .PARAMETER API 22 | Optional. API Handle (use only when not using session scope). 23 | 24 | .PARAMETER Exception 25 | The detection rule to retrieve. 26 | #> 27 | function Get-CyDetectionRuleDetail { 28 | Param ( 29 | [parameter(Mandatory=$false)] 30 | [ValidateNotNullOrEmpty()] 31 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 32 | [Parameter( 33 | Mandatory=$true, 34 | ValueFromPipeline=$true, 35 | ValueFromPipelineByPropertyName=$true)] 36 | [object[]]$Rule 37 | ) 38 | 39 | Process { 40 | Invoke-CyRestMethod -API $API -Method GET -Uri "$($API.BaseUrl)/rules/v2/$($Rule.id)" | Convert-CyObject 41 | } 42 | } 43 | 44 | 45 | <# 46 | .SYNOPSIS 47 | Gets a list of all detection rules from the console. 48 | 49 | .PARAMETER API 50 | Optional. API Handle (use only when not using session scope). 51 | #> 52 | function Get-CyDetectionExceptionList { 53 | Param ( 54 | [parameter(Mandatory=$false)] 55 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle 56 | ) 57 | 58 | Read-CyData -API $API -Uri "$($API.BaseUrl)/exceptions/v2" 59 | } 60 | 61 | <# 62 | .SYNOPSIS 63 | Retrieves the given detection exception native JSON structure 64 | 65 | .PARAMETER API 66 | Optional. API Handle (use only when not using session scope). 67 | 68 | .PARAMETER Exception 69 | The exception to retrieve. 70 | #> 71 | function Get-CyDetectionExceptionDetail { 72 | Param ( 73 | [parameter(Mandatory=$false)] 74 | [ValidateNotNullOrEmpty()] 75 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 76 | [Parameter( 77 | Mandatory=$true, 78 | ValueFromPipeline=$true, 79 | ValueFromPipelineByPropertyName=$true)] 80 | [object[]]$Exception 81 | ) 82 | 83 | Process { 84 | Invoke-CyRestMethod -API $API -Method GET -Uri "$($API.BaseUrl)/exceptions/v2/$($Exception.id)" | Convert-CyObject 85 | } 86 | } -------------------------------------------------------------------------------- /CyPolicies.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Gets a list of all policies from the console. 4 | 5 | .PARAMETER API 6 | Optional. API Handle (use only when not using session scope). 7 | #> 8 | function Get-CyPolicyList { 9 | Param ( 10 | [parameter(Mandatory=$false)] 11 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle 12 | ) 13 | 14 | Read-CyData -API $API -Uri "$($API.BaseUrl)/policies/v2" 15 | } 16 | 17 | <# 18 | .SYNOPSIS 19 | Sets the policy for a specific device 20 | 21 | .PARAMETER API 22 | Optional. API Handle (use only when not using session scope). 23 | 24 | .PARAMETER Device 25 | The device(s) to set policy for 26 | 27 | .PARAMETER Policy 28 | The policy to assign to device 29 | #> 30 | function Set-CyPolicyForDevice { 31 | Param ( 32 | [parameter(Mandatory=$false)] 33 | [ValidateNotNullOrEmpty()] 34 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 35 | [Parameter( 36 | Mandatory=$true, 37 | ValueFromPipeline=$true, 38 | ValueFromPipelineByPropertyName=$true)] 39 | [object]$Device, 40 | [Parameter(Mandatory=$true)] 41 | [object]$Policy 42 | ) 43 | 44 | Begin 45 | { 46 | if (([string]::IsNullOrEmpty($Policy.policy_id)) -and ([string]::IsNullOrEmpty($Policy.id))) 47 | { 48 | throw "Policy object does not contain 'policy_id' or 'id' property." 49 | } 50 | if (([string]::IsNullOrEmpty($Policy.policy_id)) -and (![string]::IsNullOrEmpty($Policy.id))) 51 | { 52 | $PolicyID = $policy.id 53 | } 54 | if (!([string]::IsNullOrEmpty($Policy.policy_id)) -and ([string]::IsNullOrEmpty($Policy.id))) 55 | { 56 | $PolicyID = $Policy.policy_id 57 | } 58 | if (!([string]::IsNullOrEmpty($Policy.policy_id)) -and (!([string]::IsNullOrEmpty($Policy.id)))) 59 | { 60 | if(($Policy.policy_id).tostring() -eq ($Policy.id).tostring()) 61 | { 62 | $PolicyID = $policy.id 63 | } 64 | else 65 | { 66 | throw 'Different value found in policy_id and id, expected to be the same' 67 | } 68 | } 69 | } 70 | 71 | Process { 72 | if (($null -eq $Device) -or ($null -eq $Device.id) -or ([String]::IsNullOrEmpty($Device.id))) { 73 | throw "Set-CyPolicyForDevice: Device ID cannot be null or empty." 74 | } 75 | 76 | $updateMap = @{ 77 | "name" = $($Device.name) 78 | "policy_id" = $PolicyID 79 | } 80 | 81 | $json = $updateMap | ConvertTo-Json 82 | # remain silent 83 | $null = Invoke-CyRestMethod -API $API -Method PUT -Uri "$($API.BaseUrl)/devices/v2/$($Device.id)" -ContentType "application/json; charset=utf-8" -Body $json 84 | } 85 | } 86 | 87 | <# 88 | .SYNOPSIS 89 | Retrieves the given Policy from the console. Gets the full version, not a shallow object. 90 | 91 | .PARAMETER API 92 | Optional. API Handle (use only when not using session scope). 93 | 94 | .PARAMETER Policy 95 | The policy to retrieve. 96 | #> 97 | function Get-CyPolicy { 98 | Param ( 99 | [parameter(Mandatory=$false)] 100 | [ValidateNotNullOrEmpty()] 101 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 102 | [Parameter( 103 | Mandatory=$true, 104 | ValueFromPipeline=$true, 105 | ValueFromPipelineByPropertyName=$true)] 106 | [object]$Policy 107 | ) 108 | 109 | Process { 110 | Invoke-CyRestMethod -API $API -Method GET -Uri "$($API.BaseUrl)/policies/v2/$($Policy.id)" | Convert-CyObject 111 | } 112 | } 113 | 114 | <# 115 | .SYNOPSIS 116 | Removes the given Policy from the console. 117 | 118 | .PARAMETER API 119 | Optional. API Handle (use only when not using session scope). 120 | 121 | .PARAMETER Policy 122 | The policy to retrieve the Detail for. 123 | #> 124 | function Remove-CyPolicy { 125 | Param ( 126 | [parameter(Mandatory=$false)] 127 | [ValidateNotNullOrEmpty()] 128 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 129 | [Parameter( 130 | Mandatory=$true, 131 | ValueFromPipeline=$true, 132 | ValueFromPipelineByPropertyName=$true)] 133 | [object]$Policy 134 | ) 135 | 136 | Process { 137 | Invoke-CyRestMethod -API $API -Method DELETE -Uri "$($API.BaseUrl)/policies/v2/$($Policy.id)" | Convert-CyObject 138 | } 139 | } 140 | 141 | <# 142 | .SYNOPSIS 143 | Returns an empty scaffold for a CylancePROTECT policy. 144 | #> 145 | function Get-CyPolicyScaffold { 146 | Param ( 147 | ) 148 | @{ 149 | logpolicy = @{ 150 | log_upload = @{ 151 | compress = $true 152 | delete = $false 153 | } 154 | maxlogsize = "100" 155 | retentiondays = "30" 156 | } 157 | policy_name = "" 158 | policy = @( 159 | @{ 160 | name = "auto_blocking" 161 | value = "0" 162 | } 163 | @{ 164 | name = "auto_delete" 165 | value = "0" 166 | } 167 | @{ 168 | name = "auto_uploading" 169 | value = "0" 170 | } 171 | @{ 172 | name = "autoit_auto_uploading" 173 | value = "0" 174 | } 175 | @{ 176 | name = "custom_thumbprint" 177 | value = $null 178 | } 179 | @{ 180 | name = "data_privacy" 181 | value = "0" 182 | } 183 | @{ 184 | name = "days_until_deleted" 185 | value = "14" 186 | } 187 | @{ 188 | name = "device_control" 189 | value = "0" 190 | } 191 | @{ 192 | name = "docx_auto_uploading" 193 | value = "0" 194 | } 195 | @{ 196 | name = "full_disc_scan" 197 | value = "0" 198 | } 199 | @{ 200 | name = "kill_running_threats" 201 | value = "0" 202 | } 203 | @{ 204 | name = "logpolicy" 205 | value = "1" 206 | } 207 | @{ 208 | name = "low_confidence_threshold" 209 | value = "-600" 210 | } 211 | @{ 212 | name = "memory_exploit_detection" 213 | value = "0" 214 | } 215 | @{ 216 | name = "ole_auto_uploading" 217 | value = "0" 218 | } 219 | @{ 220 | name = "optics" 221 | value = "0" 222 | } 223 | @{ 224 | name = "optics_application_control_auto_upload" 225 | value = "0" 226 | } 227 | @{ 228 | name = "optics_malware_auto_upload" 229 | value = "0" 230 | } 231 | @{ 232 | name = "optics_memory_defense_auto_upload" 233 | value = "0" 234 | } 235 | @{ 236 | name = "optics_script_control_auto_upload" 237 | value = "0" 238 | } 239 | @{ 240 | name = "optics_set_disk_usage_maximum_fixed" 241 | value = "1000" 242 | } 243 | @{ 244 | name = "pdf_auto_uploading" 245 | value = "0" 246 | } 247 | @{ 248 | name = "powershell_auto_uploading" 249 | value = "0" 250 | } 251 | @{ 252 | name = "prevent_service_shutdown" 253 | value = "0" 254 | } 255 | @{ 256 | name = "python_auto_uploading" 257 | value = "0" 258 | } 259 | @{ 260 | name = "sample_copy_path" 261 | value = $null 262 | } 263 | @{ 264 | name = "scan_max_archive_size" 265 | value = "150" 266 | } 267 | @{ 268 | name = "script_control" 269 | value = "0" 270 | } 271 | @{ 272 | name = "show_notifications" 273 | value = "0" 274 | } 275 | @{ 276 | name = "threat_report_limit" 277 | value = "500" 278 | } 279 | @{ 280 | name = "trust_files_in_scan_exception_list" 281 | value = "0" 282 | } 283 | @{ 284 | name = "watch_for_new_files" 285 | value = "0" 286 | } 287 | @{ 288 | name = "scan_exception_list" 289 | value = @() 290 | } 291 | ) 292 | memoryviolation_actions = @{ 293 | memory_violations = @( 294 | @{ 295 | violation_type = "lsassread" 296 | action = "Alert" 297 | }, 298 | @{ 299 | violation_type = "outofprocessunmapmemory" 300 | action = "Alert" 301 | }, 302 | @{ 303 | violation_type = "stackpivot" 304 | action = "Alert" 305 | }, 306 | @{ 307 | violation_type = "stackprotect" 308 | action = "Alert" 309 | }, 310 | @{ 311 | violation_type = "outofprocessoverwritecode" 312 | action = "Alert" 313 | }, 314 | @{ 315 | violation_type = "outofprocesscreatethread" 316 | action = "Alert" 317 | }, 318 | @{ 319 | violation_type = "overwritecode" 320 | action = "Alert" 321 | }, 322 | @{ 323 | violation_type = "outofprocesswritepe" 324 | action = "Alert" 325 | }, 326 | @{ 327 | violation_type = "outofprocessallocation" 328 | action = "Alert" 329 | }, 330 | @{ 331 | violation_type = "outofprocessmap" 332 | action = "Alert" 333 | }, 334 | @{ 335 | violation_type = "outofprocesswrite" 336 | action = "Alert" 337 | }, 338 | @{ 339 | violation_type = "outofprocessapc" 340 | action = "Alert" 341 | } 342 | ) 343 | memory_violations_ext = @( 344 | @{ 345 | violation_type = "dyldinjection" 346 | action = "Alert" 347 | }, 348 | @{ 349 | violation_type = "trackdataread" 350 | action = "Alert" 351 | }, 352 | @{ 353 | violation_type = "zeroallocate" 354 | action = "Alert" 355 | }, 356 | @{ 357 | violation_type = "maliciouspayload" 358 | action = "Alert" 359 | } 360 | ) 361 | memory_exclusion_list = @() 362 | } 363 | file_exclusions = @() 364 | checksum = "" 365 | script_control = @{ 366 | global_settings = @{ 367 | allowed_folders = @() 368 | control_mode = "Alert" 369 | } 370 | powershell_settings = @{ 371 | console_mode = "Allow" 372 | control_mode = "Alert" 373 | } 374 | macro_settings = @{ 375 | control_mode = "Alert" 376 | } 377 | activescript_settings = @{ 378 | control_mode = "Alert" 379 | } 380 | } 381 | filetype_actions = @{ 382 | suspicious_files = @( 383 | @{ 384 | actions = "0" 385 | file_type = "executable" 386 | } 387 | ) 388 | threat_files = @( 389 | @{ 390 | actions = "0" 391 | file_type = "executable" 392 | } 393 | ) 394 | } 395 | } 396 | } 397 | 398 | <# 399 | .SYNOPSIS 400 | Creates a new policy in the console. 401 | 402 | .DESCRIPTION 403 | The new policy is either created with default settings, or with the settings from the policy object passed. 404 | 405 | If the policy object is an existing policy, its ID, policy_name and other (checksum, last modified timestamp) properties 406 | will be overwritten before the policy is created. 407 | 408 | .PARAMETER Name 409 | The name of the new policy 410 | 411 | .PARAMETER Policy 412 | Policy object with settings to use. Optional. 413 | 414 | .PARAMETER User 415 | User object (as returned by Get-CyUserDetail or Get-CyUserList) to use as creator, OR auser's email address. 416 | #> 417 | function New-CyPolicy { 418 | Param ( 419 | [parameter(Mandatory=$false)] 420 | [ValidateNotNullOrEmpty()] 421 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 422 | [Parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] 423 | [String]$Name, 424 | [Parameter(Mandatory=$false)] 425 | [object]$Policy = $null, 426 | [Parameter(Mandatory=$true)] 427 | [object]$User 428 | ) 429 | Begin { 430 | # support passing the email address instead of a user object 431 | if ($User -match ".+@.+") { 432 | $User = Get-CyUserByEmail -API $API -Email $User 433 | } 434 | 435 | if ($null -eq $($User.id)) { 436 | Throw "ID not found in user object" 437 | return 438 | } 439 | } 440 | 441 | Process { 442 | if ($Policy -eq $null) { 443 | $Policy = Get-CyPolicyScaffold 444 | } 445 | 446 | # remove fields that don't sit well with policy puts 447 | $Policy.checksum = "" 448 | $Policy.policy_name = $Name 449 | $Policy.psobject.properties.Remove("policy_utctimestamp") 450 | $Policy.psobject.properties.Remove("policy_id") 451 | 452 | $json = @{ 453 | policy = $Policy 454 | user_id = $($User.id) 455 | } | ConvertTo-Json -Depth 100 456 | 457 | Invoke-CyRestMethod -API $API -Method POST -Uri "$($API.BaseUrl)/policies/v2" -ContentType "application/json; charset=utf-8" -Body $json 458 | } 459 | } 460 | 461 | function Update-CyPolicy { 462 | Param ( 463 | [parameter(Mandatory=$false)] 464 | [ValidateNotNullOrEmpty()] 465 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 466 | [Parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] 467 | [object]$Policy = $null, 468 | [Parameter(Mandatory=$true)] 469 | [object]$User 470 | ) 471 | Begin { 472 | # support passing the email address instead of a user object 473 | if ($User -match ".+@.+") { 474 | $User = Get-CyUserByEmail -API $API -Email $User 475 | } 476 | } 477 | 478 | Process { 479 | # remove fields that need to be removed for policy puts 480 | $Policy.checksum = "" 481 | $Policy.psobject.properties.Remove("policy_utctimestamp") 482 | 483 | $json = @{ 484 | policy = $Policy 485 | user_id = $($User.id) 486 | } | ConvertTo-Json -Depth 100 487 | 488 | Invoke-CyRestMethod -API $API -Method PUT -Uri "$($API.BaseUrl)/policies/v2" -ContentType "application/json; charset=utf-8" -Body $json 489 | } 490 | } 491 | 492 | <# 493 | .SYNOPSIS 494 | Creates a copy of an existing policy under a new name with identical settings. 495 | 496 | .PARAMETER SourcePolicyName 497 | Original policy 498 | 499 | .PARAMETER TargetPolicyName 500 | Target policy name 501 | 502 | .PARAMETER User 503 | User object (as returned by Get-CyUserDetail or Get-CyUserList) to use as creator. 504 | #> 505 | function Copy-CyPolicy { 506 | Param ( 507 | [parameter(Mandatory=$false)] 508 | [ValidateNotNullOrEmpty()] 509 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 510 | [Parameter(Mandatory=$true)] 511 | [String]$SourcePolicyName, 512 | [Parameter(Mandatory=$true)] 513 | [object]$TargetPolicyName, 514 | [Parameter(Mandatory=$true)] 515 | [object]$User 516 | ) 517 | 518 | $shallowPolicy = Get-CyPolicyList | Where-Object name -eq $SourcePolicyName 519 | $policy = Get-CyPolicy -API $API -Policy $shallowPolicy 520 | 521 | if ($null -eq $policy) { 522 | throw "Source policy not found" 523 | return 524 | } 525 | 526 | New-CyPolicy -Policy $policy -User $User -Name $TargetPolicyName 527 | } 528 | 529 | <# 530 | .SYNOPSIS 531 | Adds a value to a list setting in a policy 532 | 533 | .PARAMETER Type 534 | The type of setting to add a value to 535 | 536 | .PARAMETER Value 537 | The value to add 538 | 539 | .PARAMETER Policy 540 | The policy to add the setting to 541 | #> 542 | function Add-CyPolicyListSetting { 543 | Param ( 544 | [Parameter(Mandatory=$true)] 545 | [ValidateSet ("MemDefExclusionPath", "ScriptControlExclusionPath", "ScanExclusion" )] 546 | [String[]]$Type, 547 | [Parameter(Mandatory=$false, 548 | ValueFromPipeline=$true, 549 | ValueFromPipelineByPropertyName=$true)] 550 | [pscustomobject]$Policy, 551 | [Parameter(Mandatory=$false)] 552 | [String]$Value 553 | ) 554 | Begin { 555 | } 556 | 557 | Process { 558 | if (! ([String]::IsNullOrEmpty($Value))) { 559 | switch ($Type) { 560 | "MemDefExclusionPath" { 561 | if (! (($Policy.memoryviolation_actions.memory_exclusion_list -contains $Value))) 562 | { 563 | Write-Verbose "Adding policy memory exclusion: $($Value)" 564 | $Policy.memoryviolation_actions.memory_exclusion_list += $Value 565 | } 566 | } 567 | "ScriptControlExclusionPath" { 568 | if (! ($Policy.script_control.global_settings.allowed_folders -contains $Value)) 569 | { 570 | Write-Verbose "Adding script control exclusion: $($Value)" 571 | $Policy.script_control.global_settings.allowed_folders += $Value 572 | } 573 | } 574 | "ScanExclusion" { 575 | $scan_exception_list = $Policy.policy | Where-Object name -eq scan_exception_list 576 | if (! ($scan_exception_list.value -contains $Value)) 577 | { 578 | Write-Verbose "Adding scan folder exclusion: $($Value)" 579 | $scan_exception_list.value += $Value 580 | } 581 | } 582 | } 583 | } 584 | } 585 | } 586 | -------------------------------------------------------------------------------- /CyTDR.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NAME 3 | Cylance-TDR 4 | 5 | .SYNOPSIS 6 | This module contains various functions to deal with the Cylance Console's Threat Data Reports (TDRs) 7 | 8 | .NOTES 9 | 10 | .LINK 11 | Blog: http://tietze.io/ 12 | Jan Tietze 13 | #> 14 | 15 | 16 | <# 17 | .SYNOPSIS 18 | Downloads Cylance console's TDR reports and converts them into Excel. 19 | 20 | .DESCRIPTION 21 | The Cylance console makes certain reports available in CSV format about events and data that it collects. 22 | An even richer data set is available in real-time via Syslog, but if you want to periodically pull data 23 | from the console to keep e.g. historical snapshots or run additional processing steps on the data outside 24 | your SIEM, this script can be useful. 25 | 26 | .PARAMETER TDRPath 27 | Mandatory, the base path to store the TDR data. 28 | 29 | .PARAMETER ConsoleId 30 | Optional. Specify if you only want to retrieve a particular console's data. Is used as the key to load from the "consoles" map. 31 | 32 | .PARAMETER AccessToken 33 | Optional. When specified with "RetrieveConsoleId", this token will be used instead of performing a lookup from the "consoles" map. 34 | 35 | .PARAMETER DefaultTDRUrl 36 | Optional. When no TDR URL is specified in the console profile, use this default TDR URL (default = EUC1 shard) 37 | 38 | .LINK 39 | Blog: http://tietze.io/ 40 | Jan Tietze 41 | 42 | #> 43 | 44 | <# 45 | .SYNOPSIS 46 | Downloads all TDRs for a console into given directory structure 47 | 48 | .DESCRIPTION 49 | TDRUrl defaults to EUC1 shard. 50 | #> 51 | function Get-CyTDRs { 52 | [CmdletBinding()] 53 | Param ( 54 | [parameter(Mandatory=$True, ParameterSetName="Direct")] 55 | [String]$Id, 56 | [parameter(Mandatory=$True, ParameterSetName="Direct")] 57 | [String]$AccessToken, 58 | [parameter(Mandatory=$False)] 59 | [String]$TDRUrl = "https://protect-euc1.cylance.com/Reports/ThreatDataReportV1/", 60 | [parameter(Mandatory=$False)] 61 | [ValidateScript({Test-Path $_ -PathType Container })] 62 | [String]$TDRPath = "$($HOME)\TDRs" 63 | ) 64 | DynamicParam { 65 | Get-CyConsoleArgumentAutoCompleter -Mandatory -ParameterName "Console" -ParameterSetName "ByReference" 66 | } 67 | 68 | Begin { 69 | $TDRPath = (Get-Item $TDRPath).FullName 70 | $Timestamp = $(Get-Date -UFormat "%Y%m%d-%H%M%S") 71 | 72 | switch ($PSCmdlet.ParameterSetName) 73 | { 74 | "Direct" { 75 | } 76 | "ByReference" { 77 | $ConsoleDetails = (Get-CyConsoleConfig) | Where-Object ConsoleId -eq $PSBoundParameters.Console 78 | $Id = $ConsoleDetails.ConsoleId 79 | $AccessToken = $ConsoleDetails.Token 80 | if (($null -ne $ConsoleDetails.TDRUrl) -and ([String]::Empty -ne $ConsoleDetails.TDRUrl)) { 81 | $TDRUrl = $ConsoleDetails.TDRUrl 82 | } 83 | } 84 | } 85 | Write-Verbose "Retrieving data for console ${Id} from TDR URL ${TDRUrl} with token ${AccessToken} and storing to TDR path ${TDRPath}" 86 | 87 | ForEach ($TDRType in @("cleared", "devices", "events", "indicators", "policies", "threats", "externaldevices", "memoryprotection")) { 88 | $DirectoryName = $TDRPath + "\" + $Id+ "\" + $TDRType + "\" 89 | if (-Not (Test-Path $DirectoryName -PathType Container)) { 90 | New-Item -Path $DirectoryName -ItemType Directory -Force | Out-Null 91 | } 92 | $Filename = "${DirectoryName}\${Timestamp}_${Id}_${TDRType}.csv" 93 | 94 | # retrieve CSV 95 | $Url = "${TDRUrl}${TDRType}/${AccessToken}" 96 | # was: Invoke-WebRequest -Uri $Url -OutFile $Filename | Out-Null 97 | $null = Invoke-CyRestMethod -Method GET -Uri $Url -OutFile $Filename 98 | } 99 | 100 | # create XLSX summary - invoke with last retrieved component name 101 | Convert-CyTDRsToXLSX -CSVPath $Filename -Overwrite $False } 102 | 103 | Process { 104 | } 105 | } 106 | 107 | 108 | <# 109 | .SYNOPSIS 110 | Expands a command-delimited field (like "zone" fields in e.g. device TDR CSVs) into separate fields so that they can be used to sort effectively. 111 | #> 112 | function Convert-FromCSVField { 113 | Param ( 114 | [parameter(Mandatory=$True)] 115 | [PSObject]$Data, 116 | [Parameter(Mandatory=$True)] 117 | [Array]$ExpandFields, 118 | [Parameter(Mandatory=$True)] 119 | [Array]$FieldPrefix 120 | ) 121 | $Data | ForEach-Object { 122 | # each row 123 | foreach ($field in $ExpandFields) { 124 | if (![string]::IsNullOrWhiteSpace($_.$field)) { 125 | $fs = $_.$field.Split(",") 126 | foreach ($f in $fs) { 127 | Add-Member -NotePropertyName "$($FieldPrefix)$($f)" -InputObject $_ -NotePropertyValue "x" 128 | } 129 | } 130 | } 131 | $_ 132 | } 133 | 134 | } 135 | 136 | <# 137 | .SYNOPSIS 138 | Converts a Cylance TDR report to Excel. 139 | 140 | .DESCRIPTION 141 | The Cylance console makes certain reports available in CSV format about events and data that it collects. 142 | An even richer data set is available in real-time via Syslog, but if you want to periodically pull data 143 | from the console to keep e.g. historical snapshots or run additional processing steps on the data outside 144 | your SIEM, this script can be useful. 145 | #> 146 | function Convert-CyTDRsToXLSX { 147 | Param ( 148 | [parameter(Mandatory=$True)] 149 | [ValidateScript({Test-Path $_ -PathType Leaf })] 150 | [String]$CSVPath, 151 | [Parameter(Mandatory=$False)] 152 | [Boolean]$Overwrite = $False 153 | ) 154 | 155 | function Get-ExcelSummaryReport-FromTDR ($TDRPath, $ConsoleId, $Timestamp, $OutputXLSX) { 156 | ForEach ($TDRType in @("cleared", "devices", "events", "indicators", "policies", "externaldevices", "memoryprotection", "threats")) { 157 | $InputCSV = $TDRPath + "\" + $ConsoleId + "\" + $TDRType + "\" + $Timestamp + "_" + $ConsoleId + "_" + $TDRType + ".csv" 158 | 159 | if ((Test-Path -Path $InputCSV -PathType Leaf) -ne $true) { 160 | # skip non-existent TDRs 161 | continue 162 | } 163 | 164 | $data = Import-CSV -Delimiter "," -Path $InputCSV 165 | Write-Verbose "Processing CSV into XLSX: ${InputCSV}" 166 | 167 | # time stamps are named: 168 | # "threats": Create Time, Modification Time, Access Time, First Found, Last Found => 7/20/2017 12:40:25 AM 169 | # "memoryprotection" ADDED => 11/23/2017 9:34:05 AM 170 | # "externaldevices": Date => 11/23/2017 9:34:05 AM 171 | # "events": Date => 11/29/2017 3:14:33 PM 172 | # "devices": created, "Online Date", "Offline Date" => 11/2/2017 10:54:32 AM 173 | # "cleared": "Date Removed" => 11/24/2017 10:02:49 AM 174 | 175 | if ($null -eq $data) { continue } 176 | 177 | switch ($TDRType) { 178 | "threats" { 179 | $Data = Convert-FromCSVDate -Data $Data -Fields @("Create Time", "Modification Time", "Access Time", "First Found", "Last Found") 180 | } 181 | "memoryprotection" { 182 | $Data = Convert-FromCSVDate -Data $Data -Fields @("ADDED") 183 | } 184 | "externaldevices" { 185 | # the "externaldevices" TDR uses an inconsistent time format string and has no "seconds" field. 186 | $Data = Convert-FromCSVDate -Data $Data -Fields @("Date") -Format "M/d/yy h:mm tt" 187 | } 188 | "events" { 189 | $Data = Convert-FromCSVDate -Data $Data -Fields @("Date") 190 | } 191 | "devices" { 192 | $Data = Convert-FromCSVDate -Data $Data -Fields @("Created", "Online Date", "Offline Date") 193 | } 194 | "cleared" { 195 | $Data = Convert-FromCSVDate -Data $Data -Fields @("Date Removed") 196 | 197 | } 198 | } 199 | 200 | $Data | Export-Excel $OutputXLSX -WorkSheetname $TDRType -AutoSize -TableName "${TDRType}Table" 201 | } 202 | 203 | } 204 | 205 | # got a CSV path - make absolute path 206 | $CSVPath = (Get-Item $CSVPath).FullName 207 | 208 | $s = [regex]::match($CSVPath,'(.+)\\([^\\]+)\\([^\\]+)\\([0-9]{8}-[0-9]{6})_([^\\_)]+)_([^\\]+)\.csv').Groups 209 | $TDRPath = $s[1].Value 210 | $ConsoleId = $s[2].Value 211 | $TDRType = $s[3].Value 212 | $Timestamp = $s[4].Value 213 | # $ConsoleId2 = $s[5].Value 214 | # $ReportType2 = $s[6].Value 215 | 216 | # Generate Excel summary file from TDRs 217 | $OutputXLSX = $TDRPath + "\" + $ConsoleId + "\" + $Timestamp + "_" + $ConsoleId + ".xlsx" 218 | 219 | # check if exists 220 | If (Test-Path $OutputXLSX -PathType Leaf) { 221 | Write-Verbose "Output file ${OutputXLSX} exists." 222 | if ($Overwrite) { 223 | Write-Verbose "Removing output file ${OutputXLSX}" 224 | Remove-Item $OutputXLSX 225 | Get-ExcelSummaryReport-FromTDR -ConsoleId $ConsoleId -Timestamp $Timestamp -OutputXLSX $OutputXLSX -TDRPath $TDRPath 226 | } else { 227 | Write-Verbose "Not set to overwrite, will not remove/re-create file ${OutputXLSX}" 228 | } 229 | } else { 230 | Get-ExcelSummaryReport-FromTDR -ConsoleId $ConsoleId -Timestamp $Timestamp -OutputXLSX $OutputXLSX -TDRPath $TDRPath 231 | } 232 | } 233 | 234 | <# 235 | .SYNOPSIS 236 | Downloads a number of Cylance console's TDR reports and converts them into Excel. 237 | 238 | .DESCRIPTION 239 | Downloads a pre-configured list of of Cylance console's TDR reports and converts them into Excel. 240 | 241 | Configure "Consoles.json" in the TDR path with your console data; you can use "New-CyConsoleConfig" to create entries. 242 | 243 | .PARAMETER TDRPath 244 | Optional, the base path to store the TDR data. Defaults to $HOME\TDRs (use symoblic links!) 245 | 246 | .PARAMETER DefaultTDRUrl 247 | Optional. When no TDR URL is specified in the console profile, use this default TDR URL (default = US) 248 | #> 249 | function Get-CyTDRsForAllConsoles() { 250 | [CmdletBinding()] 251 | Param ( 252 | [parameter(Mandatory=$False)] 253 | [ValidateScript({Test-Path $_ -PathType Container })] 254 | [String]$TDRPath = "$($HOME)\TDRs", 255 | [parameter(Mandatory=$False)] 256 | [String]$DefaultTDRUrl = "https://protect.cylance.com/Reports/ThreatDataReportV1/" 257 | ) 258 | 259 | try { 260 | $Consoles = Get-CyConsoleConfig 261 | } catch { 262 | Write-Error "There was an error parsing or accessing the console JSON file: $($TDRPath)\Consoles.json" 263 | break 264 | } 265 | 266 | $Consoles | Where-Object Token -ne $null | Where-Object AutoRetrieve -ne $false | 267 | ForEach-Object { 268 | Write-Host "Retrieving console $($_.ConsoleId)..." 269 | $TDRUrl = if (([String]::Empty -eq $_.TDRUrl) -or ($_.TDRUrl -eq $null)) { $DefaultTDRUrl } else { $_.TDRUrl } 270 | Get-CyTDRs -TDRPath $TDRPath -Id $_.ConsoleId -AccessToken $_.Token -TDRUrl $TDRUrl 271 | } 272 | } -------------------------------------------------------------------------------- /CyThreats.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Gets the threat list 4 | 5 | .PARAMETER API 6 | Optional. API Handle (use only when not using session scope). 7 | #> 8 | function Get-CyThreatList { 9 | Param ( 10 | [parameter(Mandatory=$false)] 11 | [ValidateNotNullOrEmpty()] 12 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle 13 | ) 14 | 15 | Read-CyData -API $API -Uri "$($API.BaseUrl)/threats/v2" 16 | } 17 | 18 | <# 19 | .SYNOPSIS 20 | Gets the threat list for the given device 21 | 22 | .PARAMETER API 23 | Optional. API Handle (use only when not using session scope). 24 | 25 | .PARAMETER Device 26 | The device to retrieve the threats for. 27 | #> 28 | function Get-CyDeviceThreatList { 29 | Param ( 30 | [parameter(Mandatory=$false)] 31 | [ValidateNotNullOrEmpty()] 32 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 33 | [Parameter(Mandatory=$true,ParameterSetName="ByDevice",ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] 34 | [object]$Device, 35 | [Parameter(Mandatory=$true,ParameterSetName="ByDeviceId")] 36 | [object]$DeviceId 37 | ) 38 | 39 | Process { 40 | switch ($PSCmdlet.ParameterSetName) { 41 | "ByDevice" { 42 | $Uri = "$($API.BaseUrl)/devices/v2/$($Device.id)/threats" 43 | } 44 | "ByDeviceId" { 45 | $Uri = "$($API.BaseUrl)/devices/v2/$($DeviceId)/threats" 46 | } 47 | } 48 | 49 | Read-CyData -API $API -Uri $Uri 50 | } 51 | } 52 | 53 | <# 54 | .SYNOPSIS 55 | Update a device threat. 56 | 57 | .PARAMETER API 58 | Optional. API Handle (use only when not using session scope). 59 | 60 | .PARAMETER Action 61 | The action to take (quarantine or waive the threat) 62 | 63 | .PARAMETER Device 64 | The device object to apply this threat action to. 65 | #> 66 | function Update-CyDeviceThreat { 67 | Param ( 68 | [parameter(Mandatory=$false)] 69 | [ValidateNotNullOrEmpty()] 70 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 71 | [Parameter( 72 | Mandatory=$true, 73 | ValueFromPipeline=$true, 74 | ValueFromPipelineByPropertyName=$true)] 75 | [object[]]$DeviceThreat, 76 | [Parameter(Mandatory=$true)] 77 | [ValidateSet("Quarantine", "Waive")] 78 | [String]$Action, 79 | [Parameter(Mandatory=$true,ParameterSetName="ByDevice")] 80 | [object]$Device, 81 | [Parameter(Mandatory=$true,ParameterSetName="ByDeviceId")] 82 | [object]$DeviceId 83 | 84 | ) 85 | 86 | Begin { 87 | } 88 | 89 | Process { 90 | $hash = $DeviceThreat.sha256 91 | if ($null -eq $hash) { 92 | $hash = $DeviceThreat 93 | } 94 | 95 | $updateMap = @{ 96 | "threat_id" = $($hash) 97 | "event" = $Action 98 | } 99 | 100 | $json = $updateMap | ConvertTo-Json 101 | # remain silent 102 | switch ($PSCmdlet.ParameterSetName) { 103 | "ByDeviceId" { 104 | $output = Invoke-CyRestMethod -API $API -Method POST -Uri "$($API.BaseUrl)/devices/v2/$($DeviceId)/threats" -ContentType "application/json; charset=utf-8" -Body $json 105 | } 106 | "ByDevice" { 107 | $output = Invoke-CyRestMethod -API $API -Method POST -Uri "$($API.BaseUrl)/devices/v2/$($Device.id)/threats" -ContentType "application/json; charset=utf-8" -Body $json 108 | } 109 | } 110 | 111 | } 112 | } 113 | 114 | <# 115 | .SYNOPSIS 116 | Retrieves the given threat's Detail from the console. Gets full data, not a shallow version. 117 | 118 | .PARAMETER API 119 | Optional. API Handle (use only when not using session scope). 120 | 121 | .PARAMETER SHA256 122 | A collection of SHA256 values (as strings) to retrieve the data for, or threat objects with a "sha256" property. 123 | #> 124 | function Get-CyThreatDetail { 125 | Param ( 126 | [parameter(Mandatory=$false)] 127 | [ValidateNotNullOrEmpty()] 128 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 129 | [Parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] 130 | [object]$SHA256 131 | ) 132 | 133 | Process { 134 | if ($SHA256 -is [String]) { 135 | $Hash = $SHA256 136 | } elseif (![String]::IsNullOrEmpty($SHA256.sha256)) { 137 | $Hash = $SHA256.sha256 138 | } else { 139 | Throw "Cannot determine SHA256 value from threat object" 140 | } 141 | Invoke-CyRestMethod -API $API -Method GET -Uri "$($API.BaseUrl)/threats/v2/$($Hash)" | Convert-CyObject 142 | } 143 | } 144 | 145 | <# 146 | .SYNOPSIS 147 | Retrieves a download link for the given threat 148 | 149 | .PARAMETER API 150 | Optional. API Handle (use only when not using session scope). 151 | 152 | .PARAMETER SHA256 153 | The threat to retrieve the download link for. 154 | #> 155 | function Get-CyThreatDownloadLink { 156 | Param ( 157 | [parameter(Mandatory=$false)] 158 | [ValidateNotNullOrEmpty()] 159 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 160 | [Parameter( 161 | Mandatory=$true, 162 | ValueFromPipeline=$true, 163 | ValueFromPipelineByPropertyName=$true)] 164 | [String[]]$SHA256 165 | ) 166 | 167 | Process { 168 | Invoke-CyRestMethod -API $API -Method GET -Uri "$($API.BaseUrl)/threats/v2/download/$($SHA256)" 169 | } 170 | } 171 | 172 | 173 | <# 174 | .SYNOPSIS 175 | Gets the devices affected by a particular threat. 176 | 177 | .PARAMETER API 178 | Optional. API Handle (use only when not using session scope). 179 | 180 | .PARAMETER SHA256 181 | The threat SHA256 hash 182 | #> 183 | function Get-CyThreatDeviceList { 184 | Param ( 185 | [parameter(Mandatory=$false)] 186 | [ValidateNotNullOrEmpty()] 187 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 188 | [Parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] 189 | [object]$SHA256 190 | ) 191 | 192 | Process { 193 | if ($SHA256 -is [String]) { 194 | $Hash = $SHA256 195 | } elseif (![String]::IsNullOrEmpty($SHA256.sha256)) { 196 | $Hash = $SHA256.sha256 197 | } else { 198 | Throw "Cannot determine SHA256 value from threat object" 199 | } 200 | 201 | Read-CyData -API $API -Uri "$($API.BaseUrl)/threats/v2/$($Hash)/devices" 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /CyUsers.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Gets a list of users in the console 4 | #> 5 | function Get-CyUserList { 6 | [CmdletBinding(DefaultParameterSetName="All")] 7 | Param ( 8 | [parameter(Mandatory=$false)] 9 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle 10 | ) 11 | 12 | Read-CyData -API $API -Uri "$($API.BaseUrl)/users/v2" 13 | } 14 | 15 | <# 16 | .SYNOPSIS 17 | Retrieves the details of a user object. 18 | 19 | .PARAMETER User 20 | A user object (retrieved e.g. via Get-CyUserList) 21 | 22 | .PARAMETER UserId 23 | A user ID or email to retrieve the user detail for 24 | #> 25 | function Get-CyUserDetail { 26 | Param ( 27 | [parameter(Mandatory=$false)] 28 | [ValidateNotNullOrEmpty()] 29 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 30 | [Parameter(ParameterSetName="ByUser", Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] 31 | [object[]]$User, 32 | [Parameter(Mandatory=$true,ParameterSetName="ByUserIdOrEmail")] 33 | [object]$UserId 34 | ) 35 | 36 | Process { 37 | switch ($PSCmdlet.ParameterSetName) { 38 | "ByUserIdOrEmail" { 39 | $url = "$($API.BaseUrl)/users/v2/$($UserId)" 40 | } 41 | "ByUser" { 42 | $url = "$($API.BaseUrl)/users/v2/$($User.id)" 43 | } 44 | } 45 | 46 | # Read-CyData -API $API -Uri $url 47 | Invoke-CyRestMethod -API $API -Method GET -Uri $url | Convert-CyObject 48 | } 49 | } 50 | 51 | <# 52 | .SYNOPSIS 53 | Resolves a human-readable role identifier to the fixed 'guid' assigned to the role. 54 | #> 55 | function RoleToGuid() { 56 | Param( 57 | [Parameter(Mandatory=$true)] 58 | [string]$Role, 59 | [Parameter(Mandatory=$false)] 60 | [ValidateSet("User", "Zone")] 61 | [string]$Type = "User" 62 | 63 | ) 64 | switch ($Type) { 65 | "User" { 66 | switch ($Role) { 67 | "User" { 68 | "00000000-0000-0000-0000-000000000001" 69 | } 70 | "Administrator" { 71 | "00000000-0000-0000-0000-000000000002" 72 | } 73 | "ZoneManager" { 74 | "00000000-0000-0000-0000-000000000003" 75 | } 76 | 77 | } 78 | } 79 | "Zone" { 80 | switch ($Role) { 81 | "ZoneManager" { 82 | "00000000-0000-0000-0000-000000000001" 83 | } 84 | "User" { 85 | "00000000-0000-0000-0000-000000000002" 86 | } 87 | } 88 | } 89 | } 90 | } 91 | 92 | <# 93 | .SYNOPSIS 94 | Creates a new user account 95 | 96 | .PARAMETER UserId 97 | The user's email address 98 | 99 | .PARAMETER FirstName 100 | The user's first name 101 | 102 | .PARAMETER LastName 103 | The user's first name 104 | 105 | .PARAMETER Role 106 | The user's role 107 | 108 | .PARAMETER ZoneRights 109 | If the user's role is "zone manager", an array of hashtables with pairs of "zone_id" (zone's ID) and "role" ("ZoneManager", "User"). 110 | #> 111 | function New-CyUser { 112 | Param ( 113 | [parameter(Mandatory=$false)] 114 | [ValidateNotNullOrEmpty()] 115 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 116 | [string]$UserId, 117 | [Parameter(Mandatory=$true)] 118 | [string]$FirstName, 119 | [Parameter(Mandatory=$true)] 120 | [string]$LastName, 121 | [Parameter(Mandatory=$true)] 122 | [ValidateSet ("Administrator","ZoneManager","User")] 123 | [string]$Role, 124 | [Parameter(Mandatory=$false)] 125 | [Hashtable[]]$ZoneRights 126 | ) 127 | 128 | Process { 129 | $EffectiveRole = RoleToGuid -Role $Role 130 | 131 | $updateMap = @{ 132 | email = $UserId 133 | user_role = $EffectiveRole 134 | first_name = $FirstName 135 | last_name = $LastName 136 | } 137 | 138 | 139 | if ($EffectiveRole -eq "00000000-0000-0000-0000-000000000003") { 140 | $EffectiveZoneRights = @() 141 | foreach ($ZoneRight in $ZoneRights) { 142 | Write-Host "Zone: $($ZoneRight.zone_id) = $($ZoneRight.role)" 143 | 144 | $EffectiveZoneRights += @{ 145 | id = $ZoneRight.zone_id 146 | role_type = RoleToGuid -Role $ZoneRight.role -Type Zone 147 | } 148 | } 149 | $updateMap.zones = $EffectiveZoneRights 150 | 151 | } 152 | 153 | $json = ConvertTo-Json $updateMap 154 | Invoke-CyRestMethod -API $API -Method POST -Uri "$($API.BaseUrl)/users/v2" -Body $json -ContentType "application/json; charset=utf-8" | Convert-CyObject 155 | } 156 | } 157 | 158 | 159 | <# 160 | .SYNOPSIS 161 | Sends an invite to a user 162 | 163 | .PARAMETER UserId 164 | The user's email address 165 | #> 166 | function Invoke-CySendUserInvite { 167 | Param ( 168 | [parameter(Mandatory=$false)] 169 | [ValidateNotNullOrEmpty()] 170 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 171 | [Parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] 172 | [string]$UserId 173 | ) 174 | 175 | Process { 176 | if ($null -ne $UserId.email) { 177 | $id = $UserId.email 178 | } 179 | else 180 | { 181 | $id = $UserId 182 | } 183 | 184 | Invoke-CyRestMethod -API $API -Method POST -Uri "$($API.BaseUrl)/users/v2/$($id)/invite" -Body "" -ContentType "application/json; charset=utf-8" | Convert-CyObject 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /CyZones.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Gets a list of all zones from the console. 4 | 5 | .PARAMETER API 6 | Optional. API Handle (use only when not using session scope). 7 | 8 | .PARAMETER DeviceName 9 | Optional. Get zone list for a particular device name. 10 | 11 | .PARAMETER Device 12 | Optional. Get zone list for a particular device. 13 | #> 14 | function Get-CyZoneList { 15 | [CmdletBinding(DefaultParameterSetName="All")] 16 | Param ( 17 | [parameter(Mandatory=$false)] 18 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 19 | [parameter(Mandatory=$true,ParameterSetName="ByDeviceName")] 20 | [String]$DeviceName, 21 | [parameter(Mandatory=$true,ParameterSetName="ByDevice")] 22 | [object]$Device 23 | ) 24 | 25 | 26 | switch ($PSCmdlet.ParameterSetName) { 27 | "ByDeviceName" { 28 | $Device = Get-CyDeviceList | Where-Object name -eq $DeviceName 29 | Get-CyZoneList -API $API -Device $Device 30 | } 31 | "ByDevice" { 32 | Read-CyData -API $API -Uri "$($API.BaseUrl)/zones/v2/$($Device.id)/zones" 33 | } 34 | "All" { 35 | Read-CyData -API $API -Uri "$($API.BaseUrl)/zones/v2" 36 | } 37 | } 38 | } 39 | 40 | <# 41 | .SYNOPSIS 42 | Retrieves the given ZONE Detail from the console. Gets full data, not a shallow version. 43 | 44 | .PARAMETER API 45 | Optional. API Handle (use only when not using session scope). 46 | 47 | .PARAM Zone 48 | The zone object to fetch Detail for. 49 | 50 | .PARAM ZoneName 51 | The zone name to fetch the zone object for. 52 | #> 53 | function Get-CyZone { 54 | Param ( 55 | [parameter(Mandatory=$false)] 56 | [ValidateNotNullOrEmpty()] 57 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 58 | [Parameter( 59 | Mandatory=$true, 60 | ValueFromPipeline=$true, 61 | ValueFromPipelineByPropertyName=$true, 62 | ParameterSetName="ByZoneObject") 63 | ] 64 | [object[]]$Zone, 65 | [Parameter( 66 | Mandatory=$true, 67 | ValueFromPipeline=$true, 68 | ValueFromPipelineByPropertyName=$true, 69 | ParameterSetName="ByZoneName") 70 | ] 71 | [String[]]$Name 72 | ) 73 | 74 | Process { 75 | switch ($PSCmdlet.ParameterSetName) 76 | { 77 | "ByZoneName" { 78 | Write-Verbose "Get-CyZone: Getting zone by name '$($Name)'" 79 | Get-CyZoneList -API $API | Where-Object name -eq $Name 80 | } 81 | "ByZoneObject" { 82 | Write-Verbose "Get-CyZone: Getting zone via zone object for zone ID '$($Zone.id)'" 83 | Invoke-CyRestMethod -API $API -Method GET -Uri "$($API.BaseUrl)/zones/v2/$($Zone.id)" 84 | } 85 | } 86 | } 87 | } 88 | 89 | <# 90 | .SYNOPSIS 91 | Creates a new zone. 92 | 93 | .PARAMETER API 94 | Optional. API Handle (use only when not using session scope). 95 | 96 | .PARAMETER Name 97 | The name of the new zone 98 | 99 | .PARAMETER Policy 100 | Optional. The policy for the new zone 101 | 102 | .PARAMETER Criticality 103 | Optional. The criticality for the new zone 104 | #> 105 | function New-CyZone{ 106 | Param ( 107 | [parameter(Mandatory=$false)] 108 | [ValidateNotNullOrEmpty()] 109 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 110 | [Parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] 111 | [String[]]$Name, 112 | [Parameter(Mandatory=$false)] 113 | [object]$Policy = @{ id = $null}, 114 | [Parameter(Mandatory=$false)] 115 | [ValidateSet("Low", "Normal", "High")] 116 | [String]$Criticality = "Normal" 117 | 118 | ) 119 | Begin { 120 | } 121 | 122 | Process { 123 | $updateMap = @{ 124 | "name" = $($Name) 125 | "policy_id" = $($Policy.id) 126 | "criticality" = $Criticality 127 | } 128 | 129 | $json = $updateMap | ConvertTo-Json 130 | Invoke-CyRestMethod -API $API -Method POST -Uri "$($API.BaseUrl)/zones/v2" -ContentType "application/json; charset=utf-8" -Body $json 131 | } 132 | } 133 | 134 | <# 135 | .SYNOPSIS 136 | Removes a zone. 137 | 138 | .PARAMETER API 139 | Optional. API Handle (use only when not using session scope). 140 | 141 | .PARAMETER Name 142 | The name of a zone to remove 143 | 144 | .PARAMETER Zone 145 | The zone object to remove. 146 | #> 147 | function Remove-CyZone { 148 | [CmdletBinding(DefaultParameterSetName="All")] 149 | Param ( 150 | [parameter(Mandatory=$false)] 151 | [ValidateNotNullOrEmpty()] 152 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 153 | [Parameter(Mandatory=$true,ParameterSetName="ByZoneName",ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] 154 | [String]$Name, 155 | [Parameter(Mandatory=$false,ParameterSetName="ByZone",ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] 156 | [object]$Zone 157 | 158 | ) 159 | 160 | Begin { 161 | } 162 | 163 | Process { 164 | switch ($PSCmdlet.ParameterSetName) { 165 | "ByZoneName" { 166 | $Zone = Get-CyZone -API $API -Name $Name 167 | Remove-CyZone -API $API -Zone $Zone 168 | } 169 | "ByZone" { 170 | Invoke-CyRestMethod -API $API -Method DELETE -Uri "$($API.BaseUrl)/zones/v2/$($Zone.id)" -ContentType "application/json; charset=utf-8" 171 | } 172 | } 173 | } 174 | } 175 | 176 | <# 177 | .SYNOPSIS 178 | Adds device(s) to zone(s). 179 | 180 | .PARAMETER API 181 | Optional. API Handle (use only when not using session scope). 182 | 183 | .PARAMETER Zone 184 | The zone to add the device to. 185 | 186 | .PARAMETER Device 187 | The device(s) to add to the zone. 188 | #> 189 | function Add-CyDeviceToZone { 190 | Param ( 191 | [parameter(Mandatory=$false)] 192 | [ValidateNotNullOrEmpty()] 193 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 194 | [Parameter( 195 | Mandatory=$true, 196 | ValueFromPipeline=$true, 197 | ValueFromPipelineByPropertyName=$true)] 198 | [object[]]$Device, 199 | [Parameter(Mandatory=$true)] 200 | [object]$Zone 201 | ) 202 | 203 | Begin { 204 | $zoneIds = $Zone.id 205 | 206 | # zone IDs must be an array 207 | if ($zoneIds -isnot [array]) { 208 | $zoneIds = @($zoneIds) 209 | } 210 | } 211 | 212 | Process { 213 | if (($null -eq $Device) -or ($null -eq $Device.id) -or ([String]::IsNullOrEmpty($Device.id))) { 214 | throw "Add-CyDeviceToZone: Device ID cannot be null or empty." 215 | } 216 | 217 | if (($null -eq $Device.policy) -or ($null -eq $Device.policy.id) -or ([String]::IsNullOrEmpty($Device.policy.id))) { 218 | throw "Add-CyDeviceToZone: Device policy ID cannot be null or empty." 219 | } 220 | 221 | $updateMap = @{ 222 | "name" = $($Device.name) 223 | "policy_id" = $($Device.policy.id) 224 | "add_zone_ids" = $zoneIds 225 | } 226 | 227 | $json = $updateMap | ConvertTo-Json 228 | Write-Verbose "Update device JSON: $($json)" 229 | # remain silent 230 | $null = Invoke-CyRestMethod -API $API -Method PUT -Uri "$($API.BaseUrl)/devices/v2/$($Device.id)" -ContentType "application/json; charset=utf-8" -Body $json 231 | } 232 | } 233 | 234 | <# 235 | .SYNOPSIS 236 | Removes device(s) from zone(s). 237 | 238 | .PARAMETER API 239 | Optional. API Handle (use only when not using session scope). 240 | 241 | .PARAMETER Zone 242 | The zone to add the device to. 243 | 244 | .PARAMETER Device 245 | The device(s) to add to the zone. 246 | #> 247 | function Remove-CyDeviceFromZone { 248 | Param ( 249 | [parameter(Mandatory=$false)] 250 | [ValidateNotNullOrEmpty()] 251 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 252 | [Parameter( 253 | Mandatory=$true, 254 | ValueFromPipeline=$true, 255 | ValueFromPipelineByPropertyName=$true)] 256 | [object[]]$Device, 257 | [Parameter(Mandatory=$true)] 258 | [object]$Zone 259 | ) 260 | 261 | Begin { 262 | $zoneIds = $Zone.id 263 | 264 | # zone IDs must be an array 265 | if ($zoneIds -isnot [array]) { 266 | $zoneIds = @($zoneIds) 267 | } 268 | } 269 | 270 | Process { 271 | $updateMap = @{ 272 | "name" = $($Device.name) 273 | "policy_id" = $($Device.policy.id) 274 | "remove_zone_ids" = $zoneIds 275 | } 276 | 277 | $json = $updateMap | ConvertTo-Json 278 | # remain silent 279 | $null = Invoke-CyRestMethod -API $API -Method PUT -Uri "$($API.BaseUrl)/devices/v2/$($Device.id)" -ContentType "application/json; charset=utf-8" -Body $json 280 | } 281 | } 282 | 283 | <# 284 | .SYNOPSIS 285 | Updates a zone. 286 | 287 | .PARAMETER API 288 | Optional. API Handle (use only when not using session scope). 289 | 290 | .PARAMETER Zone 291 | The zone to modify. 292 | 293 | .PARAMETER Name 294 | Optional. Thenew name of the zone. 295 | 296 | .PARAMETER Policy 297 | Optional. The new policy to set as the policy for the zone. NOTE, this does NOT affect the policy set on any device assigned to the zone! This is by principle and you'll have to look at the Cylance docs to better understand what this means. 298 | 299 | .PARAMETER Criticality 300 | Optional. The new criticality to set for the zone. 301 | #> 302 | function Update-CyZone { 303 | Param ( 304 | [parameter(Mandatory=$false)] 305 | [ValidateNotNullOrEmpty()] 306 | [CylanceAPIHandle]$API = $GlobalCyAPIHandle, 307 | [Parameter(Mandatory=$true)] 308 | [object]$Zone, 309 | [String]$Name = $null, 310 | [Parameter(Mandatory=$false)] 311 | [object]$Policy = @{ id = $null}, 312 | [Parameter(Mandatory=$false)] 313 | [ValidateSet("Low", "Normal", "High")] 314 | [String]$Criticality 315 | ) 316 | 317 | # default to existing 318 | $updateMap = @{ 319 | name = $Zone.name 320 | policy_id = $Zone.policy_id 321 | criticality = $Zone.criticality 322 | } 323 | 324 | # update provided properties only 325 | if (![String]::IsNullOrEmpty($Name)) { 326 | $updateMap.name = $Name 327 | } 328 | if ($null -ne $Policy) { 329 | if (![String]::IsNullOrEmpty($Policy.id)) { 330 | $updateMap.policy_id = $Policy.id 331 | } else { 332 | $updateMap.policy_id = $Policy.policy_id 333 | } 334 | } 335 | if (![String]::IsNullOrEmpty($Criticality)) { 336 | $updateMap.criticality = $Criticality 337 | } 338 | 339 | $json = $updateMap | ConvertTo-Json 340 | Write-Verbose "Update zone JSON: $json" 341 | # remain silent 342 | $null = Invoke-CyRestMethod -API $API -Method PUT -Uri "$($API.BaseUrl)/zones/v2/$($Zone.id)" -ContentType "application/json; charset=utf-8" -Body $json 343 | } -------------------------------------------------------------------------------- /FAQ.md: -------------------------------------------------------------------------------- 1 | # FAQ 2 | 3 | ## How does the module make my life easier? 4 | 5 | *Instead of coding your own client application, you can focus on your business logic. You can achieve many tasks with a single line of code, more complex ones with a few lines, using this module.* 6 | 7 | Examples include: 8 | 9 | * Managing global lists (importing/exporting safe lists and manipulating quarantine lists) 10 | * Looking at threats, downloading, and manipulating threats - individually or for 1000s of entries at once 11 | * Manage devices and zones - creation, assignment of membership, removal... 12 | * Completely automating common administrative tasks, such as safelisting trusted files 13 | 14 | ## Where can I find examples? 15 | 16 | You can find the [CyCLI examples](https://github.com/jan-tee/cycli-examples) on Github. 17 | 18 | ## How do I install the module? 19 | 20 | Run `Install-Module CyCLI` from an administrative Powershell console. Once installed, close the console and create your first console entry. 21 | 22 | If you get error messages, you are most likely behind a corporate proxy and need to add proxy arguments (use `Get-Help Install-Module` to find out how), or your Powershell policy settings disallow the installation of modules or execution of unsigned module code. See below for help on the proxy issue. 23 | 24 | ## How do I use this behind a proxy server? 25 | 26 | To use behind a proxy, configure the proxy server in the session you are using the module. Use `Set-CyGlobalSettings` as the first cmdlet in any API session to configure proxy settings. If you need to specify credentials for the proxy server, set them first by running `$proxyCreds = Get-Credential` and then use them as a parameter to `Set-CyGlobalSettings`. As a reminder, to find out what the available parameters are to a cmdlet, use tab-autocompletion or `Get-Help `, in this case `Get-Help Set-CyGlobalSettings`. 27 | 28 | ## How do I configure the module? 29 | 30 | Run `Get-Help New-CyConsoleConfig` to understand the arguments, and then run `New-CyConsoleConfig` to add your first console entry. Use `us` for US consoles, `euc1` for Europe, and for any other console, use the appropriate suffix from your console login page URL. You only need to do this once per console you connect to. 31 | 32 | ## What should I know about the module? 33 | 34 | 1. This module is not a Cylance product. The Cylance product is the API itself, for which you can download documentation from the Cylance knowledgebase. 35 | 1. This module is an API client application. 36 | 1. You can write your own API client applications, independent of this module, with the documentation available in the Cylance knowledgebase. 37 | 38 | This module exists because: 39 | 40 | 1. Most system administrators are not used to writing code against REST APIs. 41 | 1. If you are looking to automate system administration tasks in PowerShell, chances are it will be _much_ easier for you to use this existing module than custom coding your own client against the REST API. 42 | 43 | ## How does the module work? 44 | 45 | * The modules writes a `consoles.json` file to your `$HOME` (profile) directory, or to `$HOME\TDRs` if that exists. 46 | * The `consoles.json` file can serve as a directory holding the various credentials and URLs needed to access the API, Threat Data Reports, etc. and stores them for each console under a name of your choosing 47 | * This allows you to configure once and later access the console by name, rather than by remembering the API ID, tenant ID, API secret, and base URL for a given console! 48 | * The `consoles.json` file contains encrypted credentials, and is not portable across users or devices. 49 | 50 | -------------------------------------------------------------------------------- /Invoke-InstallModule.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Install the module in the PowerShell module folder. 4 | .DESCRIPTION 5 | Install the module in the PowerShell module folder by copying all the files. 6 | #> 7 | 8 | [CmdLetBinding()] 9 | Param ( 10 | [ValidateNotNullOrEmpty()] 11 | [String]$ModuleName = 'CyCLI', 12 | [ValidateScript({Test-Path -Path $_ -Type Container})] 13 | [String]$ModulePath = 'C:\Program Files\WindowsPowerShell\Modules' 14 | ) 15 | 16 | Begin { 17 | Try { 18 | Write-Verbose "$ModuleName module installation started" 19 | 20 | $Files = @( 21 | 'CyCLI.psd1', 22 | 'CyCLI.psm1', 23 | 'CyHelper.ps1', 24 | 'CyTDR.ps1', 25 | 'CyAPI.ps1', 26 | 'CyCrypto.ps1', 27 | 'CyDevices.ps1', 28 | 'CyThreats.ps1', 29 | 'CyZones.ps1', 30 | 'CyPolicies.ps1', 31 | 'CyInstallers.ps1', 32 | 'CyGlobalLists.ps1', 33 | 'CyUsers.ps1', 34 | 'CyOpticsDetections.ps1', 35 | 'CyOpticsRules.ps1', 36 | 'CyOpticsPackages.ps1', 37 | 'CyOpticsInstaQuery.ps1', 38 | 'CyConvenience.ps1', 39 | 'CyDATA_ApplicationDefinitions.json' 40 | 'license.txt' 41 | ) 42 | } 43 | Catch { 44 | throw "Failed installing the module '$ModuleName': $_" 45 | } 46 | } 47 | 48 | Process { 49 | Try { 50 | $TargetPath = Join-Path -Path $ModulePath -ChildPath $ModuleName 51 | $SourcePath = $PSScriptRoot 52 | 53 | if (-not (Test-Path $TargetPath)) { 54 | New-Item -Path $TargetPath -ItemType Directory -EA Stop | Out-Null 55 | Write-Verbose "$ModuleName created module folder '$TargetPath'" 56 | } 57 | 58 | $Files | 59 | ForEach-Object { Get-ChildItem (Join-Path -Path $SourcePath -ChildPath $_) } | 60 | ForEach-Object { 61 | $Destination = Join-Path $TargetPath -ChildPath $_.Name 62 | Copy-Item -Path $_.FullName -Destination $Destination 63 | Write-Verbose "$ModuleName installed module file '$($_.Name)' to '$($Destination)'" 64 | } 65 | 66 | Write-Verbose "$ModuleName module installation successful" 67 | } 68 | Catch { 69 | throw "Failed installing the module '$ModuleName': $_" 70 | } 71 | } -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Jan P. Tietze 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANUAL-INSTALL-FROM-SOURCE.md: -------------------------------------------------------------------------------- 1 | # Manual installation 2 | 3 | You can install from source, but **only do this if you want to contribute new code to the module, and know what you are doing and why.**. 4 | 5 | **In fact, *please* never do this if you don't know *exactly* why you are doing this and that you definitely need it.** 6 | 7 | 1. Clone the repository. 8 | 1. Install the ImportExcel module: `Install-Module ImportExcel` 9 | 1. Install the module: `.\InstallModule` 10 | 1. Import the CyCLI: `Import-Module CyCLI` 11 | -------------------------------------------------------------------------------- /README-API.md: -------------------------------------------------------------------------------- 1 | # Simple API examples 2 | 3 | Query for name + offline date of *devices that have been offline for longer than 14 days*: 4 | 5 | ```powershell 6 | $TwoWeeks = (Get-Date).AddDays(-14) 7 | Get-CyDeviceList | Get-CyDeviceDetail | Where { $_.date_offline -ne $null -and $_.date_offline -lt $twoweeks } | select name, date_offline 8 | ``` 9 | 10 | Query for Windows computers that are *likely to be a member of ANY AD domain* (= where the last logged-on user is not local): 11 | 12 | ```powershell 13 | PS C:\Users\Jan Tietze\Repos\cylance-cli> Get-CyDeviceList | Get-CyDeviceDetail | Where os_version -like "*Windows*" | Where { $domain = $_.host_name[0..14] -join "" ; $_.last_logged_in_user -notlike "$($domain)\*" -and $_.last_logged_in_user -ne $null }``` 14 | 15 | Query for Windows computers that are *likely not part of your domain*: 16 | 17 | ```powershell 18 | Get-CyDeviceList | Get-CyDeviceDetail | Where os_version -like "*Windows*" | Where last_logged_in_user -notlike "YOURDOMAIN\*" 19 | ``` 20 | 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CyCLI 2 | API & CLI tools for Cylance 3 | 4 | ## FAQ 5 | 6 | You can find the [FAQ here in this repository](FAQ.md). 7 | 8 | ## Examples 9 | 10 | You can find the [CyCLI examples](https://github.com/jan-tee/cycli-examples) on Github, too. 11 | 12 | ## Prerequisites & Installation 13 | 14 | All instructions assume PowerShell 5.0 or greater. Download the latest Microsoft Management Framework if you are on an earlier version. 15 | 16 | ### Install from PowerShell Gallery 17 | 18 | 1. From an administrative PowerShell prompt, enter `Install-Module CyCLI` 19 | 1. To use, `Import-Module CyCLI` 20 | 21 | You can install from source too, but **only do this if you want to contribute new code to the module, and know what you are doing and why.**. [Instructions for manual install](MANUAL-INSTALL-FROM-SOURCE.md). 22 | 23 | If you receive this error message this: 24 | 25 | ```WARNING: The specified module 'CyCLI' with PowerShellGetFormatVersion '2.0' is not supported by the current version of PowerShellGet. Get the latest version of the PowerShellGet module to install this module, 'CyCLI'.``` 26 | 27 | Then you need to upgrade PowerShellGet to install. This is caused by a change in the minimum required PowerShellGet package version for PowerShellGallery.com. To fix it, from an administrative PowerShell prompt, enter `Update-Module PowerShellGet -force`, and after it completes successfully, restart the administrative PowerShell prompt and follow the instructions above again. 28 | 29 | ## See all verbs 30 | 31 | ```powershell 32 | get-help *-cy* 33 | ``` 34 | 35 | ## Getting started 36 | 37 | ### API credentials: Persistent Storage 38 | 39 | The module uses a `consoles.json` file that can reside in your user profile path (`$HOME`) or a special subdirectory (`$HOME\TDRs\`). The module will *automatically* create the file in your user profile path if none exists when you add your first console entry, or use an existing file in either path (with precedence for `$HOME\TDRs`). 40 | 41 | It will also automatically create the `consoles.json` file for you when you run any ```New-CyConsoleConfig``` commands. 42 | 43 | ### Import the module 44 | 45 | *All examples assume you have imported the module using `Import-Module CyCLI` first.* 46 | 47 | ### Proxy support 48 | 49 | If you need to use a proxy, run ```Set-CyGlobalSettings``` as the first cmdlet in any API session to configure proxy settings. 50 | 51 | ### Create your first API connection 52 | 53 | To get started, run ```New-CyConsoleConfig``` and answer all prompts. Run ```get-help New-CyConsoleConfig``` to look up the possible values for the `Region` argument. 54 | 55 | **Note:** *If you choose to supply parameters rather than answering prompts, please note that the API secret cannot be given as a literal string command line argument because it is processed as a secure string (and stored using DPAPI).* 56 | 57 | The `Console` argument throughout the module is a string that you can use to reference a set of credentials, so you do not have to remember/reference it yourself. An added advantage is that credentials are stored protected by DPAPI and you do not need to worry about accidentally sharing them when sharing your scripts. 58 | 59 | ## Example use of Powershell cmdlets for the console API 60 | 61 | To obtain API authorization valid for 30 minutes if you have configured your `Consoles.json` file: 62 | 63 | ```powershell 64 | Get-CyAPI -Console 65 | ``` 66 | 67 | If you did not configure `Consoles.json`, you can provide the secrets directly: 68 | 69 | ```powershell 70 | Get-CyAPI -APIId $APIId -APISecret $APIsecret -APITenantId $TenantId 71 | ``` 72 | 73 | To obtain collections of all devices, zones, and policies: 74 | 75 | ```powershell 76 | Get-CyDeviceList 77 | Get-CyDeviceList | Get-CyDeviceDetail 78 | Get-CyZoneList 79 | ``` 80 | 81 | To obtain the *detailed information* for one particular device: 82 | 83 | ```powershell 84 | $devices = Get-CyDeviceList 85 | Get-CyDeviceDetail -Device $devices[0] 86 | ``` 87 | 88 | To add all devices that have names like `JTIETZE-*` to a new zone `TESTOMAT` with policy `Default`: 89 | 90 | ```powershell 91 | Create-CyZone -Name "TESTOMAT" -Policy 92 | $d = Get-CyDeviceList | Where name -like "*JTIETZE-*" 93 | $z = Create-CyZone -Name "TESTOMAT" -Criticality Low 94 | $d | Add-CyDeviceToZone -Zone $z 95 | ``` 96 | 97 | To obtain the details of all threats in the environment, you can either enumerate all threats for each device: 98 | 99 | ```powershell 100 | $threats = Get-CyDeviceList | Get-CyDeviceThreatList 101 | $threatDetails = $threats.sha256 | Get-CyThreatDetails 102 | ``` 103 | 104 | Or get the whole list of threats: 105 | ```powershell 106 | $threats = Get-CyThreatList 107 | ``` 108 | 109 | (and then, if you need instance details, use `Get-CyThreatDeviceList`) 110 | 111 | # TODO 112 | - Automatic substitution of illegal characters in e.g. zone names to prevent API errors 113 | --------------------------------------------------------------------------------