Query Details

Hunting Uncommon VS Code Extensions

Query

// Define time window for network join (2 minutes)
let TimeWindow = 2m;
// Gather installed extensions including SHA256 of the package.json (Works for Windows & macOS)
let InstalledExtensions = 
    DeviceFileEvents
    | where TimeGenerated > ago(7d)
    | where FolderPath has ".vscode/extensions" or FolderPath has @".vscode\extensions"
    | where ActionType == "FileCreated" and FileName =~ "package.json"
    | where isnotempty(SHA256)
    // Dynamic extraction handles both backward and forward slashes
    | extend ExtensionID = extract(@"(?i)\.vscode[/\\]extensions[/\\]([^/\\]+)", 1, FolderPath)
    | project FileCreationTime = TimeGenerated, DeviceName, DeviceId, ExtensionID, FolderPath, SHA256,
              InitiatingProcessFileName, InitiatingProcessId, InitiatingProcessCreationTime, RequestAccountName;
// Gather network connections including macOS specific VS Code processes
let NetworkConnections = 
    DeviceNetworkEvents
    | where TimeGenerated > ago(7d)
    // Expanded process list to support macOS binaries (Electron, Code Helper, Visual Studio Code)
    | where InitiatingProcessFileName in~ ("code.exe", "code", "vsce-sign.exe", "Electron", "Visual Studio Code", "Code Helper") 
      or RemoteUrl has "vsassets.io" or RemoteUrl has "visualstudio.com"
    | project NetworkTime = TimeGenerated, DeviceId, RemoteIP, RemoteUrl, RemotePort, 
              InitiatingProcessFileName, InitiatingProcessId, InitiatingProcessCreationTime;
// Correlation and initial scoring based on network source
let ScoredNetworkData = 
    InstalledExtensions
    | join kind=inner (NetworkConnections) on DeviceId, InitiatingProcessFileName, InitiatingProcessId, InitiatingProcessCreationTime
    | where FileCreationTime between ((NetworkTime - TimeWindow) .. (NetworkTime + TimeWindow))
    | extend TimeDifferenceSeconds = datetime_diff('second', FileCreationTime, NetworkTime)
    // Scoring based on the source URL
    | extend NetworkScore = iif(RemoteUrl has "vsassets.io" or RemoteUrl has "visualstudio.com", 0, 10);
// Query FileProfile() for global prevalence
ScoredNetworkData
| invoke FileProfile(SHA256, 10000)
// Scoring based on GlobalPrevalence (handling empty values as high risk)
| extend PrevalenceScore = case(
    GlobalPrevalence < 1000 or isempty(GlobalPrevalence), 10,
    GlobalPrevalence >= 1000 and GlobalPrevalence <= 2500, 5,
    0
)
// Calculate total risk score and assign severity levels
| extend TotalRiskScore = NetworkScore + PrevalenceScore
| extend Severity = case(
    TotalRiskScore >= 20, "Critical",
    TotalRiskScore >= 10, "High",
    TotalRiskScore >= 5, "Medium",
    "Low"
)
// Select and arrange columns for the alert overview
| project Severity, TotalRiskScore, NetworkScore, PrevalenceScore, GlobalPrevalence, ExtensionID, DeviceName, RequestAccountName, 
          RemoteUrl, RemoteIP, SHA256, FolderPath, FileCreationTime, TimeDifferenceSeconds
| order by TotalRiskScore desc, FileCreationTime desc```

About this query

Explanation

This query is designed to proactively identify potentially harmful or untrusted Visual Studio Code (VSCode) extensions by monitoring their installation and correlating this with unusual network activity. Here's a simplified breakdown of how it works:

  1. Monitoring New Extensions: The query looks for newly created package.json files in the VSCode extensions directory over the past week. This helps identify new extensions installed on both Windows and macOS systems.

  2. Network Activity Correlation: It collects network events from VSCode processes or official domains and matches them with the file creation events within a 2-minute window. This helps determine if the extension was downloaded from a suspicious source.

  3. Risk Scoring:

    • Network Score: If the extension was downloaded from a non-Microsoft source, it gets a risk score of 10 points.
    • Prevalence Score: It checks Microsoft's global threat intelligence database to see how common the extension is. If it's installed on fewer than 1,000 devices globally, it adds another 10 points to the risk score.
  4. Risk Classification: The query combines these scores into a total risk score to classify the severity of the alert from Low to Critical. Extensions that are both rare and downloaded from non-standard URLs are flagged as Critical.

  5. Proactive Threat Hunting: This query is resource-intensive and is meant for proactive threat hunting rather than real-time alerts. It's effective at discovering targeted supply-chain attacks, rogue extensions, or custom malicious extensions on developer workstations.

In summary, this query helps identify potentially malicious VSCode extensions by analyzing new installations, their download sources, and their prevalence across devices, assigning a risk score to determine the severity of the threat.

Details

Benjamin Zulliger profile picture

Benjamin Zulliger

Released: June 8, 2026

Tables

DeviceFileEventsDeviceNetworkEvents

Keywords

DeviceFileEventsNetworkVisualStudioCodeExtensionsConnectionsProfileGlobalPrevalenceSeverityTotalRiskScore

Operators

letagohasor===~isnotemptyextendextractprojectin~joinbetweendatetime_diffiifinvokecase<isempty>=order bydesc

MITRE Techniques

Actions

GitHub