Skip to content

Runbook: Wazuh Rule 510 "Trojaned version of file detected" β€” Coreutils False Positives

Status: Known benign pattern β€” CONFIRMED BENIGN, closed 2026-07-27 (integrity check run and clean; see History) Applies to: Wazuh manager hetzner-backend3, agent AlejandroM (id 032) β€” and any agent running a multi-call coreutils binary (Rust uutils, BusyBox, musl/toybox) Root cause: Rust uutils coreutils 0.8.0 (one 11 MB multi-call binary, 114 applets hardlinked to it; default on Ubuntu 25.10+ and 26.04 LTS) vs. Wazuh's per-utility trojan-signature DB Last verified: 2026-07-27 β€” Step 3 integrity check clean across all 4 coreutils packages; 20/20 flagged paths share one sha256; alert census 66 alerts / 4 scan cycles 07-24 β†’ 07-27 Owner: Security / Infra


TL;DR

If you see Wazuh rule 510 (Host-based anomaly detection event (rootcheck), level 7) firing Trojaned version of file '<path>' detected. Signature used: '...' (Generic) for standard coreutils binaries β€” on agent 032 always the same 10: ls, env, echo, chown, chmod, chgrp, cat, uname, date, md5sum, each under both /bin and /usr/bin β€” this is almost certainly a false positive from Wazuh's rootcheck trojan-signature database, not a compromise.

Quickest confirmation (one line, on the agent): all flagged paths hash identically, because they are symlinks to one multi-call binary.

sha256sum /bin/{ls,cat,echo,env,uname,chmod,chown,chgrp,date,md5sum} | awk '{print $1}' | sort -u

One hash β†’ benign, suppress via Option A. More than one β†’ this runbook does not apply; triage properly. Do not escalate on the alert alone.


Why this fires

Wazuh rootcheck scans system binaries and runs regex signatures from its trojan database (/var/ossec/etc/shared/rootkit_trojans.txt) against the strings inside each binary. The (Generic) signatures are broad heuristics, e.g.:

bash|^/bin/sh|file\.h|proc\.h|/dev/|^/bin/.*sh

Ordinary coreutils binaries legitimately contain strings like bash, /bin/sh, /dev/…, and file.h. So the generic signature matches benign binaries. Key indicators it's a false positive:

  • Signature label is (Generic) β€” a heuristic string match, not a named-rootkit detection. Note that some generic signatures still include a rootkit marker such as duarawkz as one regex alternative (the ls signature does); the label, not the regex text, tells you it was a heuristic match.
  • The flagged files are standard system utilities in /bin or /usr/bin.
  • Many coreutils binaries trip at once on the same agent, each under both /bin and /usr/bin.
  • Nothing suspicious about the paths (no /tmp, hidden dirs, odd locations).

Root cause: multi-call coreutils binary (Rust uutils β€” confirmed here; also BusyBox/musl)

When the entire coreutils suite trips on one host, the cause is that /bin/ls, /bin/cat, etc. are all symlinks to a single multi-call binary rather than individual GNU binaries. Rootcheck scans the same underlying binary once per applet name; that one binary contains the combined strings for every utility (bash, /bin/sh, /dev/…, file.h, proc.h), so it matches the generic signature for every applet. Rootcheck's trojan DB was written for classic per-utility GNU coreutils and is not aware of multi-call binaries.

Confirmed instance on AlejandroM (2026-07-27): every flagged utility is a symlink to a Rust uutils binary:

/bin/ls         -> ../lib/cargo/bin/coreutils/ls
/bin/cat        -> ../lib/cargo/bin/coreutils/cat
/bin/echo       -> ../lib/cargo/bin/coreutils/echo
/bin/chmod      -> ../lib/cargo/bin/coreutils/chmod
/usr/bin/env    -> ../lib/cargo/bin/coreutils/env
/usr/bin/uname  -> ../lib/cargo/bin/coreutils/uname
/usr/bin/md5sum -> ../lib/cargo/bin/coreutils/md5sum   # re-verified 2026-07-27, uutils 0.8.0

