Suspicious IDE Extension Installation from VSIX


Description

Detects installation of an IDE extension from a local VSIX ("--install-extension" with a ".vsix" path) when that path is in a staging directory, or the parent is a script host or installer. VS Code and compatible editors (Cursor, VSCodium, Windsurf) use this CLI to sideload extensions outside the marketplace.

Query · esql

FROM logs-endpoint.events.process-* METADATA _id, _version, _index
| WHERE MV_CONTAINS(event.category, "process")
  AND MV_CONTAINS(event.type, "start")
  AND TO_LOWER(process.command_line) LIKE "*--install-extension*"
  AND TO_LOWER(process.command_line) LIKE "*.vsix*"
  AND (
    (
      host.os.type == "windows"
      AND (
        TO_LOWER(process.command_line) RLIKE
          """.*\\users\\public\\.*|.*\\appdata\\local\\temp\\.*|.*/users/public/.*|.*/appdata/local/temp/.*"""
        OR TO_LOWER(process.parent.name) IN (
          "mshta.exe",
          "wscript.exe",
          "cscript.exe",
          "powershell.exe",
          "msiexec.exe",
          "pwsh.exe",
          "node.exe",
          "bun.exe",
          "py.exe"
        )
        OR TO_LOWER(process.parent.name) LIKE "python*"
      )
    )
    OR
    (
      host.os.type IN ("macos", "linux")
      AND (
        TO_LOWER(process.command_line) LIKE (
          "*/tmp/*",
          "*/var/tmp/*",
          "*/private/tmp/*",
          "*/dev/shm/*",
          "*/users/shared/*"
        )
        OR TO_LOWER(process.parent.name) IN (
          "osascript", "node", "bun"
        )
        OR TO_LOWER(process.parent.name) LIKE "python*"
      )
    )
  )
| KEEP
    _id,
    _version,
    _index,
	data_stream.namespace,
    @timestamp,
    host.name,
	host.id,
	agent.id,
    host.os.type,
    user.name,
	user.domain,
    process.name,
    process.parent.name,
    process.command_line,
    process.parent.command_line

Investigation fields

Pivot points the source recommends for triage.

  • host.name
  • host.os.type
  • user.name
  • process.name
  • process.parent.name
  • process.command_line
  • process.parent.command_line

Implementation guide

This rule is designed for data generated by Elastic Defend, which provides native endpoint detection and response, along with event enrichments designed to work with our detection rules.

Setup instructions: https://ela.st/install-elastic-defend

Analyst notes

Investigating Suspicious IDE Extension Installation from VSIX

An IDE CLI (code, cursor, codium, or a compatible editor) was invoked with --install-extension against a .vsix file. The VSIX path is under a staging directory (Users\Public, AppData\Local\Temp, /tmp, /var/tmp, /private/tmp, /dev/shm), or the parent is a script host or installer rather than the editor itself. Sideloading a VSIX is a known way to persist or gain execution through a malicious IDE extension.

Possible investigation steps

  • From process.command_line, extract the .vsix path and the editor binary (code, code.exe, cursor, codium).
  • Review process.parent.name and process.parent.command_line to see whether a script host (powershell, pwsh, mshta, wscript, cscript, msiexec, python*, py.exe, node, bun, osascript) launched the install.
  • Hash the VSIX and compare it with the extension the editor actually installed under .vscode/extensions or the equivalent Cursor or VSCodium extensions directory.
  • On the same host and user, look for follow-on child processes from the extension host, new outbound connections, and files written by the extension.

False positive analysis

  • Extension development often installs a locally built VSIX from PowerShell, pwsh, Node, Bun, or Python, including from a temp directory. Confirm the VSIX path, publisher, and parent script with the developer before excluding.
  • Scope exceptions to a known VSIX path, parent command line, host, or user. Do not broadly exclude --install-extension or the editor binary.

Response and remediation

  • If the VSIX is unapproved, uninstall the extension, remove the VSIX, and restart the editor.
  • Quarantine the VSIX, collect its hash, and hunt that hash and command line on other hosts.
  • If a script host launched the install, investigate that parent for a downloader or installer and remove it.
  • Rotate tokens or credentials the extension could have read from the user profile or workspace.
Raw source Suspicious IDE Extension Installation from VSIX · Elastic TOML
Esc
Published by elastic/detection-rules ↗, licensed under Elastic License 2.0 ↗. Reproduced here unmodified.
[metadata]
creation_date = "2026/10/26"
integration = ["endpoint"]
maturity = "production"
min_stack_comments = "MV_CONTAINS was added to ES|QL in 9.2.0 and is not available on 8.19; 9.3.0 is the lowest supported 9.x release."
min_stack_version = "9.3.0"
updated_date = "2026/10/26"

