Skip to main content
COSMICBYTEZLABS
NewsSecurityHOWTOsToolsStudyTraining
ProjectsChecklistsAI RankingsNewsletterStatusTagsAbout
Subscribe

Press Enter to search or Esc to close

News
Security
HOWTOs
Tools
Study
Training
Projects
Checklists
AI Rankings
Newsletter
Status
Tags
About
RSS Feed
Reading List
Subscribe

Stay in the Loop

Get the latest security alerts, tutorials, and tech insights delivered to your inbox.

Subscribe NowFree forever. No spam.
COSMICBYTEZLABS

Your trusted source for IT intelligence, cybersecurity insights, and hands-on technical guides.

429+ Articles
114+ Guides

CONTENT

  • Latest News
  • Security Alerts
  • HOWTOs
  • Projects
  • Exam Prep

RESOURCES

  • Search
  • Browse Tags
  • Newsletter Archive
  • Reading List
  • RSS Feed

COMPANY

  • About Us
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CosmicBytez Labs. All rights reserved.

System Status: Operational
  1. Home
  2. HOWTOs
  3. Invoke SentinelOne Threat Hunt
Invoke SentinelOne Threat Hunt
HOWTOAdvanced

Invoke SentinelOne Threat Hunt

Proactive threat hunting is essential for identifying sophisticated threats that evade automated detection systems. This script automates the process of...

Dylan H.

Security Operations

February 11, 2026
20 min read

SCENARIO

Proactive threat hunting is essential for identifying sophisticated threats that evade automated detection systems. This script automates the process of querying SentinelOne's Deep Visibility telemetry to search for Indicators of Compromise (IOCs), suspicious behavior patterns, and malicious activity across your endpoint estate.

Use this procedure when:

  • Investigating specific IOCs from threat intelligence feeds (file hashes, IP addresses, domains)
  • Hunting for known attack patterns (credential dumping, lateral movement, persistence mechanisms)
  • Responding to security alerts that require historical endpoint analysis
  • Conducting proactive threat hunts based on MITRE ATT&CK techniques
  • Performing incident response forensics across multiple endpoints
  • Validating detection coverage for specific threat actors or malware families

The script leverages SentinelOne's Deep Visibility SQL-like query language to search endpoint telemetry (process execution, network connections, file operations, registry modifications, DNS queries) and categorizes findings by threat severity. It generates comprehensive reports in JSON and CSV formats, with optional automated response actions (network isolation, process termination, file quarantine).

REQUIREMENTS & ASSUMPTIONS

Software Requirements:

  • PowerShell 5.1 or higher (PowerShell 7+ recommended)
  • Active SentinelOne Singularity platform deployment
  • SentinelOne API token with DeepVisibility.Read permission (minimum)
  • SentinelOne API token with RemoteOps permission (if using -AutoResponse for mitigation actions)
  • Internet connectivity to SentinelOne management console

