This PowerShell script reboots an endpoint whose hostname has been provided.
To use it you should substitute your own OAuth2 Client Credentials at the bottom of the file as well as provide the hostname of the endpoint you want to reboot.
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.
Issue-Reboot
Replace the credentials and hostname with your own credentials and the hostname of the endpoint you want to reboot.
#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 Invoke-NebulaJob {
<#
.SYNOPSIS
Issue a job to an endpoint in Nebula.
.DESCRIPTION
Issues a job to an endpoint of which the machine_id is supplied.
.PARAMETER Nebula.Token
Can be provided as a parameter or in a pipeline; see .EXAMPLE.
.PARAMETER MachineID
machine_id of the endpoint to issue the job on.
.PARAMETER DumpResponse
OPTIONAL - Print the entire formatted response to screen.
.OUTPUTS
Outputs endpoint or endpoints.
.NOTES
The code provided issues a reboot, but you can perform other tasks by replacing the command. Some examples:
command.threat.scan - Issue a scan on the target endpoint (machine)
command.machine.update.now - Check for software updates
See API docs for all commands: https://api.malwarebytes.com/nebula/v1/docs
.EXAMPLE
$NebulaToken = Get-NebulaToken -AccountID 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa' -ClientID 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa' -ClientSecret '000000ABCDEF000000ABCDEF000000AB000000ABCDEF000000ABCDEF000000AB'
Invoke-NebulaJob -NebulaToken $NebulaToken -MachineID 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa'
.EXAMPLE
Get-NebulaToken -AccountID $accountID -ClientID $clientID -ClientSecret $clientSecret | Invoke-NebulaJob -MachineID 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa'
#>
[CmdletBinding()]
param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[ValidateNotNullOrEmpty()]
[PSTypeName('Nebula.Token')] $NebulaToken,
[Parameter(Mandatory = $true)]
[ValidatePattern('^[{(]?([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})[)}]?$')]
[string] $MachineID,
[Parameter()]
[switch] $DumpResponse
)
Begin {
Write-Verbose 'Initializing Invoke-NebulaJob'
$body = @{
machine_ids = @($($MachineID))
command = 'command.asset.reboot'
}
}
Process {
Try {
$bodyJson = $body | ConvertTo-Json
$URI = 'https://api.malwarebytes.com/nebula/v1/jobs'
$bodyJson = $body | ConvertTo-Json
$request = Invoke-RestMethod -Method POST -Uri $URI -Body $bodyJson -Headers $NebulaToken.Headers
}
Catch {
Write-Error -Message "Error issuing job from Nebula: $_"
}
}
End {
if ($DumpResponse) {
Write-Host "Dumping Job Response"
# Convert to JSON for each item to ensure consistent detailed output
$request | ForEach-Object { $_ | ConvertTo-Json -Depth 5 | Out-Host }
}
return $request
}
}
# Get an authorization token
$auth_token = Get-NebulaToken -AccountID '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
# Reboot the endpoint hostname 'MyHostName'
Invoke-NebulaJob -NebulaToken $auth_token -MachineID $machine_id