Systemd unit file written under a unit search path on Linux
Matches the file-creation and file-modification legs of AN0701: an auditd PATH record naming a file inside one of the directories systemd loads unit definitions from, which is where this technique plants the `ExecStart` line it wants re-executed at every boot. The directory list is MITRE's `ServicePathRegex` knob, populated here with the system and user unit search paths from systemd.unit(5); `/lib/systemd/system/` is written without the `/usr` prefix on purpose, because `contains` matching means that one entry also covers `/usr/lib/systemd/system/` and `/usr/local/lib/systemd/system/` on the merged-/usr layouts every current distribution uses. The selections are anchored on the directory rather than on a `.service` suffix because the two most common ways to change what a service runs do not produce a `.service` filename at all — `systemctl edit` writes `<unit>.service.d/override.conf`, and `systemctl enable` creates a symlink inside `<target>.target.wants/` — so a suffix gate would miss both. The cost is a deliberate over-match: a `.timer` or `.socket` unit written into the same directories also fires here, and those belong to T1053.006 rather than to this technique. A separate suffix selection and a watch-key selection are present because auditd records `name=` exactly as it was handed to the syscall, so an edit made from inside the directory logs a bare `foo.service` with the directory only on a CWD record Sigma cannot join; the key branch closes the same gap for any filename, but only for deployments whose watches carry a key containing `systemd`. Three parts of the analytic are out of reach. PATH records carry a filename and never file contents, so the `ExecStart` target cannot be read at all and MITRE's `ExecStartPathAllowlist` and `FileEntropyThreshold` knobs — along with the analytic's `/tmp` and `/dev/shm` payload check — have nothing to act on here; auditd splits PATH and SYSCALL into separate records for one event and the writing process's `uid` and `auid` live only on the SYSCALL side, so `UserContextFilter` cannot be applied inside these selections; and the `systemctl enable` plus `systemctl start` pairing behind `SystemctlOperationSet` is a separate execve event with no join available in lib/sigma, which is also why it is not OR-ed in — every service start on every host would match. The linux/osquery source in the brief is not attempted, because it has no standardised Sigma field names. Prerequisite: no distribution audits any of these paths by default, and an auditd watch covers one concrete directory only, so each selection is blind until its own watch is loaded — `-w /etc/systemd/system -p wa -k systemd`, `-w /usr/lib/systemd/system -p wa -k systemd`, `-w /run/systemd/system -p wa -k systemd` and the three matching `.../systemd/user` paths. The two home-relative entries are weaker still: `~/.config/systemd/user/` and `~/.local/share/systemd/user/` cannot be watched generically, so they stay dead until a per-user watch is added or `-w /home -p wa -k systemd` is accepted along with the volume that brings — and user-level units are a documented variant of this technique, so treat that as missing coverage rather than as absence of the behaviour. Zero rows from any of these branches reads as quiet when it actually means blind. Note also that one write can match twice, once on the PATH record and once on the SYSCALL record carrying the key, so hits should be deduplicated on the audit event id before volume is judged. UNVERIFIED AS AUTHORED — derived from MITRE ATT&CK DET0253, not hand-written and not tested by its author. Any lab result is on this rule's own page.Full descriptionShow less
The detection
The 3 SIEM queries below are previews produced by Siemphony’s own translator, not pySigma, and none has been executed against a real backend — review before you deploy.
detection.yml
Splunk · SPL
Run this as a search.
index=* ((type="PATH" AND (name="*/etc/systemd/system/*" OR name="*/run/systemd/system/*" OR name="*/lib/systemd/system/*")) OR (type="PATH" AND (name="*/etc/systemd/user/*" OR name="*/run/systemd/user/*" OR name="*/lib/systemd/user/*" OR name="*/.config/systemd/user/*" OR name="*/.local/share/systemd/user/*")) OR (type="PATH" AND name="*.service") OR key="*systemd*")Elastic · ES|QL
Run this as a search.
FROM logs-*| WHERE ((TO_LOWER(type) == "path" AND (TO_LOWER(name) LIKE "*/etc/systemd/system/*" OR TO_LOWER(name) LIKE "*/run/systemd/system/*" OR TO_LOWER(name) LIKE "*/lib/systemd/system/*")) OR (TO_LOWER(type) == "path" AND (TO_LOWER(name) LIKE "*/etc/systemd/user/*" OR TO_LOWER(name) LIKE "*/run/systemd/user/*" OR TO_LOWER(name) LIKE "*/lib/systemd/user/*" OR TO_LOWER(name) LIKE "*/.config/systemd/user/*" OR TO_LOWER(name) LIKE "*/.local/share/systemd/user/*")) OR (TO_LOWER(type) == "path" AND TO_LOWER(name) LIKE "*.service") OR TO_LOWER(key) LIKE "*systemd*")
Wazuh · XML rule
Deploy to your manager — this is a rule, not a search.
<group name="sigma,linux,"> <!-- Rule ids must be unique on your manager; 100000+ is the user range. --> <!-- 4 rules: the Sigma condition ORs across different fields, which one rule cannot express. Any one matching is a hit. --> <rule id="100000" level="5"> <!-- Set <if_sid> to the decoder/base rule for linux so this only evaluates relevant events. --> <field name="type" type="pcre2">(?i)^PATH$</field> <field name="name" type="pcre2">(?i)(/etc/systemd/system/|/run/systemd/system/|/lib/systemd/system/)</field> <description>Systemd unit file written under a unit search path on Linux (1/4)</description> <mitre> <id>T1543.002</id> </mitre> </rule> <rule id="100001" level="5"> <!-- Set <if_sid> to the decoder/base rule for linux so this only evaluates relevant events. --> <field name="type" type="pcre2">(?i)^PATH$</field> <field name="name" type="pcre2">(?i)(/etc/systemd/user/|/run/systemd/user/|/lib/systemd/user/|/\.config/systemd/user/|/\.local/share/systemd/user/)</field> <description>Systemd unit file written under a unit search path on Linux (2/4)</description> <mitre> <id>T1543.002</id> </mitre> </rule> <rule id="100002" level="5"> <!-- Set <if_sid> to the decoder/base rule for linux so this only evaluates relevant events. --> <field name="type" type="pcre2">(?i)^PATH$</field> <field name="name" type="pcre2">(?i)\.service$</field> <description>Systemd unit file written under a unit search path on Linux (3/4)</description> <mitre> <id>T1543.002</id> </mitre> </rule> <rule id="100003" level="5"> <!-- Set <if_sid> to the decoder/base rule for linux so this only evaluates relevant events. --> <field name="key" type="pcre2">(?i)systemd</field> <description>Systemd unit file written under a unit search path on Linux (4/4)</description> <mitre> <id>T1543.002</id> </mitre> </rule></group>
Sentinel · KQL
Run this as a search.
Sentinel cannot express this construct. No Sentinel table is modelled for this logsource ({"product":"linux","service":"auditd"}), so there is nothing honest to query. Pick a logsource this translator maps, or write the KQL by hand. The Sigma source is on the first tab, unchanged.
Verdicts · reactions · comments
Community
Verdicts from engineers who actually deployed it, and the conversation around it.
Nobody has run this in a real environment and said what happened.
Verdicts from engineers who deployed it
0 castNo verdicts reported yet. The first one is worth more than the tenth — say what you ran it against.
Log in to report a verdict.
Log in to join the discussion.
No comments yet. Someone who deploys this will have something to say about it.