← Wazuh Mobile Agent
Server Setup

Getting the manager to make sense of what the app sends

The agent talks to a stock Wazuh manager out of the box — nothing below is required to receive data. It's here for the next step: turning generic JSON alerts into readable, correctly‑leveled ones, and for the syslog fallback if the full agent protocol isn't an option on your network.

0 config to just receive events 8 custom rules, optional Ports 1515 / 1514
Already enrolled and seeing events? You're done — skip straight to "Custom detection rules" below if generic JSON alerts aren't specific enough, or ignore this page entirely if they're fine as-is.
Default behavior

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.

Recommended

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):

/var/ossec/etc/rules/local_rules.xmlappend
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>
XML

What each rule does:

RuleLevelFires on
1001013Base rule — any agent event, chained under stock rule 86600 so it doesn't collide with that rule's own generic JSON match (see below).
1001027App installed.
1001039App installed from outside the Play Store — deliberately above its 100102 parent, since a sideload should outrank a routine install.
10010410Device‑posture check reports root indicators present.
1001055The agent app itself crashed.
10010610Any on‑device YARA match.
1001073The bundled EICAR self‑test string — deliberately below its 100106 parent; it's a known‑benign pipeline check, not a real match.
10010813The bundled Metasploit‑stager signature — deliberately above its parent; a known‑dangerous match should outrank the generic bucket.
Level isn't just "how loud the alert is" here — 100103/100107/100108 intentionally break the usual "child level ≥ parent level" habit in whichever direction the specific case actually warrants, instead of inheriting the parent's level by default.
Why it's written this way

How the payload gets decoded

  • No custom decoder needed. Wazuh's built‑in json decoder auto‑parses the x: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 — id 86600, nominally "Suricata messages" — that matches any JSON log containing timestamp + event_type fields, 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">, not data.event_type — while keys inside the nested data object get exactly one prefix: <field name="data.package_name">. Easy to get backwards; confirmed both ways via wazuh-logtest.
  • Array fields match fine. data.root_indicators is 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.
Last step

Apply and validate

Restart the manager to load the new rules:

sudo systemctl restart wazuh-manager

Then 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-logtest

or 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.

Alternative transport

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:

/var/ossec/etc/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:

/etc/stunnel/wazuh-android.conf
[wazuh-android-syslog]
accept = 6514
connect = 127.0.0.1:514
cert = /etc/stunnel/wazuh-android.pem

In 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.