NORMAL.DOTM
ADD-INS
XLL
OUTLOOK RULES
DetectionBlue TeamSOCOctober 12, 2026•9 min read

Detecting Office Add-in, Template and Outlook Persistence

Office applications start constantly and load code on purpose. That makes templates, add-ins and mailbox rules attractive places to hide something that runs every time a user opens a document.

Scroll

Microsoft Office offers several extension points that run code when an application starts or a trigger fires: global templates like Normal.dotm, STARTUP folders, COM and VSTO add-ins, native XLL and WLL add-ins, and Outlook rules, forms and home pages. Attackers use them to persist inside a trusted process, often after an initial macro or phishing foothold. Detection rests on file writes to the extension folders, add-in registry keys and unusual child processes of Office applications.

Signals at a Glance

SignalWhereATT&CK
Normal.dotm or a file in Word STARTUP / Excel XLSTART written by a non-Office processSysmon 11, EDR file eventsT1137.001
New .wll or .xll file in an Office add-in or startup locationSysmon 11T1137.006
New COM add-in key under Office\<app>\Addins with LoadBehavior 3Sysmon 13T1137.006
Outlook rule with a start-application action, or EnableUnsafeClientMailRules enabledRegistry, mailbox auditT1137.005
Outlook form or home-page registry changes, VbaProject.OTM presentSysmon 11/13T1137.004
WINWORD.EXE, EXCEL.EXE or OUTLOOK.EXE spawning shells, script hosts or loading unsigned DLLsSysmon 1, 7T1566.001

Templates, Startup Folders and Add-in Keys

A modified Normal.dotm executes its macros every time Word starts. Word legitimately rewrites that file when a user changes defaults, so the signal is a write by a process other than Word, or a macro-enabled template appearing in the STARTUP folders. Add-ins register under per-user keys that need no admin rights. This KQL covers both:

// KQL: Office extension files and add-in registrations
let extFiles = DeviceFileEvents
    | where ActionType in ("FileCreated", "FileModified")
    | where FolderPath has_any (@"\Microsoft\Templates", @"\Microsoft\Word\STARTUP",
                                @"\Microsoft\Excel\XLSTART", @"\Microsoft\AddIns")
    | where FileName endswith ".dotm" or FileName endswith ".wll" or FileName endswith ".xll"
         or FileName endswith ".xlam" or FileName endswith ".xla"
    | where InitiatingProcessFileName !in~ ("WINWORD.EXE", "EXCEL.EXE")
    | project Timestamp, DeviceName, Signal = "office_ext_file", Detail = FolderPath, FileName,
              InitiatingProcessFileName, InitiatingProcessCommandLine;
let addinKeys = DeviceRegistryEvents
    | where ActionType == "RegistryValueSet"
    | where RegistryKey has @"\Microsoft\Office" and RegistryKey has @"\Addins\"
    | where RegistryValueName in~ ("LoadBehavior", "Manifest")
    | project Timestamp, DeviceName, Signal = "addin_key", Detail = RegistryKey, FileName = RegistryValueName,
              InitiatingProcessFileName, InitiatingProcessCommandLine;
union extFiles, addinKeys
| order by Timestamp desc
// tune/validate: enterprise add-ins (PDF, CRM, Teams) register here; allowlist by ProgID and writer.

Outlook Rules, Forms and Fleet Inventory

Outlook persistence typically relies on a mailbox rule or custom form that runs when a message arrives. Microsoft disabled the start-application rule action and form scripting by default after public tooling made them practical, so attackers must re-enable them through registry settings. That makes the configuration changes a precise signal. Inventory add-ins directly to catch anything that predates your telemetry:

# PowerShell: inventory Office add-ins (per-user and per-machine) and key Outlook policy values
$roots = 'HKCU:\Software\Microsoft\Office',
         'HKLM:\SOFTWARE\Microsoft\Office',
         'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Office'
foreach ($r in $roots) {
  Get-ChildItem $r -Recurse -ErrorAction SilentlyContinue |
    Where-Object { $_.PSPath -match '\\Addins\\[^\\]+$' } |
    ForEach-Object {
      $p = Get-ItemProperty $_.PSPath
      [pscustomobject]@{ Key = $_.Name; LoadBehavior = $p.LoadBehavior; Manifest = $p.Manifest
                         FriendlyName = $p.FriendlyName }
    }
}
Get-ChildItem 'HKCU:\Software\Microsoft\Office\*\Outlook\Security' -ErrorAction SilentlyContinue |
  Get-ItemProperty | Select-Object PSPath, EnableUnsafeClientMailRules
# tune/validate: compare to a gold image; unknown ProgIDs with LoadBehavior 3 are the interesting set.
Vetted sources

Use the SigmaHQ Office persistence and Office child-process rules, Elastic’s Office template and Outlook detections, Splunk ESCU content, MITRE CAR analytics and Atomic Red Team tests for T1137, plus Microsoft’s documentation on Office policy settings. Baseline your own environment, because business add-ins and approved templates register legitimately.

Build Detections That Fire

Mjolnir Security helps blue teams turn threat intelligence into high-fidelity detections across endpoint, network, identity and cloud telemetry.

Detection EngineeringManaged Detection & ResponseThreat HuntingIncident ResponsePurple Teaming
  • Detection Engineering We build and tune detections mapped to ATT&CK from your own telemetry.
  • Threat Hunting as a Service Hypothesis-driven hunts for the behaviours on this page.
  • 24/7 Incident Response Containment when a detection fires. Call +1 833 403 5875.
Written by: Mjolnir Threat Research (MTAC)  |  Published: October 12, 2026  |  Last reviewed: October 12, 2026