Access Requirements:

  • SentinelOne console access to generate API tokens
  • User account with appropriate role (Analyst, SOC, Admin)
  • Network path to SentinelOne management console URL (typically https://[tenant].sentinelone.net)

System Requirements:

  • C:\BIN directory for log storage (created automatically if missing)
  • C:\BIN\S1-ThreatHunt directory for report exports (created automatically by default)
  • Sufficient disk space for query results (large hunts may generate multi-MB JSON/CSV files)

Assumptions:

  • SentinelOne agents are actively reporting telemetry to the management console
  • Deep Visibility feature is enabled on your SentinelOne license
  • Endpoint agents have Deep Visibility data retention configured (typically 14-90 days)
  • You have identified specific IOCs or threat patterns to hunt for
  • Time range for queries is within the data retention window

Security Considerations:

  • API tokens are sensitive credentials with broad endpoint access - store securely
  • The script prompts for API token via SecureString if not provided as parameter
  • API tokens are cleared from memory after script execution ($token = $null + garbage collection)
  • Automated response actions (-AutoResponse) require explicit confirmation before execution
  • Network isolation and process termination are disruptive - verify affected endpoints before confirming
  • All query activity and response actions are logged to C:\BIN\LOGS-\<date\>-S1-ThreatHunt.log

PROCESS

  1. Obtain SentinelOne API Token:

    • Log in to your SentinelOne management console
    • Navigate to Settings > Users > [Your User] > API Token
    • Click Generate API Token and copy the token securely
    • Ensure the token has DeepVisibility.Read permission (check role assignments)
    • If using automated response features, verify RemoteOps permission is also assigned
  2. Identify Your Hunt Objective:

    • Determine the IOC or behavior pattern you need to search for
    • Choose the appropriate QueryType parameter:
      • FileHash: Search for known malicious file hashes (SHA1 or SHA256)
      • IPAddress: Find network connections to suspicious IP addresses
      • Domain: Search DNS queries for malicious domains
      • ProcessPattern: Hunt for suspicious process names or command-line patterns
      • RegistryMod: Detect registry modifications (persistence mechanisms)
      • NetworkConnection: Find suspicious network connections on uncommon ports
      • CustomQuery: Execute advanced custom Deep Visibility SQL queries
  3. Execute the Threat Hunt:

    Example: Search for Known Malicious File Hash

    .\Invoke-SentinelOne-ThreatHunt.ps1 `
        -ManagementUrl "https://company.sentinelone.net" `
        -QueryType FileHash `
        -SearchValue "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" `
        -TimeRange 168

    Example: Hunt for Suspicious PowerShell Activity (Last 24 Hours)

    .\Invoke-SentinelOne-ThreatHunt.ps1 `
        -ManagementUrl "https://company.sentinelone.net" `
        -QueryType ProcessPattern `
        -SearchValue "powershell.exe" `
        -TimeRange 24

    Example: Search for Network Connections to Malicious IP

    .\Invoke-SentinelOne-ThreatHunt.ps1 `
        -ManagementUrl "https://company.sentinelone.net" `
        -QueryType IPAddress `
        -SearchValue "192.0.2.100" `
        -TimeRange 72

    Example: Custom Query for Credential Access (Mimikatz Detection)

    .\Invoke-SentinelOne-ThreatHunt.ps1 `
        -ManagementUrl "https://company.sentinelone.net" `
        -QueryType CustomQuery `
        -CustomQuery "SELECT AgentName, SrcProcName, SrcProcCmdLine, TgtProcName, EventTime FROM events WHERE (SrcProcCmdLine CONTAINS 'lsass' OR SrcProcCmdLine CONTAINS 'sekurlsa') ORDER BY EventTime DESC LIMIT 1000" `
        -TimeRange 48

    Example: Hunt with Automated Response Capability

    .\Invoke-SentinelOne-ThreatHunt.ps1 `
        -ManagementUrl "https://company.sentinelone.net" `
        -QueryType Domain `
        -SearchValue "evil-c2-domain.com" `
        -TimeRange 24 `
        -AutoResponse
  4. Review the Threat Hunt Report:

    The script generates a console report showing:

    • Total events found matching the query
    • Severity distribution (CRITICAL, HIGH, MEDIUM, LOW)
    • Top findings with agent names, process names, and command lines
    • Export file locations (JSON and CSV)

    Severity Categorization:

    • CRITICAL: Known attack tools detected (mimikatz, psexec, bloodhound, cobalt strike, meterpreter, ransomware indicators)
    • HIGH: Suspicious PowerShell encodings, credential dumping patterns, privilege escalation commands
    • MEDIUM: Elevated integrity processes, SentinelOne threat indicators, suspicious network ports
    • LOW: General telemetry events matching query criteria
  5. Analyze Exported Results:

    Navigate to the export directory (default: C:\BIN\S1-ThreatHunt\):

    cd C:\BIN\S1-ThreatHunt

    JSON Report: Contains full structured data including severity analysis and categorized findings

    Get-Content .\S1-ThreatHunt-\<timestamp\>.json | ConvertFrom-Json | Format-List

    CSV Report: Flattened event data for analysis in Excel or SIEM tools

    Import-Csv .\S1-ThreatHunt-\<timestamp\>.csv | Out-GridView
  6. Execute Response Actions (If Using -AutoResponse):

    If the script detects CRITICAL or HIGH severity findings and -AutoResponse is enabled:

    • Review the list of affected endpoints displayed in the console
    • Choose from available response actions:
      • Option 1: Network isolate affected endpoints (disconnects from network, maintains S1 cloud connectivity)
      • Option 2: Kill suspicious processes on affected endpoints
      • Option 3: Quarantine malicious files detected by SentinelOne
      • Option 4: No automated action (manual investigation)
    • Type yes to confirm destructive actions (network isolation, process termination)

    Important: Response actions are currently logged but require manual implementation via SentinelOne API. The script displays the appropriate API endpoints to use.

  7. Verify Query Results in SentinelOne Console:

    Cross-reference script findings with the SentinelOne console:

    • Navigate to Visibility > Deep Visibility > Query in the SentinelOne console
    • Paste the query displayed in the script log file
    • Verify results match the script output
    • Use the console for additional pivot queries and timeline analysis
  8. Review Logs for Audit Trail:

    All threat hunt activity is logged to:

    Get-Content "C:\BIN\LOGS-$(Get-Date -Format 'yyyy-MM-dd')-S1-ThreatHunt.log"

    Log entries include:

    • API connectivity tests
    • Deep Visibility query text and query IDs
    • Event counts and severity distribution
    • Export file paths
    • Response action requests and confirmations
    • Error messages and stack traces (if failures occur)

Script detail

Core Functionality:

The script implements a complete threat hunting workflow using the SentinelOne REST API:

  1. Authentication: Securely obtains API token via SecureString parameter or interactive prompt, converts to plaintext for API headers, and ensures cleanup after execution.

  2. Deep Visibility Query Construction: The Get-DeepVisibilityQuery function generates SQL-like queries based on QueryType. Each query type selects relevant telemetry fields:

    • FileHash: Searches SrcProcSha1, SrcProcSha256, TgtFileSha1, TgtFileSha256 columns
    • IPAddress: Searches DstIp and SrcIp for network connections
    • Domain: Searches DnsRequest for DNS query activity
    • ProcessPattern: Searches SrcProcName and SrcProcCmdLine for process execution patterns
    • RegistryMod: Filters for Registry Value Create and Registry Value Modified events
    • NetworkConnection: Finds suspicious ports (4444, 5555, 8080, 8443, 1337) or unknown port names
  3. Query Execution: The Invoke-DeepVisibilityQuery function:

    • Converts PowerShell $TimeRange parameter to ISO 8601 UTC timestamps for API
    • Initiates query via POST /web/api/v2.1/dv/init-query endpoint
    • Polls query status every 2 seconds (max 30 attempts = 60 seconds timeout)
    • Retrieves paginated results using nextCursor pagination until all events are collected
    • Returns array of event objects with full telemetry fields
  4. Threat Severity Analysis: The Get-ThreatSeverity function categorizes each event:

    • CRITICAL indicators: Hardcoded list of known attack tool names (mimikatz, psexec, bloodhound, cobalt, meterpreter, empire, covenant, ransomware, lazagne)
    • HIGH indicators: Suspicious command patterns (encoded PowerShell, download cradles, privilege escalation, shadow copy deletion)
    • MEDIUM indicators: Elevated process integrity levels, SentinelOne threat indicators, uncommon network ports
    • LOW indicators: All other events matching query criteria
  5. Report Generation: The New-ThreatHuntReport function:

    • Adds ThreatSeverity property to each event object
    • Groups findings by severity level
    • Generates structured hashtable report with summary statistics
    • Outputs color-coded console report with top findings preview
    • Logs severity distribution to audit log
  6. Export Capabilities: The Export-ThreatHuntResults function:

    • Creates timestamped JSON file with complete report structure (nested findings by severity)
    • Creates timestamped CSV file with flattened event data for spreadsheet analysis
    • Logs export file paths for easy reference
  7. Automated Response Actions: The Invoke-ThreatResponse function (when -AutoResponse is enabled):

    • Filters for CRITICAL and HIGH severity events only
    • Extracts unique affected agent names
    • Presents interactive menu for response actions
    • Requires explicit yes confirmation for destructive actions
    • Note: Current implementation logs response requests but does not execute API calls (requires agent ID resolution which needs additional API queries)

API Integration Details:

The script uses SentinelOne API v2.1 with these endpoints:

  • Authentication: Authorization: ApiToken <token> header for all requests
  • Account Verification: GET /web/api/v2.1/accounts (connectivity test)
  • Query Initialization: POST /web/api/v2.1/dv/init-query (returns queryId)
  • Query Status: GET /web/api/v2.1/dv/query-status?queryId={id} (polls for FINISHED state)
  • Query Results: GET /web/api/v2.1/dv/events?queryId={id}&cursor={cursor} (paginated results)
  • Response Actions (logged but not executed):
    • Network Isolation: POST /web/api/v2.1/agents/actions/disconnect
    • Process Termination: POST /web/api/v2.1/agents/actions/kill-process
    • File Quarantine: POST /web/api/v2.1/threats/actions/mitigate

Error Handling:

  • All API requests wrapped in try-catch blocks with detailed logging
  • Query timeout after 60 seconds (30 attempts × 2 second intervals)
  • Parameter validation ensures required values are provided for each QueryType
  • API token cleared from memory in finally block with garbage collection
  • Stack traces logged for debugging failed queries

Query Result Limits:

Each Deep Visibility query is limited to 1000 events via the LIMIT clause. For hunts that may exceed this limit:

  • Consider narrowing the time range (-TimeRange parameter)
  • Use more specific search values
  • Break into multiple queries targeting specific timeframes
  • Use custom queries with additional WHERE clause filters

Practical Use Cases (Included in Script Comments):

The script includes comprehensive query templates for common threat hunting scenarios:

  1. Credential Access Detection: LSASS process access, Mimikatz indicators
  2. Lateral Movement Detection: PSExec, WMI, Remote Services, SMB connections
  3. Persistence Mechanism Detection: Registry Run keys, Scheduled Tasks
  4. Command and Control Detection: Encoded PowerShell, suspicious network patterns
  5. Data Exfiltration Detection: Archiving tools, large uploads, FTP/SSH connections
  6. Ransomware Behavior Detection: Shadow copy deletion, mass file encryption
  7. Living Off The Land Binaries (LOLBins): Certutil, Bitsadmin, Regsvr32, Mshta
  8. Suspicious Parent-Child Processes: Office apps spawning PowerShell, web servers spawning command shells

Script contents

<#
.SYNOPSIS
    Automates SentinelOne Deep Visibility threat hunting queries via API with optional response actions.
 
.DESCRIPTION
    Connects to SentinelOne management console API to execute Deep Visibility queries for threat hunting.
    Supports IOC searches (hash, IP, domain), process patterns, registry modifications, and network connections.
    Generates categorized reports and optionally executes response actions (isolate, kill process, quarantine).
    Logs all operations to C:\BIN\LOGS-\<date\>-S1-ThreatHunt.log.
 
.PARAMETER ManagementUrl
    SentinelOne console URL (e.g., https://yourinstance.sentinelone.net)
 
.PARAMETER ApiToken
    API token with Deep Visibility and response permissions (if not provided, prompts securely)
 
.PARAMETER QueryType
    Type of hunt: FileHash, IPAddress, Domain, ProcessPattern, RegistryMod, NetworkConnection, CustomQuery
 
.PARAMETER SearchValue
    Search term (hash, IP, domain, process name, registry key, etc.)
 
.PARAMETER CustomQuery
    Full Deep Visibility SQL query (used when QueryType is CustomQuery)
 
.PARAMETER TimeRange
    Hours to search back (default: 24)
 
.PARAMETER AutoResponse
    Enable automated response actions with confirmation prompts
 
.PARAMETER ExportPath
    Directory for CSV/JSON exports (default: C:\BIN\S1-ThreatHunt)
 
.EXAMPLE
    .\Invoke-SentinelOne-ThreatHunt.ps1 -ManagementUrl "https://company.sentinelone.net" -QueryType FileHash -SearchValue "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
 
.EXAMPLE
    .\Invoke-SentinelOne-ThreatHunt.ps1 -ManagementUrl "https://company.sentinelone.net" -QueryType ProcessPattern -SearchValue "powershell.exe" -TimeRange 168 -AutoResponse
 
.EXAMPLE
    .\Invoke-SentinelOne-ThreatHunt.ps1 -ManagementUrl "https://company.sentinelone.net" -QueryType CustomQuery -CustomQuery "SELECT * FROM events WHERE processName = 'cmd.exe' AND cmdLine CONTAINS 'mimikatz'"
 
.NOTES
    Version: 1.0
    Author: CosmicBytez IT Automation
    Requires: SentinelOne API token with DeepVisibility.Read and optional RemoteOps permissions
    Dependencies: PowerShell 5.1+, Internet connectivity to SentinelOne console
#>
 
[CmdletBinding()]
param(
    [Parameter(Mandatory = $true)]
    [ValidatePattern('^https://.*\.sentinelone\.(net|com)$')]
    [string]$ManagementUrl,
 
    [Parameter(Mandatory = $false)]
    [SecureString]$ApiToken,
 
    [Parameter(Mandatory = $true)]
    [ValidateSet('FileHash', 'IPAddress', 'Domain', 'ProcessPattern', 'RegistryMod', 'NetworkConnection', 'CustomQuery')]
    [string]$QueryType,
 
    [Parameter(Mandatory = $false)]
    [string]$SearchValue,
 
    [Parameter(Mandatory = $false)]
    [string]$CustomQuery,
 
    [Parameter(Mandatory = $false)]
    [ValidateRange(1, 8760)]
    [int]$TimeRange = 24,
 
    [Parameter(Mandatory = $false)]
    [switch]$AutoResponse,
 
    [Parameter(Mandatory = $false)]
    [string]$ExportPath = "C:\BIN\S1-ThreatHunt"
)
 
$ErrorActionPreference = 'Stop'
 
# Initialize logging
$timestamp = Get-Date -Format "yyyy-MM-dd"
$logFile = "C:\BIN\LOGS-$timestamp-S1-ThreatHunt.log"
 
# Ensure log directory exists
if (-not (Test-Path "C:\BIN")) {
    New-Item -Path "C:\BIN" -ItemType Directory -Force | Out-Null
}
 
function Write-Log {
    param(
        [Parameter(Mandatory = $true)]
        [string]$Message,
 
        [Parameter(Mandatory = $false)]
        [ValidateSet('INFO', 'WARNING', 'ERROR', 'SUCCESS')]
        [string]$Level = 'INFO'
    )
 
    $logTimestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    $logEntry = "[$logTimestamp] [$Level] $Message"
    Add-Content -Path $logFile -Value $logEntry
 
    switch ($Level) {
        'ERROR'   { Write-Host $logEntry -ForegroundColor Red }
        'WARNING' { Write-Host $logEntry -ForegroundColor Yellow }
        'SUCCESS' { Write-Host $logEntry -ForegroundColor Green }
        default   { Write-Host $logEntry -ForegroundColor White }
    }
}
 
function Get-S1ApiToken {
    if ($null -eq $ApiToken) {
        Write-Host "`nSentinelOne API Token required (with DeepVisibility.Read permission)" -ForegroundColor Cyan
        Write-Host "Obtain from: $ManagementUrl/settings/users > API Token" -ForegroundColor Gray
        $ApiToken = Read-Host "Enter API Token" -AsSecureString
    }
 
    $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($ApiToken)
    $plainToken = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
    [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR)
 
    return $plainToken
}
 
