Sasha vs. OpenCanary & Wazuh

Honeypots, Regex Rage, and Flamingo Flapping: Sasha vs. OpenCanary

Hello darlings, it’s your favorite pink hacker, Sasha the Dancing Flamingo. 🦩✨

This week I went beak-first into the world of Wazuh custom rules for OpenCanary honeypots. You’d think a flamingo would have better things to do (like salsa dancing on a beach), but nooo… I was knee-deep in <regex> tantrums and decoder drama.

What I Wanted

Simple: when attackers peck at my honeypots, I want Wazuh to tattle in style. Not just “interaction detected,” but:

  • Who they pretended to be (USERNAME)
  • What password they dared to try
  • Where they came from (IP + port)

If you’re going to flap at my honeypot, I want the full gossip.

The Feathers Got Ruffled

  • JSON vs. Custom Decoder — My logs decode as json. Great! Until I tried to get fancy and make a custom decoder. Suddenly, it was all “ERROR: invalid option” and “syntax error.” (The log parser clearly needs more flamingo sparkle.)
  • PCRE2 Drama — Did you know <pcre2> is NOT a tag? You have to use <regex type="pcre2">. Took me a few crashes to figure that one out.
  • Static Field Trap — Wazuh squawked:
    • ERROR: Failure to read rule 100020. Field 'user' is static.
      Apparently, some fields are sacred. You can’t just stomp over them like a flamingo at a conga line.
  • Regex Betrayal — My regex looked fine to me, but Wazuh shrieked anyway. Spoiler: I forgot to escape things. Always test regex outside before feeding it to your SIEM overlord.

Sweet, Sweet Victory

After many flaps and feathers flying, logtest finally purred:

**Phase 3: Completed filtering (rules).
id: '100020'
description: 'OpenCanary: user 'ftpuser' with password 'pi' from 87.120.191.13:49628 to 192.3.213.87:22'

YESSSS!!! Username, password, source IP, destination port - all in one glorious alert.
Exactly what this bird wanted!!

Lessons from a Flamingo

  • Don’t restart Wazuh Manager until your rules work. Use wazuh-logtest—it’ll save your feathers.
  • Respect Wazuh’s “static” fields. Some things are sacred.
  • Regex are tricksy beasts. Handle with care.
  • Celebrate small wins. Every rule that fires is another beat in the flamingo samba.

And for those of you thinking “Sasha, that’s cool, but I don’t have any OpenCanary honeypots”

🦩 Stay tuned to this flamingo-channel! Same bird-time, same bird-channel. I’ll keep flapping out more honeypot magic.

Sasha’s Ready-to-Use OpenCanary Ruleset 🎁🦩

If you’ve got OpenCanary logs landing in Wazuh (decoded as json), here’s a copy-paste set of working rules you can drop into your local_rules.xml.

<group name="opencanary,honeypot,">

  <!-- Base: any OpenCanary event with a username -->
  <rule id="100020" level="5">
    <decoded_as>json</decoded_as>
    <field name="logdata.USERNAME" type="pcre2">.+</field>
    <description>OpenCanary: user '$(logdata.USERNAME)' with password '$(logdata.PASSWORD)' from $(src_host):$(src_port) to $(dst_host):$(dst_port)</description>
  </rule>

  <!-- Common usernames -->
  <rule id="100021" level="8">
    <if_sid>100020</if_sid>
    <field name="logdata.USERNAME">^admin$</field>
    <description>OpenCanary: ADMIN attempt — user '$(logdata.USERNAME)' / pass '$(logdata.PASSWORD)'</description>
  </rule>

  <rule id="100022" level="8">
    <if_sid>100020</if_sid>
    <field name="logdata.USERNAME">^administrator$</field>
    <description>OpenCanary: ADMINISTRATOR attempt — user '$(logdata.USERNAME)' / pass '$(logdata.PASSWORD)'</description>
  </rule>

  <rule id="100023" level="8">
    <if_sid>100020</if_sid>
    <field name="logdata.USERNAME">^root$</field>
    <description>OpenCanary: ROOT attempt — user '$(logdata.USERNAME)' / pass '$(logdata.PASSWORD)'</description>
  </rule>

  <!-- Special usernames -->
  <rule id="100024" level="8">
    <if_sid>100020</if_sid>
    <field name="logdata.USERNAME">^emartin$</field>
    <description>OpenCanary: Emartin attempt — user '$(logdata.USERNAME)' / pass '$(logdata.PASSWORD)'</description>
  </rule>

  <!-- Service account patterns -->
  <rule id="100025" level="7">
    <if_sid>100020</if_sid>
    <field name="logdata.USERNAME" type="pcre2">^(support|no-reply|test01|developer|gateway)$</field>
    <description>OpenCanary: Service account attempt — user '$(logdata.USERNAME)' / pass '$(logdata.PASSWORD)'</description>
  </rule>

  <!-- Format-based patterns -->
  <rule id="100026" level="6">
    <if_sid>100020</if_sid>
    <field name="logdata.USERNAME" type="pcre2">^\d{5}$</field>
    <description>OpenCanary: Numeric username attempt (ZIP?) — user '$(logdata.USERNAME)' / pass '$(logdata.PASSWORD)'</description>
  </rule>

  <rule id="100027" level="7">
    <if_sid>100020</if_sid>
    <field name="logdata.USERNAME" type="pcre2">^[^@]+@[^"]+$</field>
    <description>OpenCanary: Email-format username — user '$(logdata.USERNAME)' / pass '$(logdata.PASSWORD)'</description>
  </rule>

  <rule id="100028" level="7">
    <if_sid>100020</if_sid>
    <field name="logdata.USERNAME" type="pcre2">^[a-f0-9]{15,}$</field>
    <description>OpenCanary: Hash-like username — user '$(logdata.USERNAME)' / pass '$(logdata.PASSWORD)'</description>
  </rule>

  <!-- Domain-based usernames -->
  <rule id="100032" level="7">
    <if_sid>100020</if_sid>
    <field name="logdata.USERNAME" type="pcre2">^.*\.(com|org|net|edu)$</field>
    <description>OpenCanary: Domain-based username — user '$(logdata.USERNAME)' / pass '$(logdata.PASSWORD)'</description>
  </rule>

  <!-- Structured usernames -->
  <rule id="100033" level="8">
    <if_sid>100020</if_sid>
    <field name="logdata.USERNAME" type="pcre2">^[^:]+:[^:]+:[^"]+$</field>
    <description>OpenCanary: Structured username format — user '$(logdata.USERNAME)' / pass '$(logdata.PASSWORD)'</description>
  </rule>

  <!-- Correlation: frequency from same src_host -->
  <rule id="100029" level="10" frequency="10" timeframe="120">
    <if_matched_sid>100020</if_matched_sid>
    <same_field name="src_host" />
    <description>OpenCanary: High frequency attempts from same source host</description>
  </rule>

  <rule id="100031" level="8" frequency="3" timeframe="300">
    <if_matched_sid>100020</if_matched_sid>
    <same_field name="src_host" />
    <description>OpenCanary: Multiple reconnaissance from same source host</description>
  </rule>

  <!-- Credential stuffing campaign -->
  <rule id="100034" level="12" frequency="20" timeframe="600">
    <if_matched_sid>100021</if_matched_sid>
    <if_matched_sid>100022</if_matched_sid>
    <if_matched_sid>100023</if_matched_sid>
    <same_field name="src_host" />
    <description>OpenCanary: Intensive credential stuffing campaign detected</description>
  </rule>

</group>