The .../lib/cargo/bin/coreutils/ target (Cargo = Rust's build tool) identifies this as uutils/coreutils, the Rust reimplementation of GNU coreutils, built as one multi-call binary (like BusyBox).

Exact mechanism, measured on the host (2026-07-27): the indirection is two hops, and the second one is what collapses everything onto a single file.

/bin/md5sum ─(/bin β†’ usr/bin)β†’ /usr/bin/md5sum ─symlinkβ†’ /usr/lib/cargo/bin/coreutils/md5sum
                                                          └─ hardlink β†’ inode 2884235
$ stat -c '%i links=%h size=%s %n' /usr/lib/cargo/bin/coreutils/{ls,cat,md5sum,date,uname}
2884235 links=115 size=11352352 /usr/lib/cargo/bin/coreutils/ls
2884235 links=115 size=11352352 /usr/lib/cargo/bin/coreutils/cat
2884235 links=115 size=11352352 /usr/lib/cargo/bin/coreutils/md5sum
2884235 links=115 size=11352352 /usr/lib/cargo/bin/coreutils/date
2884235 links=115 size=11352352 /usr/lib/cargo/bin/coreutils/uname

All 114 applets are hardlinks to one 11 MB inode (link count 115 = 114 applets + the dispatcher name). So rootcheck opens literally the same file 20 times and, unsurprisingly, gets the same verdict 20 times. That one 11 MB binary contains the string constants of every utility β€” bash, /bin/sh, /dev/…, file.h, proc.h β€” which is why utilities that as separate GNU binaries would be tiny and string-poor (echo, uname, date) now trip signatures written for a compromised ls.

Rootcheck's trojan DB assumes one distinct binary per utility and has no concept of a multi-call binary, so the false-positive count scales with however many coreutils names are in its list.

GNU coreutils is still installed here (gnu-coreutils 9.7-3ubuntu2) but ships under prefixed names (/usr/bin/gnuls, /usr/bin/gnucat, …), so it never occupies the paths rootcheck scans and never contributes to this alert.

Fleet impact: this host is Ubuntu 26.04 LTS, where uutils is the default /usr/bin coreutils. Because it is an LTS, expect this pattern to become the fleet norm rather than a rolling-release curiosity β€” worth fixing centrally (Option A) rather than per host. Any such host enrolled with server-style trojan-checking will produce this whole-suite noise.


Alert fingerprint β€” what the benign pattern looks like

Measured from 66 rule-510 alerts for agent 032 (2026-07-24 β†’ 2026-07-27). Match against this before doing anything else; a clean match is the benign pattern.

Exactly 10 utilities Γ— 2 paths = 20 alerts per scan cycle, always the same set:

Utility Paths Generic signature that matched
ls /bin/ls, /usr/bin/ls bash\\|^/bin/sh\\|dev/[^clu]\\|\.tmp/lsfile\\|duarawkz\\|/prof\\|/security\\|file\.h
env /bin/env, /usr/bin/env bash\\|^/bin/sh\\|file\.h\\|proc\.h\\|/dev/\\|^/bin/.*sh
echo, chown, chmod, chgrp, cat /bin/…, /usr/bin/… bash\\|^/bin/sh\\|file\.h\\|proc\.h\\|/dev/[^cl]\\|^/bin/.*sh
uname /bin/uname, /usr/bin/uname bash\\|^/bin/sh\\|file\.h\\|proc\.h\\|^/bin/.*sh
date, md5sum /bin/…, /usr/bin/… bash\\|^/bin/sh\\|file\.h\\|proc\.h\\|/dev/[^cfhlnrstuw]\\|^/bin/.*sh

Corroborating details:

  • The /bin + /usr/bin doubling is expected, not two findings. On merged-/usr systems /bin is a symlink to /usr/bin, so rootcheck scans the same file twice under two names. Alert pairs land ~0.3–0.5 s apart with consecutive rule.firedtimes.
  • Only coreutils members fire. Rootcheck's trojan list also covers ps, netstat, find, grep, sed, ssh, top, … β€” none of those alerted, because they are separate, non-uutils binaries. A hit on a non-coreutils binary is outside this pattern; triage it on its own merits.
  • ls is the one to not panic about. Its signature string literally contains duarawkz (a named rootkit marker) as one regex alternative. That does not mean the marker was found β€” the match is on a generic alternative such as bash or dev/[^clu]. See "When to escalate" for how to tell.
  • Cadence tracks boots, not a fixed schedule. Observed cycles: 2026-07-24 13:29 UTC, 2026-07-26 13:06 UTC, 2026-07-26 16:04 UTC, 2026-07-27 05:24 UTC. Two cycles ~3 h apart on 07-26 rules out a pure 12 h frequency timer β€” rootcheck re-scans on agent start, so a laptop that sleeps/reboots generates bursts. Rising alert volume here means "more reboots", not "more compromise".
  • rule.firedtimes resets to 1 at the start of most cycles (it is a manager-side counter, not a per-host severity signal). Do not read a high firedtimes as escalating badness.

Triage (≀ 5 minutes)

Step 1 β€” Pull the full list of flagged files for the agent

Against the Wazuh indexer (replace host/creds and agent.id):

curl -sk -u wazuh:PASSWORD \
  'https://WAZUH_INDEXER:9200/wazuh-alerts-4.x-*/_search' \
  -H 'Content-Type: application/json' -d '{
    "size": 0,
    "query": { "bool": { "must": [
      { "term": { "rule.id": "510" } },
      { "term": { "agent.id": "032" } }
    ]}},
    "aggs": { "files": { "terms": { "field": "data.file", "size": 200 } } }
  }' | python3 -m json.tool