function Invoke-S1ApiRequest {
    param(
        [Parameter(Mandatory = $true)]
        [string]$Endpoint,
 
        [Parameter(Mandatory = $false)]
        [string]$Method = 'GET',
 
        [Parameter(Mandatory = $false)]
        [hashtable]$Body,
 
        [Parameter(Mandatory = $true)]
        [string]$Token
    )
 
    $headers = @{
        'Authorization' = "ApiToken $Token"
        'Content-Type'  = 'application/json'
    }
 
    $uri = "$ManagementUrl/web/api/v2.1/$Endpoint"
 
    try {
        $params = @{
            Uri     = $uri
            Method  = $Method
            Headers = $headers
        }
 
        if ($Body) {
            $params.Body = ($Body | ConvertTo-Json -Depth 10 -Compress)
        }
 
        Write-Log "API Request: $Method $Endpoint" -Level INFO
        $response = Invoke-RestMethod @params
        return $response
    }
    catch {
        Write-Log "API Request Failed: $_" -Level ERROR
        throw
    }
}
 
function Get-DeepVisibilityQuery {
    param(
        [Parameter(Mandatory = $true)]
        [string]$Type,
 
        [Parameter(Mandatory = $false)]
        [string]$Value,
 
        [Parameter(Mandatory = $false)]
        [string]$Custom
    )
 
    # Deep Visibility SQL-like query templates
    switch ($Type) {
        'FileHash' {
            return @"
SELECT
    AgentName,
    SrcProcUser,
    SrcProcCmdLine,
    SrcProcName,
    SrcProcSha1,
    SrcProcSha256,
    TgtFilePath,
    TgtFileSha1,
    TgtFileSha256,
    EventTime
FROM events
WHERE (SrcProcSha1 = '$Value' OR SrcProcSha256 = '$Value' OR TgtFileSha1 = '$Value' OR TgtFileSha256 = '$Value')
ORDER BY EventTime DESC
LIMIT 1000
"@
        }
 
        'IPAddress' {
            return @"
SELECT
    AgentName,
    SrcProcName,
    SrcProcCmdLine,
    DstIp,
    DstPort,
    SrcIp,
    IndicatorName,
    EventType,
    EventTime
FROM events
WHERE (DstIp = '$Value' OR SrcIp = '$Value')
ORDER BY EventTime DESC
LIMIT 1000
"@
        }
 
        'Domain' {
            return @"
SELECT
    AgentName,
    SrcProcName,
    SrcProcCmdLine,
    DnsRequest,
    DnsResponse,
    DstIp,
    EventTime
FROM events
WHERE DnsRequest CONTAINS '$Value'
ORDER BY EventTime DESC
LIMIT 1000
"@
        }
 
        'ProcessPattern' {
            return @"
SELECT
    AgentName,
    SrcProcUser,
    SrcProcName,
    SrcProcCmdLine,
    SrcProcSha256,
    SrcProcParentName,
    SrcProcIntegrityLevel,
    EventTime
FROM events
WHERE (SrcProcName CONTAINS '$Value' OR SrcProcCmdLine CONTAINS '$Value')
ORDER BY EventTime DESC
LIMIT 1000
"@
        }
 
        'RegistryMod' {
            return @"
SELECT
    AgentName,
    SrcProcName,
    SrcProcCmdLine,
    RegistryKeyPath,
    RegistryOldValue,
    RegistryValue,
    EventType,
    EventTime
FROM events
WHERE (EventType = 'Registry Value Create' OR EventType = 'Registry Value Modified')
  AND RegistryKeyPath CONTAINS '$Value'
ORDER BY EventTime DESC
LIMIT 1000
"@
        }
 
        'NetworkConnection' {
            return @"
SELECT
    AgentName,
    SrcProcName,
    SrcProcCmdLine,
    DstIp,
    DstPort,
    DstPortName,
    IndicatorName,
    EventType,
    EventTime
FROM events
WHERE EventType IN ('IP Connect', 'IP Listen')
  AND (DstPort IN (4444, 5555, 8080, 8443, 1337) OR DstPortName = 'unknown')
ORDER BY EventTime DESC
LIMIT 1000
"@
        }
 
        'CustomQuery' {
            return $Custom
        }
    }
}
 
