Wazuh fix note · Wazuh 5.0 release candidate

Wazuh 5.0 "System service started" alerts from sysstat-collect, and the post-filter that drops everything else

What you see: on Ubuntu, the rules "System service started - {{service.name}}" and "System service stopped" fire for sysstat-collect.service again and again (reported every 10 minutes in wazuh/wazuh#40801). Why: both rules skip a fixed list of benign services, filter_benign_services, and sysstat-collect is not on it. What you can do: you cannot edit or turn off these rules; a rule in the standard space is read-only. You can add a post-filter that drops those events. Before you do: a post-filter keeps the events its check matches and drops all the others. In our test, a filter written as "match sysstat" kept only the matching event and dropped everything else. Write it as NOT (...).

Dong Nguyen, ATK New Technology · 28 October 2026 · measured on Wazuh 5.0.0-rc1 (commit aff7a0b), indexer OpenSearch 3.6.0, our test containers

5.0.0-rc1 is a release candidate, not the 5.0.0 GA release. Check this again on 5.0 GA. Our content was the "RC1 public" standard space, last synced on or before 15 October 2026; the two rules were last modified on 21 September 2026. Events were synthetic: no agents, no real services.

Stuck on this right now? Email the filter you wrote, a sample event and your Wazuh version to support@atkvn.com. We reply within 24 hours with what we find. Free. Remove hostnames, IPs and usernames first. Moving 4.x custom rules to 5.0? The Wazuh 5.0 Readiness Check reads your rules, decoders and lists and tells you, rule by rule, what has to change. USD 49.


A post-filter keeps what it matches

This is the part that can cost you data, so it comes first. We created a post-filter in the standard space, then pushed two events through the manager's engine: one sshd line and one sudo line. We counted what reached wazuh-events-v5-*. Two runs, one per filter, through the local ingest socket:

filter checksshd eventsudo event
string_equal($process.name, 'sshd')indexeddropped
NOT string_equal($process.name, 'sshd')droppedindexed

The filter that "matches sshd" did not drop sshd. It kept sshd and dropped the sudo event, which it did not mention at all. A filter written as contains($service.name, 'sysstat-collect') would keep the sysstat events and drop every other event the manager receives. To drop something, the check has to say NOT.

Nothing warns you. The filter passes the engine's validation, and the manager log shows only a normal reload line ([Orchestrator] Hot swapped namespace).

How to check your install

All three are read-only. Set these first:

IDX=http://localhost:9200   # your indexer. Ours had the security plugin off: plain HTTP, no login.
CURL="curl -s"              # normal install: CURL="curl -sk -u admin:YOUR_PASSWORD" and IDX=https://...

1. Is sysstat-collect on the rule's list?

$CURL "$IDX/_cat/indices/wazuh-threatintel-rules*?h=index"    # ours: wazuh-threatintel-rules-a
$CURL "$IDX/wazuh-threatintel-rules-a/_search?size=2" -H 'Content-Type: application/json' \
  -d '{"query":{"terms":{"document.id":["653da773-9e1b-5f39-a662-e5fafe3b1516","1e9dc454-ce81-5df4-a95b-2ab4d8bde5d3"]}}}' \
  | python3 -m json.tool | grep -E '"title"|"modified"|sysstat|filter_benign'

Those are the IDs of "System service started" and "System service stopped" in our content. If your content has a newer version of the rules, the IDs or the list may differ: search by title instead. No sysstat in the output means the service is not on the list.

2. Does your event match? (rule test, no agent needed)

$CURL -XPOST "$IDX/_plugins/_content_manager/logtest/detection" -H 'Content-Type: application/json' -d '{
  "space": "standard",
  "integration": "7f21dc98-7e96-579d-89bd-5e2cb6e51801",
  "input": { "event": { "action": "service-started", "category": ["host"] },
             "service": { "name": "sysstat-collect.service" } }
}'

7f21dc98… is the integration wazuh-generic-2, which holds the "started" rule. The "stopped" rule is in wazuh-generic-1, e7743934-ddd9-5e7d-8c07-a25a30352d1d: test service-stopped there. If you test it against the wrong integration, you get 0 matches and a false all-clear; that happened to us once.

3. Do you already have a filter?

$CURL "$IDX/wazuh-threatintel-policies-a/_search?size=10&_source=space.name,document.filters,user_overrides" | python3 -m json.tool

Ours returned five documents: one policy per space (draft, test, custom, standard) and a registry document, wazuh-user-overrides. Any ID under filters for the standard space is a filter that runs on every event. If someone added one and events went missing at the same time, read its check before anything else.

Why it happens

Both rules have the same shape:

condition: selection and not filter_benign_services
selection:
  event.action: [ service-started ]        # service-stopped in the second rule
  event.category|contains: host
filter_benign_services:
  service.name|contains: [ fprintd, geoclue, spice-vdagentd, fwupd-refresh, setvtrgb,
    systemd-localed, systemd-timedated, NetworkManager-dispatcher, modprobe, dmesg,
    packagekit, systemd-tmpfiles-clean, user-runtime-dir ]

Any service that starts or stops and is not on those 13 names matches. According to #40801, the default sysstat-collect.service on Ubuntu 24 and 26 runs every 10 minutes, so it matches every time it runs; we did not run sysstat ourselves. Both rules are level informational, tagged attack.t1543.

