Query Details

Multi Stage Browser Exploitation Post Compromise Detection

Query

# *Multi-Stage Browser Exploitation & Post-Compromise Detection*

## Query Information

#### MITRE ATT&CK Technique(s)

| Technique ID | Title    | Link    |
| ---  | --- | --- |
| T1055 | Process Injection | https://attack.mitre.org/techniques/T1055 |
| T1203 | Exploit for Client Execution | https://attack.mitre.org/techniques/T1203/ |


#### Description

This query identifies high-confidence browser-based attacks by correlating three distinct suspicious behaviors: Remote Code Injection (memory writes), Anomalous Process Spawning (LOLBins and shell commands), and the loading of Unsigned Modules from sensitive user directories.

To minimize false positives, it utilizes a robust verification logic that cross-references known benign injectors and system paths. The results are aggregated per process session and categorized into a severity-based scoring system, allowing security analysts to prioritize incidents where multiple attack vectors converge on a single browser instance.

#### Author <Optional>
- **Name: Benjamin Zulliger**
- **Github: https://github.com/benscha/KQLAdvancedHunting**
- **LinkedIn: https://www.linkedin.com/in/benjamin-zulliger/**

#### References
- 


## Defender XDR
```KQL
// =============================================================================
// Browser Attack Detection – Optimized
// Detects: Remote Injection, Shell-Spawning, Unsigned Module Loads in Browsers
// =============================================================================
// ── Configuration ─────────────────────────────────────────────────────────────
let LookbackPeriod = 24h;
let Browsers = dynamic([
    "msedge.exe",
    "chrome.exe",
    "brave.exe",
    "firefox.exe"
]);
// High-risk LOLBins → flag directly
// cmd.exe is handled separately with a CommandLine pattern check
let HighRiskBinaries = dynamic([
    "powershell.exe",
    "pwsh.exe",
    "wscript.exe",
    "cscript.exe",
    "mshta.exe",
    "bitsadmin.exe",
    "rundll32.exe",
    "regsvr32.exe",
    "certutil.exe"
]);
// Suspicious cmd.exe patterns: only flag if cmd.exe itself 
let SuspiciousCmdPatterns = dynamic([
    "powershell",
    "encoded",
    "bypass",
    "hidden",
    "invoke",
    "iex",
    "downloadstring",
    "downloadfile",
    "webclient",
    "curl ",
    "wget ",
    "certutil",
    "bitsadmin",
    "regsvr32",
    "mshta",
    "wscript",
    "cscript",
    ".ps1",
    ".vbs",
    ".hta",
    ".bat",
    "/c reg add",
    "/c sc ",
    "/c net ",
    "base64"
]);
// Known legitimate injectors (process names)
// Checked in combination with KnownBenignPaths (Name + Path)
let KnownBenignInjectors = dynamic([
    "msedge.exe",
    "chrome.exe",
    "firefox.exe",
    "brave.exe",
    "svchost.exe",
    "memcompression.exe",
    "msmpeng.exe",
    "senseir.exe",
    "csfalconservice.exe"
]);
// Unified path exclusion list – used in BOTH subqueries:
//   BrowserInjection       → checked against InitiatingProcessFolderPath
//   BrowserSpawnedHighRisk → checked against ProcessCommandLine (SpawnedCmdLine)
let KnownBenignPaths = dynamic([
    @"C:\Windows\System32",
    @"C:\Windows\SysWOW64",
    @"C:\Program Files\Windows Defender",
    @"C:\Program Files\Microsoft Security Client",
    @"system32\spool\DRIVERS",       // Printer drivers (e.g., rundll32 + HP-DLL)
    @"Windows Security\BrowserCore", // Edge BrowserCore (legitimate SSO broker)
    @"Windows\BrowserCore",
    @"Acrobat DC\Acrobat\Browser"    
]);
// ── Remote Thread Injection / Memory Write in Browser ─────────────
let BrowserInjection =
    DeviceEvents
    | where Timestamp > ago(LookbackPeriod)
    | where ActionType in ("CreateRemoteThreadApiCall", "WriteProcessMemoryApiCall")
    | extend TargetProcessName = tostring(parse_json(AdditionalFields).TargetProcessName)
    | where TargetProcessName in~ (Browsers)
    | mv-apply _path = KnownBenignPaths to typeof(string) on (
        summarize _benignPathCount = countif(InitiatingProcessFolderPath contains _path)
    )
    // Exclusion: known injector name AND from a known path
    | where not (
        InitiatingProcessFileName in~ (KnownBenignInjectors)
        and _benignPathCount > 0
    )
    | project
        Timestamp,
        DeviceName,
        InitiatingProcessId,
        InitiatingProcessFileName,
        InitiatingProcessFolderPath,
        InitiatingProcessCommandLine,
        InitiatingProcessAccountName,
        TargetProcessName,
        SpawnedCmdLine   = "",
        DetailFile       = FileName,
        DetailPath       = FolderPath,
        ActionType,
        Activity         = "Browser Injection";
// ── Browser spawns high-risk LOLBins ─────────────────────────
let BrowserSpawnedHighRisk =
    DeviceProcessEvents
    | where Timestamp > ago(LookbackPeriod)
    | where InitiatingProcessFileName in~ (Browsers)
    | where FileName in~ (HighRiskBinaries)
    | mv-apply _path = KnownBenignPaths to typeof(string) on (
        summarize _benignPathCount = countif(ProcessCommandLine contains _path)
    )
    | where _benignPathCount == 0
    | project
        Timestamp,
        DeviceName,
        InitiatingProcessId,
        InitiatingProcessFileName,
        InitiatingProcessFolderPath,
        InitiatingProcessCommandLine,
        InitiatingProcessAccountName,
        TargetProcessName    = FileName,
        SpawnedCmdLine       = ProcessCommandLine,
        DetailFile           = FileName,
        DetailPath           = FolderPath,
        ActionType,
        Activity             = "Browser Spawned LOLBin/Shell";
// ── Browser spawns cmd.exe – only with suspicious arguments ───────
// Browser → cmd.exe is normal OS behavior (protocol handlers, opening links).
// Only flag if the cmd.exe CommandLine itself contains something suspicious.
let BrowserSpawnedSuspiciousCmd =
    DeviceProcessEvents
    | where Timestamp > ago(LookbackPeriod)
    | where InitiatingProcessFileName in~ (Browsers)
    | where FileName =~ "cmd.exe"
    | where isnotempty(ProcessCommandLine)
    | where ProcessCommandLine has_any (SuspiciousCmdPatterns)
    | mv-apply _path = KnownBenignPaths to typeof(string) on (
        summarize _benignPathCount = countif(ProcessCommandLine contains _path)
    )
    | where _benignPathCount == 0
    | project
        Timestamp,
        DeviceName,
        InitiatingProcessId,
        InitiatingProcessFileName,
        InitiatingProcessFolderPath,
        InitiatingProcessCommandLine,
        InitiatingProcessAccountName,
        TargetProcessName    = FileName,
        SpawnedCmdLine       = ProcessCommandLine,
        DetailFile           = FileName,
        DetailPath           = FolderPath,
        ActionType,
        Activity             = "Browser Spawned LOLBin/Shell";
// ── Unsigned Module Load by Browser from suspicious paths ────
let SuspiciousModuleLoad =
    DeviceImageLoadEvents
    | where Timestamp > ago(LookbackPeriod)
    | where InitiatingProcessFileName in~ (Browsers)
    | where IsCertificateValidated != true
    | where FolderPath has_any (
        @"\Users\",
        @"\ProgramData\",
        @"\Temp\",
        @"\AppData\Local\Temp\",
        @"\AppData\Roaming\"
    )
    | where FileName !in~ (
        "d3dcompiler_47.dll",
        "libEGL.dll",
        "libGLESv2.dll",
        "vk_swiftshader.dll",
        "vulkan-1.dll"
    )
    | project
        Timestamp,
        DeviceName,
        InitiatingProcessId,
        InitiatingProcessFileName,
        InitiatingProcessFolderPath,
        InitiatingProcessCommandLine,
        InitiatingProcessAccountName,
        TargetProcessName    = "",
        SpawnedCmdLine       = "",
        DetailFile           = FileName,
        DetailPath           = FolderPath,
        ActionType           = "",
        Activity             = "Unsigned Module In Browser";
// ── Correlation & Scoring ─────────────────────────────────────────────────────
union isfuzzy=true
    BrowserInjection,
    BrowserSpawnedHighRisk,
    BrowserSpawnedSuspiciousCmd,
    SuspiciousModuleLoad
// Collect all activities per browser process + device
| summarize
    Activities        = make_set(Activity),
    ActivityCount     = dcount(Activity),
    FirstSeen         = min(Timestamp),
    LastSeen          = max(Timestamp),
    InjectedInto      = make_set_if(TargetProcessName, Activity == "Browser Injection"),
    SpawnedBinaries   = make_set_if(DetailFile,        Activity == "Browser Spawned LOLBin/Shell"),
    SpawnedCmdLines   = make_set_if(SpawnedCmdLine,    Activity == "Browser Spawned LOLBin/Shell"),
    LoadedModules     = make_set_if(DetailPath,        Activity == "Unsigned Module In Browser"),
    BrowserCmdLines   = make_set(InitiatingProcessCommandLine),
    AccountName       = take_any(InitiatingProcessAccountName)
    by
        DeviceName,
        InitiatingProcessFileName,
        InitiatingProcessId
// ── Score-based Filtering ──────────────────────────────────────────────────
| extend Score =
    iff(Activities has "Browser Injection",            3, 0) +
    iff(Activities has "Browser Spawned LOLBin/Shell", 2, 0) +
    iff(Activities has "Unsigned Module In Browser",   1, 0)
| where Score >= 2
// ── Severity Label for Triage ──────────────────────────────────────────────────
| extend Severity = case(
    Score >= 5, "Critical",
    Score >= 3, "High",
    Score >= 2, "Medium",
    "Low"
)
```