function Invoke-DeepVisibilityQuery {
    param(
        [Parameter(Mandatory = $true)]
        [string]$Query,
 
        [Parameter(Mandatory = $true)]
        [int]$Hours,
 
        [Parameter(Mandatory = $true)]
        [string]$Token
    )
 
    # Calculate time range
    $toDate = Get-Date
    $fromDate = $toDate.AddHours(-$Hours)
 
    $body = @{
        query    = $Query
        fromDate = $fromDate.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ")
        toDate   = $toDate.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ")
        limit    = 1000
    }
 
    Write-Log "Executing Deep Visibility Query (last $Hours hours)" -Level INFO
    Write-Log "Query: $($Query -replace "`n",' ')" -Level INFO
 
    # Create query
    $queryResponse = Invoke-S1ApiRequest -Endpoint "dv/init-query" -Method POST -Body $body -Token $Token
 
    if (-not $queryResponse.data.queryId) {
        Write-Log "Failed to initialize query" -Level ERROR
        return $null
    }
 
    $queryId = $queryResponse.data.queryId
    Write-Log "Query ID: $queryId" -Level INFO
 
    # Poll for query completion
    $maxAttempts = 30
    $attempt = 0
    $queryStatus = $null
 
    while ($attempt -lt $maxAttempts) {
        Start-Sleep -Seconds 2
        $attempt++
 
        $statusResponse = Invoke-S1ApiRequest -Endpoint "dv/query-status?queryId=$queryId" -Token $Token
        $queryStatus = $statusResponse.data.responseState
 
        Write-Verbose "Query status: $queryStatus (attempt $attempt/$maxAttempts)"
 
        if ($queryStatus -eq 'FINISHED') {
            break
        }
        elseif ($queryStatus -eq 'FAILED') {
            Write-Log "Query execution failed" -Level ERROR
            return $null
        }
    }
 
    if ($queryStatus -ne 'FINISHED') {
        Write-Log "Query timeout after $maxAttempts attempts" -Level ERROR
        return $null
    }
 
    # Retrieve results
    $results = @()
    $cursor = $null
 
    do {
        $resultsEndpoint = "dv/events?queryId=$queryId"
        if ($cursor) {
            $resultsEndpoint += "&cursor=$cursor"
        }
 
        $resultsResponse = Invoke-S1ApiRequest -Endpoint $resultsEndpoint -Token $Token
 
        if ($resultsResponse.data) {
            $results += $resultsResponse.data
        }
 
        $cursor = $resultsResponse.pagination.nextCursor
    } while ($cursor)
 
    Write-Log "Retrieved $($results.Count) events" -Level SUCCESS
    return $results
}
 
function Get-ThreatSeverity {
    param(
        [Parameter(Mandatory = $true)]
        [object]$Event
    )
 
    $criticalIndicators = @(
        'mimikatz', 'psexec', 'bloodhound', 'sharphound', 'rubeus', 'cobalt',
        'meterpreter', 'empire', 'covenant', 'ransomware', 'lazagne'
    )
 
    $highIndicators = @(
        'powershell -enc', '-nop -w hidden', 'invoke-expression', 'downloadstring',
        'iex(', 'invoke-mimikatz', 'net user', 'net localgroup administrators',
        'wmic', 'reg add', 'schtasks', 'vssadmin delete shadows'
    )
 
    $eventText = ($Event | ConvertTo-Json -Compress).ToLower()
 
    # Critical severity checks
    foreach ($indicator in $criticalIndicators) {
        if ($eventText -match $indicator) {
            return 'CRITICAL'
        }
    }
 
    # High severity checks
    foreach ($indicator in $highIndicators) {
        if ($eventText -match [regex]::Escape($indicator)) {
            return 'HIGH'
        }
    }
 
    # Medium severity for suspicious patterns
    if ($Event.SrcProcIntegrityLevel -eq 'high' -or
        $Event.IndicatorName -or
        $Event.DstPort -in @(4444, 5555, 1337, 31337)) {
        return 'MEDIUM'
    }
 
    return 'LOW'
}
 
function New-ThreatHuntReport {
    param(
        [Parameter(Mandatory = $true)]
        [array]$Events,
 
        [Parameter(Mandatory = $true)]
        [string]$QueryType,
 
        [Parameter(Mandatory = $false)]
        [string]$SearchTerm
    )
 
    Write-Log "Analyzing $($Events.Count) events for threats..." -Level INFO
 
    # Categorize by severity
    $findings = $Events | ForEach-Object {
        $severity = Get-ThreatSeverity -Event $_
        $_ | Add-Member -NotePropertyName 'ThreatSeverity' -NotePropertyValue $severity -Force -PassThru
    }
 
    $critical = @($findings | Where-Object { $_.ThreatSeverity -eq 'CRITICAL' })
    $high = @($findings | Where-Object { $_.ThreatSeverity -eq 'HIGH' })
    $medium = @($findings | Where-Object { $_.ThreatSeverity -eq 'MEDIUM' })
    $low = @($findings | Where-Object { $_.ThreatSeverity -eq 'LOW' })
 
    # Generate report
    $report = @{
        GeneratedAt    = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
        QueryType      = $QueryType
        SearchTerm     = $SearchTerm
        TimeRangeHours = $TimeRange
        TotalEvents    = $Events.Count
        Severity       = @{
            Critical = $critical.Count
            High     = $high.Count
            Medium   = $medium.Count
            Low      = $low.Count
        }
        CriticalFindings = $critical
        HighFindings     = $high
        MediumFindings   = $medium
        LowFindings      = $low
    }
 
    # Console output
    Write-Host "`n========================================" -ForegroundColor Cyan
    Write-Host "  SentinelOne Threat Hunt Report" -ForegroundColor Cyan
    Write-Host "========================================" -ForegroundColor Cyan
    Write-Host "Generated: $($report.GeneratedAt)" -ForegroundColor White
    Write-Host "Query Type: $QueryType" -ForegroundColor White
    if ($SearchTerm) {
        Write-Host "Search Term: $SearchTerm" -ForegroundColor White
    }
    Write-Host "Time Range: Last $TimeRange hours" -ForegroundColor White
    Write-Host "`nTotal Events: $($report.TotalEvents)" -ForegroundColor White
    Write-Host "`nSeverity Distribution:" -ForegroundColor Yellow
    Write-Host "  CRITICAL: $($critical.Count)" -ForegroundColor Red
    Write-Host "  HIGH:     $($high.Count)" -ForegroundColor DarkRed
    Write-Host "  MEDIUM:   $($medium.Count)" -ForegroundColor DarkYellow
    Write-Host "  LOW:      $($low.Count)" -ForegroundColor Gray
 
    if ($critical.Count -gt 0) {
        Write-Host "`nCRITICAL Findings:" -ForegroundColor Red
        $critical | Select-Object -First 5 | ForEach-Object {
            Write-Host "  - Agent: $($_.AgentName) | Process: $($_.SrcProcName) | Cmd: $($_.SrcProcCmdLine)" -ForegroundColor Red
        }
        if ($critical.Count -gt 5) {
            Write-Host "  ... and $($critical.Count - 5) more" -ForegroundColor Red
        }
    }
 
    if ($high.Count -gt 0) {
        Write-Host "`nHIGH Findings:" -ForegroundColor DarkRed
        $high | Select-Object -First 3 | ForEach-Object {
            Write-Host "  - Agent: $($_.AgentName) | Process: $($_.SrcProcName) | Cmd: $($_.SrcProcCmdLine)" -ForegroundColor DarkRed
        }
        if ($high.Count -gt 3) {
            Write-Host "  ... and $($high.Count - 3) more" -ForegroundColor DarkRed
        }
    }
 
    Write-Host "`n========================================`n" -ForegroundColor Cyan
 
    Write-Log "Threat analysis complete: $($critical.Count) CRITICAL, $($high.Count) HIGH, $($medium.Count) MEDIUM, $($low.Count) LOW" -Level SUCCESS
 
    return $report
}
 