In 5.0 these rules live in the indexer, in the standard space that Wazuh ships and updates. You cannot change them in place. We tried to update the "started" rule through the content manager API and got:

Rule with ID '653da773-9e1b-5f39-a662-e5fafe3b1516' is not in draft space.

The request in #40801 is for Wazuh to add sysstat-collect to the list. When we checked on 28 October it was open, with no reply.

How to drop the sysstat events (tested, with one gap)

Create a post-filter in the standard space that keeps every event except a sysstat-collect start or stop:

$CURL -XPOST "$IDX/_plugins/_content_manager/filters" -H 'Content-Type: application/json' -d '{
  "space": "standard",
  "resource": {
    "name": "filter/drop-sysstat-collect/0",
    "metadata": { "title": "Drop sysstat-collect service start/stop events", "author": "you",
                  "description": "Benign timer service, see wazuh/wazuh#40801",
                  "references": [], "documentation": "" },
    "enabled": true,
    "type": "post-filter",
    "check": "NOT (contains($service.name, '"'"'sysstat-collect'"'"') AND (string_equal($event.action, '"'"'service-started'"'"') OR string_equal($event.action, '"'"'service-stopped'"'"')))"
  }
}'

(The '"'"' pieces put a single quote inside the shell's single-quoted body. The check the engine stores is NOT (contains($service.name, 'sysstat-collect') AND (string_equal($event.action, 'service-started') OR string_equal($event.action, 'service-stopped'))).)

$CURL "$IDX/wazuh-events-v5-*/_search?size=0" -H 'Content-Type: application/json' -d '{
  "query": { "range": { "@timestamp": { "gte": "now-5m" } } },
  "aggs": { "p": { "terms": { "field": "process.name", "missing": "(none)" } } } }'

With this filter on, our sshd and sudo test events were both indexed, and both produced findings about a minute later.

The gap: we did not see the filter drop a real sysstat event, because our lab could not produce one (see limits). What we did measure is that a NOT filter dropped the event it matched (the table above) and that this exact check kept events it does not match.

To remove it: $CURL -XDELETE "$IDX/_plugins/_content_manager/filters/FILTER_ID" -H 'Content-Type: application/json' -d '{"space":"standard"}'

Trade-off: a post-filter drops the event itself, not only the alert. Those sysstat start and stop events will not be in wazuh-events-v5-* either. If you need them kept for audit, this is the wrong tool. We did not test any way to suppress only the finding.

What we measured

Wazuh manager 5.0.0-rc1 (commit aff7a0b) and one indexer node, OpenSearch 3.6.0, security plugin off, our test containers, 28 October 2026. Times are UTC.

Rule test (logtest/detection)

integrationevent.actionservice.nameresult
wazuh-generic-2service-startedsysstat-collect.service1 of 31 rules matched: System service started
wazuh-generic-2service-startedfwupd-refresh.service (on the list)0 of 31
wazuh-generic-2service-startednginx.service1 of 31: System service started
wazuh-generic-1service-stoppedsysstat-collect.service1 of 50 rules matched: System service stopped
wazuh-generic-1service-stoppedfwupd-refresh.service (on the list)0 of 50

Filter test (events through the engine)

Each run: one line sshd[...]: Server listening on 0.0.0.0 port 222xx. (the system-auth decoder maps it to service-started) and one sudo line, pushed into the manager's engine through its local ingest socket (engine-ingest-http.sock, POST /events/enriched). We waited for the manager to reload after each filter change, then counted documents in wazuh-events-v5-* by process.name from the time of the push.

runpost-filter checksshdsudo
03:31:02string_equal($process.name, 'sshd')10
03:34:50NOT string_equal($process.name, 'sshd')01
03:36:59the sysstat check from the fix11

We removed every test filter afterwards; the standard policy's filters list was empty again.

Limits of what we measured

Not measured: which decoder produces service-started for a systemd service on Ubuntu. In our content, the inventory decoders (dbsync_services) produce only service-installed, service-deleted and config-modified. The decoders that produce service-started are for sshd, containerd, Redis, WebLogic, SQL Server, Sysmon for Linux, macOS, Windows events, Juniper and Sophos. So the sysstat events in #40801 come from content newer than ours or from a source we do not have, and we could not reproduce them end to end. The rule match above uses an event we wrote by hand in the normalized form the rule reads.

Also not measured: whether a filter in the standard space survives the next content update from Wazuh (we saw it recorded in the wazuh-user-overrides registry, which is how the content manager keeps user changes to the standard space, but we did not run an update), 5.0 GA, the dashboard, the security plugin's permissions for the content manager API, more than one indexer node, real agents and real load. Two filter runs per check, one event of each kind per run.

Moving to 5.0

Upgrading custom rules from 4.x? The Wazuh 5.0 Readiness Check reads your 4.x custom rules, decoders and lists and tells you, rule by rule, what has to change before 5.0. USD 49, one organisation.

Rather check one thing first? The free Wazuh 5.0 migration check runs in your browser; nothing is uploaded.

Send it

Free answer within 24 hours.

Paste the filter, rule or a few sample events below, with hostnames and IPs removed. We reply within 24 hours with what is wrong. If you want us to fix it: USD 149 per problem, paid only after it works on your system.

Or email support@atkvn.com.