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.
- ERROR: Failure to read rule 100020. Field 'user' is static.
- 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>