function Export-ThreatHuntResults {
    param(
        [Parameter(Mandatory = $true)]
        [hashtable]$Report,
 
        [Parameter(Mandatory = $true)]
        [string]$ExportDirectory
    )
 
    # Ensure export directory exists
    if (-not (Test-Path $ExportDirectory)) {
        New-Item -Path $ExportDirectory -ItemType Directory -Force | Out-Null
        Write-Log "Created export directory: $ExportDirectory" -Level INFO
    }
 
    $timestamp = Get-Date -Format "yyyyMMdd-HHmmss"
    $baseFilename = "$ExportDirectory\S1-ThreatHunt-$timestamp"
 
    # Export JSON (full report)
    $jsonPath = "$baseFilename.json"
    $Report | ConvertTo-Json -Depth 10 | Out-File -FilePath $jsonPath -Encoding UTF8
    Write-Log "Exported JSON report: $jsonPath" -Level SUCCESS
 
    # Export CSV (flattened findings)
    $csvPath = "$baseFilename.csv"
    $allFindings = @()
    $allFindings += $Report.CriticalFindings
    $allFindings += $Report.HighFindings
    $allFindings += $Report.MediumFindings
    $allFindings += $Report.LowFindings
 
    if ($allFindings.Count -gt 0) {
        $allFindings | Export-Csv -Path $csvPath -NoTypeInformation -Encoding UTF8
        Write-Log "Exported CSV findings: $csvPath" -Level SUCCESS
    }
 
    return @{
        JsonPath = $jsonPath
        CsvPath  = $csvPath
    }
}
 