βœ… Benign signal: every file is a standard utility in /bin or /usr/bin. 🚩 Investigate further: files in /tmp, /dev, /var/tmp, hidden dirs, home dirs, or unexpected paths.

Step 2 β€” Check for a multi-call coreutils binary (on the agent)

ls -l /bin/ls /bin/cat /bin/echo /usr/bin/env /usr/bin/uname /bin/chmod /usr/bin/md5sum /usr/bin/date
file /bin/ls /bin/cat

If these are symlinks to a single binary, the cause is confirmed β†’ benign, go to remediation Option A. Recognize the target:

  • .../lib/cargo/bin/coreutils/<name> β†’ Rust uutils coreutils (Ubuntu 25.10+ default). This is what AlejandroM showed on 2026-07-27.
  • busybox (or shared inode) β†’ BusyBox/toybox (Alpine-style).

Fastest single confirmation β€” all flagged binaries share one hash. Every applet resolves to the same multi-call binary, so sha256sum (which follows symlinks) returns an identical digest for all of them. One line proves the root cause:

sha256sum /bin/{ls,cat,echo,env,uname,chmod,chown,chgrp,date,md5sum} | awk '{print $1}' | sort -u

One hash out β†’ multi-call binary confirmed, benign. Ten different hashes β†’ these are per-utility GNU binaries and this runbook's root cause does not apply; go to Step 3 and treat the result seriously.

Also record which implementation and version you have β€” it belongs in the incident note:

/usr/bin/md5sum --version | head -1     # e.g. "md5sum (uutils coreutils) 0.8.0"

Step 3 β€” Verify package integrity (on the agent)

Confirm the binaries (and symlinks) match their distro package.