[rule]
author = ["Elastic"]
description = """
Detects installation of an IDE extension from a local VSIX ("--install-extension" with a ".vsix" path) when that path is
in a staging directory, or the parent is a script host or installer. VS Code and compatible editors (Cursor, VSCodium,
Windsurf) use this CLI to sideload extensions outside the marketplace.
"""
from = "now-9m"
language = "esql"
license = "Elastic License v2"
name = "Suspicious IDE Extension Installation from VSIX"
note = """## Triage and analysis

### Investigating Suspicious IDE Extension Installation from VSIX

An IDE CLI (`code`, `cursor`, `codium`, or a compatible editor) was invoked with `--install-extension` against a
`.vsix` file. The VSIX path is under a staging directory (`Users\\Public`, `AppData\\Local\\Temp`, `/tmp`,
`/var/tmp`, `/private/tmp`, `/dev/shm`), or the parent is a script host or installer rather than the editor itself.
Sideloading a VSIX is a known way to persist or gain execution through a malicious IDE extension.

### Possible investigation steps

- From `process.command_line`, extract the `.vsix` path and the editor binary (`code`, `code.exe`, `cursor`, `codium`).
- Review `process.parent.name` and `process.parent.command_line` to see whether a script host (`powershell`,
  `pwsh`, `mshta`, `wscript`, `cscript`, `msiexec`, `python*`, `py.exe`, `node`, `bun`, `osascript`) launched the install.
- Hash the VSIX and compare it with the extension the editor actually installed under `.vscode/extensions` or the
  equivalent Cursor or VSCodium extensions directory.
- On the same host and user, look for follow-on child processes from the extension host, new outbound connections,
  and files written by the extension.

### False positive analysis

- Extension development often installs a locally built VSIX from PowerShell, pwsh, Node, Bun, or Python, including
  from a temp directory. Confirm the VSIX path, publisher, and parent script with the developer before excluding.
- Scope exceptions to a known VSIX path, parent command line, host, or user. Do not broadly exclude
  `--install-extension` or the editor binary.

### Response and remediation

- If the VSIX is unapproved, uninstall the extension, remove the VSIX, and restart the editor.
- Quarantine the VSIX, collect its hash, and hunt that hash and command line on other hosts.
- If a script host launched the install, investigate that parent for a downloader or installer and remove it.
- Rotate tokens or credentials the extension could have read from the user profile or workspace.
"""
references = [
    "https://www.mdsec.co.uk/2021/01/macos-post-exploitation-shenanigans-with-vscode-extensions/",
    "https://code.visualstudio.com/docs/editor/extension-marketplace#_install-from-a-vsix",
]
risk_score = 47
rule_id = "7babe894-46e5-4cc8-a7ae-3b0e71e31d97"
setup = """## Setup

This rule is designed for data generated by [Elastic Defend](https://www.elastic.co/security/endpoint-security), which provides native endpoint detection and response, along with event enrichments designed to work with our detection rules.

Setup instructions: https://ela.st/install-elastic-defend
"""
severity = "medium"
tags = [
    "Domain: Endpoint",
    "OS: Linux",
    "OS: Windows",
    "OS: macOS",
    "Use Case: Threat Detection",
    "Tactic: Persistence",
    "Data Source: Elastic Defend",
    "Resources: Investigation Guide",
    "Rule Type: ES|QL",
    "Platform: Windows",
    "Platform: Linux",
    "Platform: macOS",
]
timestamp_override = "event.ingested"
type = "esql"

query = '''
FROM logs-endpoint.events.process-* METADATA _id, _version, _index
| WHERE MV_CONTAINS(event.category, "process")
  AND MV_CONTAINS(event.type, "start")
  AND TO_LOWER(process.command_line) LIKE "*--install-extension*"
  AND TO_LOWER(process.command_line) LIKE "*.vsix*"
  AND (
    (
      host.os.type == "windows"
      AND (
        TO_LOWER(process.command_line) RLIKE
          """.*\\users\\public\\.*|.*\\appdata\\local\\temp\\.*|.*/users/public/.*|.*/appdata/local/temp/.*"""
        OR TO_LOWER(process.parent.name) IN (
          "mshta.exe",
          "wscript.exe",
          "cscript.exe",
          "powershell.exe",
          "msiexec.exe",
          "pwsh.exe",
          "node.exe",
          "bun.exe",
          "py.exe"
        )
        OR TO_LOWER(process.parent.name) LIKE "python*"
      )
    )
    OR
    (
      host.os.type IN ("macos", "linux")
      AND (
        TO_LOWER(process.command_line) LIKE (
          "*/tmp/*",
          "*/var/tmp/*",
          "*/private/tmp/*",
          "*/dev/shm/*",
          "*/users/shared/*"
        )
        OR TO_LOWER(process.parent.name) IN (
          "osascript", "node", "bun"
        )
        OR TO_LOWER(process.parent.name) LIKE "python*"
      )
    )
  )
| KEEP
    _id,
    _version,
    _index,
	data_stream.namespace,
    @timestamp,
    host.name,
	host.id,
	agent.id,
    host.os.type,
    user.name,
	user.domain,
    process.name,
    process.parent.name,
    process.command_line,
    process.parent.command_line
'''


[[rule.threat]]
framework = "MITRE ATT&CK"

[[rule.threat.technique]]
id = "T1176"
name = "Software Extensions"
reference = "https://attack.mitre.org/techniques/T1176/"

[[rule.threat.technique.subtechnique]]
id = "T1176.002"
name = "IDE Extensions"
reference = "https://attack.mitre.org/techniques/T1176/002/"

[rule.threat.tactic]
id = "TA0003"
name = "Persistence"
reference = "https://attack.mitre.org/tactics/TA0003/"

[rule.investigation_fields]
field_names = [
    "host.name",
    "host.os.type",
    "user.name",
    "process.name",
    "process.parent.name",
    "process.command_line",
    "process.parent.command_line"
]

Detection rules belong to the projects that publish them and remain under their own licenses. This site indexes and links to them; it claims no rights in them.