← Back to Explore
elastichighTTP
Persistence via Microsoft Office AddIns
Detects attempts to establish persistence on an endpoint by abusing Microsoft Office add-ins.
Detection Query
file where host.os.type == "windows" and event.type != "deletion" and
file.extension : ("wll","xll","ppa","ppam","xla","xlam") and
file.path : (
"C:\\Users\\*\\AppData\\Roaming\\Microsoft\\Word\\Startup\\*",
"C:\\Users\\*\\AppData\\Roaming\\Microsoft\\AddIns\\*",
"C:\\Users\\*\\AppData\\Roaming\\Microsoft\\Excel\\XLSTART\\*",
/* Crowdstrike specific condition as it uses NT Object paths */
"\\Device\\HarddiskVolume*\\Users\\*\\AppData\\Roaming\\Microsoft\\Word\\Startup\\*",
"\\Device\\HarddiskVolume*\\Users\\*\\AppData\\Roaming\\Microsoft\\AddIns\\*",
"\\Device\\HarddiskVolume*\\Users\\*\\AppData\\Roaming\\Microsoft\\Excel\\XLSTART\\*"
) and
not (file.name : "~$*" and process.name : "excel.exe" and ?file.size in (0, 165))
Author
Elastic
Created
2020/10/16
Data Sources
Elastic EndgameElastic DefendSysmonMicrosoft Defender XDRSentinelOneCrowdstrikelogs-endpoint.events.file-*winlogbeat-*logs-windows.sysmon_operational-*endgame-*logs-m365_defender.event-*logs-sentinel_one_cloud_funnel.*logs-crowdstrike.fdr*
Tags
Domain: EndpointOS: WindowsUse Case: Threat DetectionTactic: PersistenceData Source: Elastic EndgameData Source: Elastic DefendData Source: SysmonData Source: Microsoft Defender XDRData Source: SentinelOneData Source: CrowdstrikeResources: Investigation Guide
Raw Content
[metadata]
creation_date = "2020/10/16"
integration = ["endpoint", "windows", "m365_defender", "sentinel_one_cloud_funnel", "crowdstrike"]
maturity = "production"
updated_date = "2026/05/03"
[rule]
author = ["Elastic"]
description = "Detects attempts to establish persistence on an endpoint by abusing Microsoft Office add-ins."
from = "now-9m"
index = [
"logs-endpoint.events.file-*",
"winlogbeat-*",
"logs-windows.sysmon_operational-*",
"endgame-*",
"logs-m365_defender.event-*",
"logs-sentinel_one_cloud_funnel.*",
"logs-crowdstrike.fdr*",
]
language = "eql"
license = "Elastic License v2"
name = "Persistence via Microsoft Office AddIns"
references = ["https://labs.withsecure.com/publications/add-in-opportunities-for-office-persistence"]
risk_score = 73
rule_id = "f44fa4b6-524c-4e87-8d9e-a32599e4fb7c"
severity = "high"
tags = [
"Domain: Endpoint",
"OS: Windows",
"Use Case: Threat Detection",
"Tactic: Persistence",
"Data Source: Elastic Endgame",
"Data Source: Elastic Defend",
"Data Source: Sysmon",
"Data Source: Microsoft Defender XDR",
"Data Source: SentinelOne",
"Data Source: Crowdstrike",
"Resources: Investigation Guide",
]
timestamp_override = "event.ingested"
type = "eql"
query = '''
file where host.os.type == "windows" and event.type != "deletion" and
file.extension : ("wll","xll","ppa","ppam","xla","xlam") and
file.path : (
"C:\\Users\\*\\AppData\\Roaming\\Microsoft\\Word\\Startup\\*",
"C:\\Users\\*\\AppData\\Roaming\\Microsoft\\AddIns\\*",
"C:\\Users\\*\\AppData\\Roaming\\Microsoft\\Excel\\XLSTART\\*",
/* Crowdstrike specific condition as it uses NT Object paths */
"\\Device\\HarddiskVolume*\\Users\\*\\AppData\\Roaming\\Microsoft\\Word\\Startup\\*",
"\\Device\\HarddiskVolume*\\Users\\*\\AppData\\Roaming\\Microsoft\\AddIns\\*",
"\\Device\\HarddiskVolume*\\Users\\*\\AppData\\Roaming\\Microsoft\\Excel\\XLSTART\\*"
) and
not (file.name : "~$*" and process.name : "excel.exe" and ?file.size in (0, 165))
'''
note = """## Triage and analysis
### Investigating Persistence via Microsoft Office AddIns
#### Possible investigation steps
- Which Office add-in autoload mechanism does the alert show?
- Focus: alert `file.path`, `file.name`, `file.extension`, and `user.id` on `host.id`: "wll" maps to Word Startup, "xla"/"xlam" to Excel XLSTART, and "xll"/"ppa"/"ppam" to add-in locations that may need loader settings.
- Implication: escalate when a user-profile Office startup or add-in path receives a one-off or user-initiated add-in; lower suspicion only when path, name, extension, writer, and provenance all fit the same recognized deployment or repair.
- Does the artifact identity or provenance look like a staged payload?
- Why: DLL-style "wll"/"xll" and Office container add-ins have different expected headers, so rename and origin fields can expose payload staging.
- Focus: `file.Ext.header_bytes`, `file.Ext.original.path`, `file.origin_url`, and `file.Ext.windows.zone_identifier`.
- Implication: escalate when headers, rename history, internet-zone provenance, or archive/mail-cache origin conflict with the claimed add-in; lower suspicion when format and provenance align with the same recognized vendor package.
- Does the writer process fit an add-in installer or a dropper chain?
- Focus: writer `process.executable`, `process.command_line`, `process.parent.executable`, and `process.code_signature.subject_name`.
- Implication: escalate when browsers, mail clients, scripting engines, archive tools, or unsigned user-writable binaries write the add-in; lower suspicion when signer, path, command line, and parent all match the same recognized installer or managed integration. Signer trust alone never clears the placement.
- If registry telemetry is available, did the writer create loader settings required for this add-in type?
- Why: the alert is a file write; loader settings, when visible, show whether Office was configured to consume the add-in rather than merely store it.
- Focus: same `host.id` and writer `process.entity_id`; check `registry.path`, `registry.value`, `registry.data.type`, and `registry.data.strings` for Office loader values pointing to the alerted file. $investigate_0
- Implication: escalate when loader values reference the alerted file or directory; do not require registry corroboration when path, provenance, or writer evidence is already suspicious. Missing registry telemetry is unresolved, not benign.
- Did Office later load the add-in or spawn follow-on activity from it?
- Focus: later Office `process.name` values "WINWORD.EXE", "EXCEL.EXE", or "POWERPNT.EXE" on the same `host.id`, Office `process.entity_id`, child `process.parent.entity_id`, child `process.executable`, and, if library telemetry exists, `dll.path` matching the add-in.
- $investigate_1
- $investigate_2
- Hint: missing library telemetry is unresolved, not benign; use those pivots and child process expansion from recovered Office instances as fallback execution evidence.
- Implication: escalate when Office loads the add-in path, starts a child from the same session, or touches a payload dropped with the add-in; do not wait for library telemetry when file, writer, or loader evidence already warrants escalation.
- If local evidence is suspicious or unresolved, what related host activity changes scope?
- Focus: related alerts on the same `host.id`, especially document delivery, script execution, suspicious file writes, Office child processes, or additional persistence activity. $investigate_3
- Implication: expand scope when related host alerts connect the add-in write to delivery, execution, credential access, or another persistence mechanism; do not close solely because related alerts are absent.
- What disposition do the add-in mechanism, artifact identity, writer lineage, loader state, Office execution, and scope support?
- Escalate on suspicious provenance, unexpected writer lineage, matching loader values, Office follow-on execution, or related host activity; close only when file, writer, provenance, loader state, and Office behavior align with one verified deployment or integration and no contradictions remain; preserve artifacts and escalate mixed or incomplete evidence.
### False positive analysis
- Managed Office add-in deployment, repair, and productivity, accessibility, or CRM first-use installs can place files in these paths. Close only when writer `process.executable`, `process.command_line`, signer `process.code_signature.subject_name`, parent `process.parent.executable`, final `file.path`, required loader state, provenance fields, later Office `process.name`, and child `process.executable` all align with one deployment or vendor workflow and no contradictory evidence remains. Use change or inventory records as corroboration, not as a telemetry substitute.
- Before creating an exception, build it from the minimum current-case pattern: `file.path`, writer `process.code_signature.subject_name`, writer `process.parent.executable`, required loader-state pattern, and `host.id` or `user.id` scope. Avoid exceptions on Office startup directories, file extensions, or Office process names alone.
### Response and remediation
- If confirmed benign, reverse temporary containment and document the verified workflow with `file.path`, `file.hash.sha256`, writer `process.executable`, signer `process.code_signature.subject_name`, and any recovered loader-state evidence. Create any exception from that exact workflow pattern and scope it narrowly to the confirmed host or user cohort.
- If suspicious but unconfirmed, preserve the alert export, the add-in file and hash from `file.path` and `file.hash.sha256`, writer `process.entity_id` and `process.command_line`, and any recovered loader-state, library-load, or child-process evidence before deleting anything. Apply reversible containment first, such as quarantining the add-in file, disabling the affected Office add-in path, or temporarily restricting Office execution on the host.
- If confirmed malicious, isolate the host if its role can tolerate interruption, then terminate responsible Office or loader processes only after preserving their entity IDs, command lines, and the add-in file evidence. Remove the malicious add-in, clear associated Office loader configuration, restore affected Office add-in settings, and remediate the delivery path that introduced the file. Reset credentials only if the investigation also shows account misuse.
- Review related hosts and users for the same `file.path`, `file.hash.sha256`, writer lineage, and loader-state pattern before eradication. For Word WLL cases, do not rely on the Office UI alone to prove the add-in is disabled; verify the file and supporting configuration are gone.
- Post-incident hardening: restrict write access to Office startup directories and add-in loader locations, prefer signed managed add-in deployment, retain process/file plus registry or library telemetry needed to confirm later Office execution, and document adjacent variants such as Word WLL autoload abuse or registry-backed Office add-in loading in the case record.
"""
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
### Additional data sources
This rule also supports the following third-party data sources. For setup instructions, refer to the links below:
- [CrowdStrike](https://ela.st/crowdstrike-integration)
- [Microsoft Defender XDR](https://ela.st/m365-defender)
- [SentinelOne Cloud Funnel](https://ela.st/sentinel-one-cloud-funnel)
- [Sysmon Event ID 11 - File Create](https://ela.st/sysmon-event-11-setup)
"""
[rule.investigation_fields]
field_names = [
"@timestamp",
"event.action",
"host.id",
"user.id",
"process.entity_id",
"process.pid",
"process.executable",
"process.command_line",
"process.parent.executable",
"process.code_signature.subject_name",
"file.path",
"file.extension",
"file.hash.sha256",
"file.origin_url",
]
[transform]
[[transform.investigate]]
label = "Registry events for the same writing process"
description = ""
providers = [
[
{ excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" },
{ excluded = false, field = "process.entity_id", queryType = "phrase", value = "{{process.entity_id}}", valueType = "string" },
{ excluded = false, field = "event.category", queryType = "phrase", value = "registry", valueType = "string" }
],
[
{ excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" },
{ excluded = false, field = "process.pid", queryType = "phrase", value = "{{process.pid}}", valueType = "string" },
{ excluded = false, field = "event.category", queryType = "phrase", value = "registry", valueType = "string" }
]
]
relativeFrom = "now-1h"
relativeTo = "now"
[[transform.investigate]]
label = "Office process starts on the host"
description = ""
providers = [
[
{ excluded = false, field = "event.category", queryType = "phrase", value = "process", valueType = "string" },
{ excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" },
{ excluded = false, field = "process.name", queryType = "phrase", value = "WINWORD.EXE", valueType = "string" }
],
[
{ excluded = false, field = "event.category", queryType = "phrase", value = "process", valueType = "string" },
{ excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" },
{ excluded = false, field = "process.name", queryType = "phrase", value = "EXCEL.EXE", valueType = "string" }
],
[
{ excluded = false, field = "event.category", queryType = "phrase", value = "process", valueType = "string" },
{ excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" },
{ excluded = false, field = "process.name", queryType = "phrase", value = "POWERPNT.EXE", valueType = "string" }
]
]
relativeFrom = "now-24h/h"
relativeTo = "now"
[[transform.investigate]]
label = "Library loads for the add-in path"
description = ""
providers = [
[
{ excluded = false, field = "event.category", queryType = "phrase", value = "library", valueType = "string" },
{ excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" },
{ excluded = false, field = "dll.path", queryType = "phrase", value = "{{file.path}}", valueType = "string" }
]
]
relativeFrom = "now-24h/h"
relativeTo = "now"
[[transform.investigate]]
label = "Alerts associated with the host"
description = ""
providers = [
[
{ excluded = false, field = "event.kind", queryType = "phrase", value = "signal", valueType = "string" },
{ excluded = false, field = "host.id", queryType = "phrase", value = "{{host.id}}", valueType = "string" }
]
]
relativeFrom = "now-48h/h"
relativeTo = "now"
[[rule.threat]]
framework = "MITRE ATT&CK"
[[rule.threat.technique]]
id = "T1137"
name = "Office Application Startup"
reference = "https://attack.mitre.org/techniques/T1137/"
[[rule.threat.technique.subtechnique]]
id = "T1137.006"
name = "Add-ins"
reference = "https://attack.mitre.org/techniques/T1137/006/"
[rule.threat.tactic]
id = "TA0003"
name = "Persistence"
reference = "https://attack.mitre.org/tactics/TA0003/"