function Invoke-ThreatResponse {
    param(
        [Parameter(Mandatory = $true)]
        [hashtable]$Report,
 
        [Parameter(Mandatory = $true)]
        [string]$Token
    )
 
    if (-not $AutoResponse) {
        Write-Host "`nAutomated response actions disabled. Use -AutoResponse to enable." -ForegroundColor Yellow
        return
    }
 
    $criticalEvents = $Report.CriticalFindings + $Report.HighFindings
 
    if ($criticalEvents.Count -eq 0) {
        Write-Host "`nNo CRITICAL or HIGH severity findings requiring response." -ForegroundColor Green
        return
    }
 
    Write-Host "`n========================================" -ForegroundColor Red
    Write-Host "  Automated Response Actions" -ForegroundColor Red
    Write-Host "========================================" -ForegroundColor Red
    Write-Host "Found $($criticalEvents.Count) CRITICAL/HIGH severity events`n" -ForegroundColor Yellow
 
    # Get unique agents from critical events
    $affectedAgents = $criticalEvents | Select-Object -ExpandProperty AgentName -Unique
 
    Write-Host "Affected Endpoints:" -ForegroundColor Yellow
    $affectedAgents | ForEach-Object { Write-Host "  - $_" -ForegroundColor White }
 
    Write-Host "`nAvailable Response Actions:" -ForegroundColor Cyan
    Write-Host "  1. Isolate affected endpoints from network" -ForegroundColor White
    Write-Host "  2. Kill suspicious processes" -ForegroundColor White
    Write-Host "  3. Quarantine malicious files" -ForegroundColor White
    Write-Host "  4. No action (manual investigation)" -ForegroundColor White
 
    $choice = Read-Host "`nSelect action (1-4)"
 
    switch ($choice) {
        '1' {
            $confirm = Read-Host "Confirm network isolation of $($affectedAgents.Count) endpoints? (yes/no)"
            if ($confirm -eq 'yes') {
                Write-Log "Network isolation requested for $($affectedAgents.Count) endpoints" -Level WARNING
                # Note: Actual implementation requires agent IDs and disconnect endpoint API
                Write-Host "Network isolation would be executed here (requires agent ID resolution)" -ForegroundColor Yellow
                Write-Host "API Endpoint: POST /web/api/v2.1/agents/actions/disconnect" -ForegroundColor Gray
            }
            else {
                Write-Log "Network isolation cancelled by user" -Level INFO
            }
        }
 
        '2' {
            $confirm = Read-Host "Confirm process termination on affected endpoints? (yes/no)"
            if ($confirm -eq 'yes') {
                Write-Log "Process termination requested" -Level WARNING
                Write-Host "Process termination would be executed here (requires agent ID and process ID)" -ForegroundColor Yellow
                Write-Host "API Endpoint: POST /web/api/v2.1/agents/actions/kill-process" -ForegroundColor Gray
            }
            else {
                Write-Log "Process termination cancelled by user" -Level INFO
            }
        }
 
        '3' {
            $confirm = Read-Host "Confirm file quarantine on affected endpoints? (yes/no)"
            if ($confirm -eq 'yes') {
                Write-Log "File quarantine requested" -Level WARNING
                Write-Host "File quarantine would be executed here (requires threat ID)" -ForegroundColor Yellow
                Write-Host "API Endpoint: POST /web/api/v2.1/threats/actions/mitigate" -ForegroundColor Gray
            }
            else {
                Write-Log "File quarantine cancelled by user" -Level INFO
            }
        }
 
        '4' {
            Write-Log "No automated action taken - manual investigation recommended" -Level INFO
        }
 
        default {
            Write-Host "Invalid selection. No action taken." -ForegroundColor Red
        }
    }
}
 