Explanation

This KQL query is designed to detect potential browser-based attacks by looking for specific suspicious activities. It focuses on identifying three main behaviors that could indicate a security threat:

  1. Remote Code Injection: This involves unauthorized memory writes or thread creation in browser processes, which could suggest an attempt to inject malicious code.

  2. Anomalous Process Spawning: This checks if browsers are starting high-risk programs (like PowerShell or other command-line utilities) that could be used for malicious purposes.

  3. Unsigned Module Loads: This looks for browsers loading modules from unusual or sensitive directories without proper digital signatures, which can be a sign of tampering.

The query uses a set of known safe processes and paths to reduce false positives, ensuring that only genuinely suspicious activities are flagged. It aggregates the results by process session and assigns a severity score based on the combination of detected activities. This scoring helps prioritize incidents, with higher scores indicating a greater potential threat. The query is part of a broader effort to enhance security monitoring by identifying complex, multi-stage attacks that target web browsers.

Details

Benjamin Zulliger profile picture

Benjamin Zulliger

Released: May 11, 2026

Tables

DeviceEventsDeviceProcessEventsDeviceImageLoadEvents

Keywords

BrowserInjectionDeviceEventsProcessModuleLoad

Operators

letdynamicinin~toonsummarizecountifnotprojectwhereextendmv-applyhas_any!in~unionisfuzzymake_setmake_set_ifdcountminmaxtake_anybyiffcase

Actions