This is an example of using the composable functions from Nebula API examples using PowerShell. To use the API, a client credential pair and Account ID are required.
Generate client credentials
To generate the credentials, log into Nebula and go to Integrate.
For more information on APIs, see Nebula API Documentation or hover over the tooltip next to OAuth2 Clients on the Integrate page and click the link.
EndpointEvents.ps1
Use this script to retrieve the last 7 days' worth of events from a specific endpoint.
Note: At the bottom of the file, enter your: accountid, client_id, client_secret, and the hostname of the endpoint you'd like to see events for.
#Requires -Version 3.0
# Set the security protocol to TLS 1.2
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
function Get-NebulaToken {
<#
.SYNOPSIS
Authenticate to a Nebula account, returning an OAuth2 token.
.DESCRIPTION
Authenticates via the Nebula Public API using an OAuth2 Client Pair.
Client credentials should be stored/retrieved from secure store as they provide access to your Nebula account.
Returns an authentication object containing OAuth2 Bearer Token and metadata with expiry and BaseUrl.
.PARAMETER AccountId
The Nebula AccountID e.g. aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa
This can be found between the first // in the console's URL.
E.g. https://cloud.malwarebytes.com/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa/dashboard/securityAdvisor
Required to supply this value to others API endpoints that consume Nebula.Token objects.
.PARAMETER ClientID
ClientID e.g. aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa
.PARAMETER ClientSecret
ClientSecret e.g. 000000ABCDEF000000ABCDEF000000AB000000ABCDEF000000ABCDEF000000AB
.PARAMETER Scope
OPTIONAL - Specify the read, write, and/or execute scope of the token. Defaults to read write execute.
.PARAMETER Verbose
OPTIONAL - Indicates that additional details should be displayed.
This parameter uses the built-in -Verbose feature to show internal processing steps.
Example: Get-NebulaToken -AccountID $accountID -ClientID $clientID -ClientSecret $clientSecret -Verbose
.OUTPUTS
Outputs a Nebula.Token PSCustomObject that can be used as a parameter or in a pipeline for other Nebula API endpoints.
.NOTES
Header returned contains:
'Authorization' = $TokenRequest.access_token
'AccountID' = $AccountID
The ClientID & ClientSecrect should be stoted/retreived from a secure credential store as it provides access to your Nebula account.
.EXAMPLE
$accountID = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa'
$clientID = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa'
$clientSecret = '000000ABCDEF000000ABCDEF000000AB000000ABCDEF000000ABCDEF000000AB'
$NebulaToken = Get-NebulaToken -AccountID $accountID -ClientID $clientID -ClientSecret $clientSecret
.EXAMPLE
$NebulaToken = Get-NebulaToken -AccountID $accountID -ClientID $clientID -ClientSecret $clientSecret -Verbose
.EXAMPLE
Get-NebulaToken -AccountID 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa' -ClientID 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa' -ClientSecret '000000ABCDEF000000ABCDEF000000AB000000ABCDEF000000ABCDEF000000AB'
#>
Param (
[Parameter(Mandatory=$true)][ValidateNotNullOrEmpty()]
[ValidatePattern('^[{(]?([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})[)}]?$')]
[string] $AccountID,
[Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()]
[ValidatePattern('^[{(]?([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})[)}]?$')]
[string] $ClientID,
[Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()]
[ValidatePattern('^[a-f0-9]{64}$')]
[string] $ClientSecret,
[Parameter(Mandatory=$false)][ValidateNotNullOrEmpty()][ValidateSet('read','write','execute','read write','read execute','write execute','read write execute')]
[string] $Scope = 'read write execute',
[ValidateNotNullOrEmpty()][Parameter(Mandatory=$false)]
[string] $BaseUrl = 'https://api.malwarebytes.com'
)
Write-Verbose 'Executing function Get-NebulaToken'
Try {
$URI = $BaseUrl + '/oauth2/token'
Write-Verbose $URI
$credential_pair = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(($ClientID, $ClientSecret -join ':')))
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$headers = @{
'Authorization' = ('Basic', $credential_pair -join ' ');
'Content-Type' = 'application/x-www-form-urlencoded'
}
$headers | Select-Object -Property * | Out-String | Write-Verbose
$body = @{
scope="$Scope"
grant_type='client_credentials'
}
$body | Format-List | Out-String | Write-Verbose
$TokenRequest = Invoke-RestMethod -Method POST -Uri $URI -Body $body -Headers $headers
Write-Verbose -Message 'Creating NebulaToken object'
$NebulaToken = [PSCustomObject]@{
'PSTypeName' = 'Nebula.Token'
'accountID' = $AccountID
'access_token' = $TokenRequest.access_token
'expires_in' = $TokenRequest.expires_in
'expires_at' = (Get-Date).AddSeconds($TokenRequest.expires_in)
'headers' = @{
'Authorization' = 'Bearer ' + $TokenRequest.access_token
'accountid' = $AccountID
'Content-Type' = 'application/json'
}
'scope' = $TokenRequest.scope
'token_type' = $TokenRequest.token_type
'baseurl' = $BaseUrl
}
$NebulaToken | Out-String | Write-Verbose
}
Catch {
Write-Host "The following error occured while obtaining the token from Nebula: $_"
$ErrorRecord = $_
$Result = $ErrorRecord.ErrorDetails.Message | ConvertFrom-Json
Write-Host $Result
Return $ErrorRecord
}
Return $NebulaToken
}
function Get-NebulaEndpoint {
<#
.SYNOPSIS
Receive Nebula Endpoints.
.DESCRIPTION
Returns either all endpoints or a specific endpoint if it's id is provided.
.PARAMETER Nebula.Token
Can be provided as a parameter or in a pipeline; see .EXAMPLE.
.PARAMETER HostName
OPTIONAL - Search for endpoint by HostName.
.PARAMETER HostName
OPTIONAL - Search for endpoint by Alias.
.PARAMETER DumpResponse
OPTIONAL - Print the entire formatted response to screen.
.OUTPUTS
Outputs endpoint or endpoints.
.EXAMPLE
$NebulaToken = Get-NebulaToken -AccountID 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa' -ClientID 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa' -ClientSecret '000000ABCDEF000000ABCDEF000000AB000000ABCDEF000000ABCDEF000000AB'
Get-NebulaEndpoint -NebulaToken $NebulaToken
.EXAMPLE
Get-NebulaToken -AccountID $accountID -ClientID $clientID -ClientSecret $clientSecret | Get-NebulaEndpoint -HostName 'PC1234'
.EXAMPLE
Get-NebulaToken -AccountID $accountID -ClientID $clientID -ClientSecret $clientSecret | Get-NebulaEndpoint
#>
[CmdletBinding()]
param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[ValidateNotNullOrEmpty()]
[PSTypeName('Nebula.Token')] $NebulaToken,
[Parameter(Mandatory = $false)]
[string] $HostName,
[Parameter(Mandatory = $false)]
[string] $Alias,
[Parameter()]
[switch] $DumpResponse
)
Begin {
Write-Verbose 'Initializing Get-NebulaEndpoint'
# Check if both $HostName and $Alias are provided
if ($HostName -and $Alias) {
throw "You cannot both -HostName and Alias. Please provide only one."
}
# Initialize endpoints as an empty array for consistent output
$endpoints = @()
$body = @{
next_cursor = ''
page_size = 2000
}
}
Process {
if (!($HostName -or $Alias)) {
Try {
$bodyJson = $body | ConvertTo-Json
$URI = 'https://api.malwarebytes.com/nebula/v1/endpoints'
$next_cursor = $body.next_cursor
do {
$body.next_cursor = $next_cursor
$bodyJson = $body | ConvertTo-Json
$request = Invoke-RestMethod -Method POST -Uri $URI -Body $bodyJson -Headers $NebulaToken.Headers
$endpoints += $request.endpoints
$next_cursor = $request.next_cursor
} while ($next_cursor -ne '')
}
Catch {
Write-Error -Message "Error obtaining Endpoints from Nebula: $_"
}
} elseif ($HostName) {
Try {
$body = @{"host_name.keyword" = $HostName} | ConvertTo-Json
$URI = "https://api.malwarebytes.com/nebula/v1/endpoints"
$request = Invoke-RestMethod -Method POST -Uri $URI -Body $body -Headers $NebulaToken.Headers
$endpoints = $request
}
Catch {
Write-Error -Message "Error obtaining Endpoint ID $EndpointID from Nebula: $_"
}
} elseif ($Alias) {
Try {
$body = @{ "alias" = $Alias } | ConvertTo-Json
$URI = "https://api.malwarebytes.com/nebula/v1/endpoints"
$request = Invoke-RestMethod -Method POST -Uri $URI -Body $body -Headers $NebulaToken.Headers
$endpoints = $request
}
Catch {
Write-Error -Message "Error obtaining Alias $Alias from Nebula: $_"
}
}
}
End {
if ($DumpResponse) {
Write-Host "Dumping Endpoints"
# Convert to JSON for each item to ensure consistent detailed output
$endpoints | ForEach-Object { $_ | ConvertTo-Json -Depth 5 | Out-Host }
}
return $endpoints
}
}
function Get-NebulaEvent {
<#
.SYNOPSIS
Receive Nebula Events.
.DESCRIPTION
Returns either all events within a time frame or events from a specific endpoint if its machine_id is provided.
.PARAMETER Nebula.Token
Can be provided as a parameter or in a pipeline; see .EXAMPLE.
.PARAMETER machine_id
OPTIONAL - Search for events for endpoint with machine_id.
.PARAMETER OneWeek, OneMonth, ThreeMonths, OneYear
OPTIONAL - Time frame of events.
.PARAMETER DumpResponse
OPTIONAL - Print the entire formatted response to screen.
.OUTPUTS
Outputs events within the time frame or events for a specified endpoint.
.EXAMPLE
$NebulaToken = Get-NebulaToken -AccountID 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa' -ClientID 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa' -ClientSecret '000000ABCDEF000000ABCDEF000000AB000000ABCDEF000000ABCDEF000000AB'
Get-NebulaEvent -NebulaToken $NebulaToken
.EXAMPLE
Get-NebulaToken -AccountID $accountID -ClientID $clientID -ClientSecret $clientSecret | Get-NebulaEvent -machine_id 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa'
.EXAMPLE
Get-NebulaToken -AccountID $accountID -ClientID $clientID -ClientSecret $clientSecret | Get-NebulaEvent -OneMonth # Get all events from the last month
#>
[CmdletBinding()]
param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[ValidateNotNullOrEmpty()]
[PSTypeName('Nebula.Token')] $NebulaToken,
[Parameter(Mandatory = $false)]
[string]$machine_id,
[switch]$OneWeek,
[switch]$OneMonth,
[switch]$ThreeMonths,
[switch]$OneYear,
[Parameter()]
[switch] $DumpResponse
)
Begin {
Write-Verbose 'Initializing Get-NebulaEvent'
# Initialize exclusions as an empty array for consistent output
$events = @()
$next_cursor = ''
$page_size = 2000 # Set the desired page size
$useStartDate = $false
}
Process {
# Collect all switches in an array
$selectedSwitches = @($OneWeek, $OneMonth, $ThreeMonths, $OneYear)
# Check how many switches are set to true
$countSelected = ($selectedSwitches | Where-Object { $_ }).Count
# Validate: Only allow 0 or 1 switch
if ($countSelected -gt 1) {
throw "You can specify only one time range option: -OneWeek, -OneMonth, -ThreeMonths, or -OneYear."
}
switch ($countSelected) {
1 {
$useStartDate = $true
# Determine which switch was selected
if ($OneWeek) { $offset = -7 }
if ($OneMonth) { $offset = -30 }
if ($ThreeMonths) { $offset = -90 }
if ($OneYear) { $offset = -365 }
}
0 {
$offset = -7 # Default start date of a week ago
}
}
$dateStr = (Get-Date).AddDays($offset).ToString("yyyy-MM-ddTHH:mm:ssZ")
Try {
$URI = 'https://api.malwarebytes.com/nebula/v1/events'
# Start forming the URI with page_size and 'start'
if ($useStartDate) {
$URI += "?start=$dateStr&page_size=$page_size"
} else {
$URI += "?page_size=$page_size"
}
# Add machine_id if it is provided
if ($machine_id) {
$URI += "&machine_id=$machine_id"
}
do {
# Build the URI correctly with next_cursor if available
if ($next_cursor) {
$uriWithCursor = "$URI&next_cursor=$next_cursor"
} else {
$uriWithCursor = $URI
}
$request = Invoke-RestMethod -Method GET -Uri $uriWithCursor -Headers $NebulaToken.Headers
$events += $request.events
$next_cursor = $request.next_cursor
} while ($next_cursor -ne '')
} Catch {
Write-Error -Message "Error obtaining Events from Nebula: $_"
}
}
End {
if ($DumpResponse) {
Write-Host "Dumping Events"
# Convert to JSON for each item to ensure consistent detailed output
$events | ForEach-Object { $_ | ConvertTo-Json -Depth 5 | Out-Host }
}
return $events
}
}
# Get an authorization token
$auth_token = $NebulaToken = Get-NebulaToken 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa' -ClientID 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa' -ClientSecret '000000ABCDEF000000ABCDEF000000AB000000ABCDEF000000ABCDEF000000AB'
# Get information about an endpoint with hostname 'MyHostName'
$get_endpoint_response = Get-NebulaEndpoint -NebulaToken $auth_token -HostName 'MyHostName'
# Extract the machine_id from the response above; i.e. the machine_id of the endpoint named 'MyHostName'
$machine_id = $get_endpoint_response.endpoints[0].machine.id
# Get events for the last 7 days (default) for the endpoint with hostname 'MyHostName'
Get-NebulaEvent -NebulaToken $auth_token -machine_id $machine_id