# ============================================================================
# MAIN EXECUTION
# ============================================================================
 
Write-Log "=== SentinelOne Threat Hunt Started ===" -Level INFO
Write-Log "Management Console: $ManagementUrl" -Level INFO
Write-Log "Query Type: $QueryType" -Level INFO
 
try {
    # Get API token
    $token = Get-S1ApiToken
 
    # Test API connectivity
    Write-Log "Testing API connectivity..." -Level INFO
    $accountInfo = Invoke-S1ApiRequest -Endpoint "accounts" -Token $token
    Write-Log "Connected to SentinelOne console successfully" -Level SUCCESS
 
    # Validate parameters
    if ($QueryType -ne 'CustomQuery' -and [string]::IsNullOrEmpty($SearchValue)) {
        throw "SearchValue parameter required for QueryType: $QueryType"
    }
 
    if ($QueryType -eq 'CustomQuery' -and [string]::IsNullOrEmpty($CustomQuery)) {
        throw "CustomQuery parameter required when QueryType is CustomQuery"
    }
 
    # Build Deep Visibility query
    $dvQuery = Get-DeepVisibilityQuery -Type $QueryType -Value $SearchValue -Custom $CustomQuery
 
    # Execute query
    $queryResults = Invoke-DeepVisibilityQuery -Query $dvQuery -Hours $TimeRange -Token $token
 
    if ($null -eq $queryResults -or $queryResults.Count -eq 0) {
        Write-Log "No events found matching query criteria" -Level WARNING
        Write-Host "`nNo results found. Consider expanding time range or adjusting search criteria." -ForegroundColor Yellow
        exit 0
    }
 
    # Generate threat hunt report
    $report = New-ThreatHuntReport -Events $queryResults -QueryType $QueryType -SearchTerm $SearchValue
 
    # Export results
    $exportedFiles = Export-ThreatHuntResults -Report $report -ExportDirectory $ExportPath
 
    Write-Host "Results exported to:" -ForegroundColor Cyan
    Write-Host "  JSON: $($exportedFiles.JsonPath)" -ForegroundColor White
    Write-Host "  CSV:  $($exportedFiles.CsvPath)" -ForegroundColor White
 
    # Automated response actions
    Invoke-ThreatResponse -Report $report -Token $token
 
    Write-Log "=== Threat Hunt Completed Successfully ===" -Level SUCCESS
}
catch {
    Write-Log "Threat hunt failed: $_" -Level ERROR
    Write-Log "Stack Trace: $($_.ScriptStackTrace)" -Level ERROR
    throw
}
finally {
    # Secure cleanup
    if ($token) {
        $token = $null
        [System.GC]::Collect()
    }
}
 
<#
EXAMPLE DEEP VISIBILITY QUERIES FOR REFERENCE:
 
1. Credential Access Detection (Mimikatz, LSASS dumping):
SELECT AgentName, SrcProcName, SrcProcCmdLine, TgtProcName, EventTime
FROM events
WHERE (SrcProcCmdLine CONTAINS 'lsass' OR SrcProcCmdLine CONTAINS 'sekurlsa'
       OR TgtProcName = 'lsass.exe')
  AND EventType IN ('Process Creation', 'Open Remote Process')
ORDER BY EventTime DESC
 