🐞 Two traps that produce a fake "clean" result β€” both hit on this host.

  1. dpkg -S /bin/ls returns nothing. On merged-/usr systems the dpkg database records /usr/bin/ls; the /bin alias is unowned. A naive loop then runs dpkg -V "", gets no output, and prints OK for all ten /bin/* paths without verifying anything. The loop below normalises /bin/… β†’ /usr/bin/… and treats an unresolved owner as UNKNOWN, never OK.
  2. dpkg -V coreutils is meaningless here. On this host coreutils is an Architecture: all meta package that ships no binaries at all. It verifies clean forever. The packages that actually matter are coreutils-from-uutils (ships the /usr/bin/* symlinks) and rust-coreutils (ships the multi-call binary under /usr/lib/cargo/bin/coreutils/).
for f in /bin/ls /usr/bin/ls /bin/env /usr/bin/env /bin/echo /usr/bin/echo \
         /bin/chown /usr/bin/chown /bin/chmod /usr/bin/chmod /bin/chgrp /usr/bin/chgrp \
         /bin/cat /usr/bin/cat /bin/uname /usr/bin/uname /bin/date /usr/bin/date \
         /bin/md5sum /usr/bin/md5sum $(readlink -f /usr/bin/md5sum); do
  [ -e "$f" ] || continue
  if command -v dpkg >/dev/null; then          # Debian/Ubuntu
    pkg=$(dpkg -S "$f" 2>/dev/null | cut -d: -f1)
    # merged-/usr: /bin/X is an unowned alias of /usr/bin/X
    [ -z "$pkg" ] && case "$f" in /bin/*) pkg=$(dpkg -S "/usr$f" 2>/dev/null | cut -d: -f1);; esac
    if   [ -z "$pkg" ];                          then echo "UNKNOWN (no owning package): $f"
    elif dpkg -V "$pkg" 2>/dev/null | grep -qF "$f"; then echo "CHANGED: $f  [$pkg]"
    else                                              echo "OK: $f  [$pkg]"; fi
  else                                          # RHEL/Rocky/CentOS
    rpm -Vf "$f" | grep -q . && echo "CHANGED: $f" || echo "OK: $f"
  fi
done

# Then verify each distinct owning package as a whole (no output = intact):
for p in $(dpkg -S /usr/bin/ls /usr/bin/md5sum "$(readlink -f /usr/bin/md5sum)" 2>/dev/null \
           | cut -d: -f1 | sort -u); do printf '%-24s: ' "$p"; dpkg -V "$p" || echo clean; done
  • All OK: and every package clean β†’ confirmed false positive β†’ remediate.
  • Any CHANGED: β†’ STOP. Treat as a potential integrity issue. See "When to escalate".
  • Any UNKNOWN: β†’ not a pass. A system binary with no owning package needs explaining before you close the alert.

Remediation

Apply on the manager hetzner-backend3, then systemctl restart wazuh-manager.

Option A β€” Multi-call coreutils host (Rust uutils / BusyBox): disable the trojan check (preferred)

The trojan DB is meaningless against a single multi-call binary, so disable the check for these hosts. Do this centrally via an agent group rather than editing the workstation by hand β€” it fixes every current and future host with the same profile at once.

# on the Wazuh manager (hetzner-backend3)
/var/ossec/bin/agent_groups -a -g workstations -q
/var/ossec/bin/agent_groups -a -i 032 -g workstations -q   # assign the agent to the group

Then in the group's shared config /var/ossec/etc/shared/workstations/agent.conf:

<agent_config>
  <rootcheck>
    <!-- Rust uutils / multi-call coreutils: per-utility trojan scan is noise. Verified benign. -->
    <check_trojans>no</check_trojans>
  </rootcheck>
</agent_config>

(Equivalent single-host form, if not using groups β€” put <rootcheck><check_trojans>no</check_trojans></rootcheck> directly in the agent's ossec.conf.)

Option B β€” Normal host: whitelist the specific binaries

In /var/ossec/etc/ossec.conf. This is the complete set of 20 paths observed firing on agent 032 β€” every utility appears under both /bin and /usr/bin, and omitting either half leaves the alert firing:

<rootcheck>
  <ignore>/bin/ls</ignore>        <ignore>/usr/bin/ls</ignore>
  <ignore>/bin/env</ignore>       <ignore>/usr/bin/env</ignore>
  <ignore>/bin/echo</ignore>      <ignore>/usr/bin/echo</ignore>
  <ignore>/bin/chown</ignore>     <ignore>/usr/bin/chown</ignore>
  <ignore>/bin/chmod</ignore>     <ignore>/usr/bin/chmod</ignore>
  <ignore>/bin/chgrp</ignore>     <ignore>/usr/bin/chgrp</ignore>
  <ignore>/bin/cat</ignore>       <ignore>/usr/bin/cat</ignore>
  <ignore>/bin/uname</ignore>     <ignore>/usr/bin/uname</ignore>
  <ignore>/bin/date</ignore>      <ignore>/usr/bin/date</ignore>
  <ignore>/bin/md5sum</ignore>    <ignore>/usr/bin/md5sum</ignore>
</rootcheck>

Earlier revisions of this runbook listed /bin/false, /usr/bin/diff and /usr/bin/grep here and omitted date and md5sum. Those three were never observed firing on agent 032; date and md5sum fire on every cycle. The list above is the corrected, evidence-backed set.

This option is a poor fit for uutils hosts: it enumerates 20 paths to work around one binary, and each future coreutils applet added to Wazuh's trojan DB reopens the noise. Prefer Option A there.

Option C β€” Silence rule 510 for a specific noisy agent (keep the check elsewhere)

In /var/ossec/etc/rules/local_rules.xml:

<group name="rootcheck,">
  <!-- Rootcheck generic-trojan FPs on coreutils, agent 032 β€” verified benign 2026-07-27 -->
  <rule id="100510" level="0">
    <if_sid>510</if_sid>
    <field name="agent.id">032</field>
    <description>Ignore rootcheck generic trojan FP (coreutils) on agent 032 β€” verified benign</description>
  </rule>
</group>

Prefer Option A when the host uses a multi-call coreutils binary (Rust uutils or BusyBox) β€” this is the actual root cause and the case confirmed here. Use Option B for targeted files on normal GNU-coreutils hosts, and Option C only when you must keep rule 510 fully active on other agents. Never blanket-ignore files you have not verified in Step 3.


When to escalate (this is NOT the benign pattern)

Treat as a real incident and investigate if any of these are true:

  • Package integrity check (Step 3) reports CHANGED: for a flagged file.

  • The flagged file is not a standard system utility, or lives in /tmp, /dev, /var/tmp, a home dir, or a hidden directory.

  • The signature is a named rootkit marker (e.g. duarawkz, t0rn, lrk), not (Generic) β€” and the binary actually contains that literal string.

⚠️ Do not escalate on ls for this reason. The ls signature in the benign pattern contains duarawkz as one alternative in its regex, but the alert is still labelled (Generic) and the match is on a generic alternative. Wazuh does not report which alternative matched, so check the binary directly before escalating:

strings -a "$(readlink -f /bin/ls)" | grep -aiE 'duarawkz|t0rn|lrk|\.tmp/lsfile'

No output β†’ generic match, benign. Any hit β†’ real finding, escalate.

  • Only one or two binaries trip while identical peer hosts are silent, and integrity can't be confirmed.

  • The set of flagged files differs from the fixed 20 in "Alert fingerprint" β€” e.g. a non-coreutils binary (ps, netstat, find, ssh, top) appears, or a utility fires under only one of its two paths. Both break the "one shared multi-call binary" explanation.

  • sha256sum across the flagged paths returns more than one distinct hash (Step 2) β€” the multi-call root cause does not hold.

  • Alerts correlate with other suspicious activity (new setuid files, unexpected listeners, modified cron/systemd units, rootcheck rule 511/512 hits).

Escalation path: isolate the host from the network, snapshot for forensics, run an offline integrity scan (compare hashes against known-good packages), and follow the standard IR process.


History / evidence

Incident: 2026-07-24 β†’ 2026-07-27 β€” CONFIRMED BENIGN

  • Host: agent AlejandroM (id 032), hostname ale, a developer workstation, reporting to manager hetzner-backend3 (the Wazuh server). Agent IP roamed 192.168.1.7 (07-24, 07-26) β†’ 10.11.6.14 (07-27 alert) β†’ 10.11.1.6 (07-27 live check), consistent with a laptop moving networks. The host is also on Tailscale (100.64.99.120) and runs Docker. The IP change is not an indicator β€” do not read it as agent spoofing.
  • Alerts: 66 alerts pulled (91 matched the query window), rule 510, level 7, all (Generic) signatures, across 4 scan cycles: 07-24 13:29, 07-26 13:06, 07-26 16:04, 07-27 05:24 (UTC). Every complete cycle produced the identical 20 alerts β€” 10 utilities Γ— /bin + /usr/bin: ls, env, echo, chown, chmod, chgrp, cat, uname, date, md5sum. All standard coreutils; no non-coreutils binary and no unusual path ever appeared. Full breakdown in "Alert fingerprint".
  • Verification performed (2026-07-27, directly on the host):
  • /usr/bin/md5sum β†’ lrwxrwxrwx root root … -> ../lib/cargo/bin/coreutils/md5sum (symlink, dated Mar 30 19:50 β€” install time, not recent tampering).
  • /usr/bin/md5sum --version β†’ md5sum (uutils coreutils) 0.8.0.
  • sha256sum /usr/bin/md5sum (follows the symlink to the multi-call binary) β†’ 48893b0fb21436b54619db80486e83ef39dfccaf1aefe83dfa00c02d6146e8c0. Useful as a cross-host baseline: on any other Ubuntu 25.10 host with uutils 0.8.0, all flagged paths should hash to this same value.
  • Step 3 integrity check β€” RUN AND CLOSED 2026-07-27. Host is Ubuntu 26.04 LTS.
  • All 21 checked paths (20 flagged + the resolved multi-call target) β†’ OK, none CHANGED.
  • Every owning package verifies clean: coreutils-from-uutils (0.0.0~ubuntu25, ships the /usr/bin/* symlinks), rust-coreutils (0.8.0-0ubuntu3, ships the multi-call binary), gnu-coreutils (9.7-3ubuntu2), coreutils (9.5-1ubuntu2+0.0.0~ubuntu25).
  • All 20 flagged paths hash to a single sha256 48893b0f…46e8c0; the 114 applets under /usr/lib/cargo/bin/coreutils/ are hardlinks to one inode (2884235, 11352352 bytes, link count 115).
  • Two flaws found in the earlier verification, both now fixed in Step 3:
    • dpkg -S /bin/<util> returns no owner on merged-/usr, so the original loop printed OK for all ten /bin/* paths while verifying nothing. Those passes were vacuous.
    • The earlier dpkg -V coreutils clean result was worthless: on this host coreutils is an Architecture: all meta package shipping no binaries. It was never going to report anything. (My prior note guessing the owner was rust-coreutils was also only half right β€” the /usr/bin symlinks belong to coreutils-from-uutils.)
  • Conclusion β€” CONFIRMED BENIGN, closed. The whole-suite trojan hits are Wazuh scanning one 11 MB multi-call Rust uutils binary 20 times under 20 different names. Package integrity is intact across all four coreutils packages, and the flagged set is exactly the coreutils subset of Wazuh's trojan list. Not a compromise. No action taken on the machine; fix applied on the manager (Option A β€” disable check_trojans for the workstation agent group).
  • Fleet note: Ubuntu 25.10+ ships Rust uutils coreutils by default. Expect this pattern to recur as other hosts upgrade; giving workstations/upgraded hosts a group with check_trojans off prevents it.

References

  • Wazuh rootcheck / rootkit detection: https://documentation.wazuh.com/current/user-manual/capabilities/malware-detection/index.html
  • Trojan signature DB on disk: /var/ossec/etc/shared/rootkit_trojans.txt
  • Rule 510 definition: /var/ossec/ruleset/rules/ (rootcheck rules)
  • uutils/coreutils (Rust coreutils, the multi-call binary at the root of this pattern): https://github.com/uutils/coreutils