Nothing to configure to start receiving data
If authd is enabled — it is on every Wazuh manager by default — enrollment and telemetry both work immediately against the stock <remote><connection>secure</connection><port>1514</port></remote> and <auth> blocks every install ships with. Once the phone enrolls (see the main page's install steps) and starts sending, events land in the manager like any other agent's — you'll see them in the Wazuh dashboard's Discover view as JSON under a generic rule.
Everything past this point is about making those alerts specific — correct rule names, sensible severity levels, and one rule per event type — instead of leaving them as one undifferentiated JSON bucket.
Custom detection rules
Each event the agent sends is logged as x:wazuh-android-agent:<json>, for example:
{"event_type":"battery_status","timestamp":"2026-08-25T23:04:11Z","severity":6,"data":{"battery_pct":81,"charging":false}}Wazuh's built‑in JSON decoder parses this with no custom decoder needed. The rules below give each event type a real name and an appropriate alert level. Paste this on the manager (root, no manager restart needed until the last step):
sudo tee -a /var/ossec/etc/rules/local_rules.xml > /dev/null << 'XML'
<group name="wazuh_android_agent,">
<rule id="100101" level="3">
<if_sid>86600</if_sid>
<location>wazuh-android-agent</location>
<description>Wazuh Mobile Agent (Android): $(event_type)</description>
<group>wazuh_android_agent,</group>
</rule>
<rule id="100102" level="7">
<if_sid>100101</if_sid>
<field name="event_type">^app_installed$</field>
<description>Wazuh Mobile Agent (Android): app installed - $(data.package_name)</description>
</rule>
<rule id="100103" level="9">
<if_sid>100102</if_sid>
<field name="data.installer_package" negate="yes">^com\.android\.vending$</field>
<description>Wazuh Mobile Agent (Android): app installed from a non-Play-Store source - $(data.package_name)</description>
</rule>
<rule id="100104" level="10">
<if_sid>100101</if_sid>
<field name="event_type">^device_info$</field>
<field name="data.root_indicators">\w</field>
<description>Wazuh Mobile Agent (Android): root indicators present on device</description>
</rule>
<rule id="100105" level="5">
<if_sid>100101</if_sid>
<field name="event_type">^agent_crash$</field>
<description>Wazuh Mobile Agent (Android): agent crashed - $(data.exception)</description>
</rule>
<rule id="100106" level="10">
<if_sid>100101</if_sid>
<field name="event_type">^yara_match$</field>
<description>Wazuh Mobile Agent (Android): YARA rule match - $(data.rule_name) in $(data.package_name)</description>
</rule>
<rule id="100107" level="3">
<if_sid>100106</if_sid>
<field name="data.rule_name">^EICAR_Test_String_Present$</field>
<description>Wazuh Mobile Agent (Android): YARA self-test string matched (not malware) - pipeline check for $(data.package_name)</description>
</rule>
<rule id="100108" level="13">
<if_sid>100106</if_sid>
<field name="data.rule_name">^Metasploit_Android_Meterpreter_Stager$</field>
<description>Wazuh Mobile Agent (Android): known Metasploit Android stager signature matched in $(data.package_name)</description>
</rule>
</group>
XMLWhat each rule does:
| Rule | Level | Fires on |
|---|---|---|
| 100101 | 3 | Base rule — any agent event, chained under stock rule 86600 so it doesn't collide with that rule's own generic JSON match (see below). |
| 100102 | 7 | App installed. |
| 100103 | 9 | App installed from outside the Play Store — deliberately above its 100102 parent, since a sideload should outrank a routine install. |
| 100104 | 10 | Device‑posture check reports root indicators present. |
| 100105 | 5 | The agent app itself crashed. |
| 100106 | 10 | Any on‑device YARA match. |
| 100107 | 3 | The bundled EICAR self‑test string — deliberately below its 100106 parent; it's a known‑benign pipeline check, not a real match. |
| 100108 | 13 | The bundled Metasploit‑stager signature — deliberately above its parent; a known‑dangerous match should outrank the generic bucket. |
How the payload gets decoded
- No custom decoder needed. Wazuh's built‑in
jsondecoder auto‑parses thex:wazuh-android-agent:<json>log line. - Chain onto
if_sid 86600, don't write a top‑level rule. Wazuh's stock ruleset includes a very generic rule — id86600, nominally "Suricata messages" — that matches any JSON log containingtimestamp+event_typefields, which collides with this app's own field names. Chaining as a child rule is also just the correct general pattern for refining an existing vendor rule rather than fighting it. - Field‑name prefixing. The JSON‑decoded log's top‑level keys are addressed directly —
<field name="event_type">, notdata.event_type— while keys inside the nesteddataobject get exactly one prefix:<field name="data.package_name">. Easy to get backwards; confirmed both ways viawazuh-logtest. - Array fields match fine.
data.root_indicatorsis a JSON array on the wire (not a scalar), and<field name="data.root_indicators">\w</field>correctly matches non‑empty arrays without false‑positiving on empty ones.
Apply and validate
Restart the manager to load the new rules:
sudo systemctl restart wazuh-managerThen validate against a real event before trusting it live — paste a sample log line (like the battery_status example above) into:
/var/ossec/bin/wazuh-logtestor the equivalent PUT /logtest REST API call. Confirm the description interpolates real field values and the rule ID/level match the table above — decoder and rule precedence can vary across manager versions, so re‑verify on your own instance rather than assuming this file behaves identically everywhere.
Syslog mode, if the full agent protocol isn't reachable
The app's syslog‑forwarding mode needs no enrollment and no protocol reimplementation risk — useful on a network where port 1515/1514 isn't an option. Wazuh's remoted syslog listener is plaintext‑only; add it in ossec.conf:
<remote>
<connection>syslog</connection>
<port>514</port>
<protocol>tcp</protocol>
<allowed-ips>203.0.113.0/24</allowed-ips> <!-- restrict to your device/VPN subnet -->
</remote>For TLS, put stunnel in front of it rather than exposing plaintext syslog directly, and bind the <remote> block to 127.0.0.1 so it's only reachable through the tunnel:
[wazuh-android-syslog]
accept = 6514
connect = 127.0.0.1:514
cert = /etc/stunnel/wazuh-android.pemIn the app: set syslog port 6514 with TLS on. The custom rules above don't apply to syslog mode as written — they key off the agent‑protocol JSON payload shape, not RFC5424 syslog lines.