2. Lateral Movement Detection (PSExec, WMI, Remote Services):
SELECT AgentName, SrcProcName, SrcProcCmdLine, DstIp, DstPort, EventTime
FROM events
WHERE (SrcProcName IN ('psexec.exe', 'wmic.exe', 'mmc.exe', 'wmiprvse.exe')
       OR DstPort IN (445, 135, 5985, 5986))
  AND EventType IN ('IP Connect', 'Process Creation')
ORDER BY EventTime DESC
 
3. Persistence Mechanism Detection (Registry Run Keys, Scheduled Tasks):
SELECT AgentName, SrcProcName, RegistryKeyPath, RegistryValue, EventTime
FROM events
WHERE EventType IN ('Registry Value Create', 'Registry Value Modified')
  AND (RegistryKeyPath CONTAINS 'CurrentVersion\Run'
       OR RegistryKeyPath CONTAINS 'CurrentVersion\Policies\Explorer\Run'
       OR RegistryKeyPath CONTAINS 'CurrentVersion\Windows\Load')
ORDER BY EventTime DESC
 
4. Command and Control Detection (Encoded Commands, Suspicious Network):
SELECT AgentName, SrcProcName, SrcProcCmdLine, DstIp, DnsRequest, EventTime
FROM events
WHERE (SrcProcCmdLine CONTAINS '-enc' OR SrcProcCmdLine CONTAINS '-e '
       OR SrcProcCmdLine CONTAINS 'FromBase64String'
       OR SrcProcCmdLine CONTAINS 'DownloadString')
  AND SrcProcName IN ('powershell.exe', 'cmd.exe', 'wscript.exe', 'cscript.exe')
ORDER BY EventTime DESC
 
5. Data Exfiltration Detection (Large Uploads, Archiving):
SELECT AgentName, SrcProcName, SrcProcCmdLine, TgtFilePath, EventTime
FROM events
WHERE (SrcProcName IN ('7z.exe', 'winrar.exe', 'tar.exe')
       OR SrcProcCmdLine CONTAINS 'compress-archive'
       OR (EventType = 'IP Connect' AND DstPort IN (21, 22, 443, 8443)))
ORDER BY EventTime DESC
 
6. Ransomware Behavior Detection (Shadow Copy Deletion, Mass File Encryption):
SELECT AgentName, SrcProcName, SrcProcCmdLine, TgtFilePath, EventTime
FROM events
WHERE (SrcProcCmdLine CONTAINS 'vssadmin delete shadows'
       OR SrcProcCmdLine CONTAINS 'wbadmin delete catalog'
       OR SrcProcCmdLine CONTAINS 'bcdedit /set {default} recoveryenabled no'
       OR TgtFilePath CONTAINS '.encrypted' OR TgtFilePath CONTAINS '.locked')
ORDER BY EventTime DESC
 
7. Living Off The Land Binaries (LOLBins):
SELECT AgentName, SrcProcName, SrcProcCmdLine, SrcProcParentName, EventTime
FROM events
WHERE SrcProcName IN ('certutil.exe', 'bitsadmin.exe', 'regsvr32.exe', 'mshta.exe',
                      'rundll32.exe', 'regasm.exe', 'installutil.exe', 'msbuild.exe')
  AND EventType = 'Process Creation'
ORDER BY EventTime DESC
 
8. Suspicious Parent-Child Process Relationships:
SELECT AgentName, SrcProcParentName, SrcProcName, SrcProcCmdLine, EventTime
FROM events
WHERE (SrcProcParentName IN ('winword.exe', 'excel.exe', 'powerpnt.exe', 'outlook.exe')
       AND SrcProcName IN ('powershell.exe', 'cmd.exe', 'wscript.exe', 'cscript.exe'))
  OR (SrcProcParentName IN ('w3wp.exe', 'httpd.exe', 'nginx.exe')
      AND SrcProcName IN ('cmd.exe', 'powershell.exe', 'net.exe'))
ORDER BY EventTime DESC
 
API ENDPOINTS REFERENCE:
- Deep Visibility Query Init: POST /web/api/v2.1/dv/init-query
- Query Status: GET /web/api/v2.1/dv/query-status?queryId={id}
- Query Results: GET /web/api/v2.1/dv/events?queryId={id}
- Network Isolation: POST /web/api/v2.1/agents/actions/disconnect
- Process Kill: POST /web/api/v2.1/agents/actions/kill-process
- Threat Mitigation: POST /web/api/v2.1/threats/actions/mitigate
- Agent Details: GET /web/api/v2.1/agents?computerName={name}
 
SENTINELCTL CLI ALTERNATIVE (Windows Agent):
sentinelctl.exe deepvisibility query --query "SELECT * FROM events WHERE..." --from "2025-11-05T00:00:00Z" --to "2025-11-06T00:00:00Z"
sentinelctl.exe network disconnect
sentinelctl.exe process kill --pid {pid}
#>

Related Reading

  • SentinelOne Control vs Complete Feature Comparison
  • SentinelOne Deep Visibility Threat Hunting
  • SentinelOne File Fetch and Forensic File Collection
#sentinelone#edr#Security#threat-hunting#deployment#automation#forensics#api#incident-response#mitre-attack#detection-rules

Related Articles

SentinelOne Control vs Complete Feature Comparison

This document provides a comprehensive comparison between SentinelOne Singularity Control and Singularity Complete SKUs to help MSP teams understand the...

17 min read

SentinelOne Deep Visibility Threat Hunting

Deep Visibility is SentinelOne's EDR telemetry engine that provides comprehensive endpoint data collection for threat hunting, incident investigation, and...

22 min read

SentinelOne File Fetch and Forensic File Collection

During threat investigations, security analysts need to retrieve suspicious files from endpoints for deeper forensic analysis. Traditional methods...

8 min read
Back to all HOWTOs