Upload files to "configs"

This commit is contained in:
2026-07-31 16:56:01 +00:00
parent 55ff61462c
commit 4f413914dd
2 changed files with 615 additions and 0 deletions
+194
View File
@@ -0,0 +1,194 @@
<!-- Local Decoders -->
<!-- Modify it at your will. -->
<!-- Copyright (C) 2015, Wazuh Inc. -->
<!--
================================================================
Local decoders for Wittenberger.us Homelab
Allowed static fields (Wazuh reference):
location - where the log came from (only on FTS)
srcuser - extracts the source username
dstuser - extracts the destination (target) username
user - alias to dstuser (only one of the two can be used)
srcip - source IP
dstip - destination IP
srcport - source port
dstport - destination port
protocol - protocol
id - event ID
url - URL of the event
action - event action (deny, drop, accept, etc.)
status - event status (success, failure, etc.)
extra_data - any extra data
Custom fields extracted by decoders in this file:
src_subnet - first three octets of srcip (for /24 correlation)
Deploy procedure:
1. sudo cp local_decoder.xml /var/ossec/etc/decoders/
2. sudo /var/ossec/bin/wazuh-analysisd -t
3. sudo systemctl restart wazuh-manager
Author: Chris Berger
Last updated: 2026-07-31
================================================================
-->
<!--
================================================================
pfSense: wrapped filter log parser
Parses pfSense filterlog entries that arrive wrapped in
additional syslog headers from the pfSense box. Uses chained
child decoders with offset="after_regex" to extract fields
from the CSV-like filter log format.
All child decoders share the name "pfsense-wrapped-fields"
intentionally - Wazuh supports multiple same-named children
when each uses offset="after_regex" to continue from the
previous child's match position.
Referenced by rules 87699, 87761, 87762.
================================================================
-->
<decoder name="pfsense-wrapped">
<prematch>filterlog</prematch>
</decoder>
<!-- First extraction: rule ID and action -->
<decoder name="pfsense-wrapped-fields">
<parent>pfsense-wrapped</parent>
<regex>filterlog\S* \S*,\S*,\S*,(\S*),\S*,\S*,(\S*),</regex>
<order>id,action</order>
</decoder>
<!-- Second extraction: protocol and IP addresses -->
<decoder name="pfsense-wrapped-fields">
<parent>pfsense-wrapped</parent>
<regex offset="after_regex">\S*,\S*,\S*,\S*,\S*,\S*,\S*,\S*,\S*,(\S*),\S*,(\S*),(\S*),</regex>
<order>protocol,srcip,dstip</order>
</decoder>
<!-- Third extraction: source and destination ports -->
<decoder name="pfsense-wrapped-fields">
<parent>pfsense-wrapped</parent>
<regex offset="after_regex">(\d*),(\d*),\S*</regex>
<order>srcport,dstport</order>
</decoder>
<!-- Fourth extraction: packet length -->
<decoder name="pfsense-wrapped-fields">
<parent>pfsense-wrapped</parent>
<regex offset="after_regex">datalength=(\S*)|(\d*)</regex>
<order>length</order>
</decoder>
<!--
================================================================
Mailcow journald unwrap (reserved for future use)
Strips the outer journald syslog envelope from mailcow container
logs to expose the inner Postfix log for additional decoding.
Currently not actively referenced by rules; the
mailcow-postfix-sasl-fail decoder handles the specific
Postfix SASL case directly.
================================================================
-->
<decoder name="mailcow-journald-unwrap">
<prematch>postfix\(\d+\): \w+ \d+ \d+:\d+:\d+ \w+ \.+\(\d+\):</prematch>
</decoder>
<decoder name="mailcow-journald-unwrap-child">
<parent>mailcow-journald-unwrap</parent>
<regex offset="after_parent">\.+</regex>
<order>extra_data</order>
</decoder>
<!--
================================================================
Gitea: authentication event parser
Parses Gitea log lines from the syslog output. Referenced by
all Gitea rules (100400-100470 in local_rules.xml).
Sample logs this parses:
2026/05/29 14:19:59 routers/web/auth/auth.go:309:SignInPost()
[W] Failed authentication attempt for admin from 192.0.2.100
2026/05/29 14:19:59 HTTPRequest [I] router: completed POST
/user/login for 192.0.2.100
================================================================
-->
<decoder name="gitea">
<program_name>gitea</program_name>
</decoder>
<!-- gitea-auth-fail: extracts username and source IP from failed login events -->
<decoder name="gitea-auth-fail">
<parent>gitea</parent>
<prematch>Failed authentication attempt</prematch>
<regex>Failed authentication attempt for (\S+) from (\d+.\d+.\d+.\d+)</regex>
<order>user, srcip</order>
</decoder>
<!--
================================================================
Mailcow Dovecot: rip= field extractor
Extracts source IP from any Dovecot event containing "rip=IP,"
in the log line. Universally useful because rip= appears in
successful logins, disconnects, no-auth probes, and every other
connection-related Dovecot event.
Named "success" for historical reasons; actually captures on
ALL Dovecot rip= events. Consider renaming to
mailcow-dovecot-rip in a future refactor.
Referenced by rules 100200-100206 (suppression, recon) and
indirectly by rule 100311 via rule 100204 chain.
================================================================
-->
<decoder name="mailcow-dovecot-success">
<parent>dovecot</parent>
<regex>rip=(\S+),</regex>
<order>srcip</order>
</decoder>
<!--
================================================================
Mailcow Postfix SASL failure decoder
Extracts srcip, src_subnet, and dstuser from mailcow-wrapped
Postfix SASL authentication failure logs. The double timestamp
wrapping (journald + inner container timestamp) requires PCRE2
to match anywhere in the string rather than anchoring to start.
Fields extracted:
srcip full IPv4 source address (e.g., 192.0.2.100)
src_subnet first three octets (e.g., 192.0.2)
dstuser the sasl_username the attacker attempted
src_subnet enables /24 correlation via
<same_field>src_subnet</same_field> in rules like 100314.
Append ".0/24" in rule descriptions for human-readable notation.
Sample log this matches:
Jul 31 02:00:03 mailcow postfix[142]: Jul 30 21:00:03
e239f49ad459 postfix/submission/smtpd[19867]: warning:
unknown[92.118.39.225]: SASL LOGIN authentication failed:
(reason unavailable), sasl_username=ftpuser
Referenced by shipped rule 3332 and custom rules 100310-100316.
================================================================
-->
<decoder name="mailcow-postfix-sasl-fail">
<parent>postfix</parent>
<prematch type="pcre2">postfix/\S+smtpd\S*: warning: \S+: SASL </prematch>
<regex type="pcre2">unknown\[((\d+\.\d+\.\d+)\.\d+)\]: SASL \S+ authentication failed.*sasl_username=(\S+)</regex>
<order>srcip, src_subnet, dstuser</order>
</decoder>
+421
View File
@@ -0,0 +1,421 @@
<!-- Local rules -->
<!-- Modify it at your will. -->
<!-- Copyright (C) 2015, Wazuh Inc. -->
<!--
================================================================
Local rules for Wittenberger.us Homelab and CyberForce prep
Rule ID ranges:
87699-87762 pfSense (overrides shipped IDs, needed for
wrapped-log correlation)
100000-100099 SSH
100100-100199 Attack / reputation lookups
100200-100299 Dovecot (mailcow noise suppression + recon)
100300-100399 Postfix / mail correlation
100400-100499 Gitea
100500-100599 (reserved)
Deploy procedure:
1. sudo cp local_rules.xml /var/ossec/etc/rules/
2. sudo /var/ossec/bin/wazuh-analysisd -t
3. sudo systemctl restart wazuh-manager
Author: Chris Berger
Last updated: 2026-07-31
================================================================
-->
<!--
================================================================
SSH: authentication and login events
Range: 100000-100099
================================================================
-->
<group name="local,syslog,sshd,">
<!-- 100001: example SSH failure rule (placeholder, from shipped local_rules.xml)
Sample log: Dec 10 01:02:02 host sshd[1234]: Failed none for root from 1.1.1.1 port 1066 ssh2 -->
<rule id="100001" level="5">
<if_sid>5716</if_sid>
<srcip>1.1.1.1</srcip>
<description>sshd: authentication failed from IP 1.1.1.1.</description>
<group>authentication_failed,pci_dss_10.2.4,pci_dss_10.2.5,</group>
</rule>
</group>
<!--
================================================================
pfSense: firewall drop correlation
Range: 87699-87762 (overrides shipped IDs)
These rules extend Wazuh's shipped pfSense correlation to work
with the wrapped log format arriving from the homelab pfSense
box. Require the pfsense-wrapped decoder chain in
local_decoder.xml.
================================================================
-->
<group name="local,pfsense,firewall,">
<!-- 87699: bridge rule - makes pfsense-wrapped decoder trigger the pfSense rule chain -->
<rule id="87699" level="0">
<decoded_as>pfsense-wrapped</decoded_as>
<description>pfSense wrapped syslog parent rule.</description>
</rule>
<!-- 87761: individual firewall block event (any action=block) -->
<rule id="87761" level="5">
<if_sid>87699</if_sid>
<action>block</action>
<description>pfSense firewall drop event (wrapped).</description>
<group>pfsense,firewall_block,pci_dss_1.4,gpg13_4.12,hipaa_164.312.a.1,nist_800_53_SC.7,tsc_CC6.7,tsc_CC6.8,</group>
</rule>
<!-- 87762: brute force / scan correlation (18 blocks in 45s from same source, then ignore that source for 4min) -->
<rule id="87762" level="10" frequency="18" timeframe="45" ignore="240">
<if_matched_sid>87761</if_matched_sid>
<same_srcip />
<description>Multiple pfSense firewall block events from same source (wrapped).</description>
<mitre>
<id>T1110</id>
</mitre>
<group>pfsense,</group>
</rule>
</group>
<!--
================================================================
Attack / reputation lookups
Range: 100100-100199
IP reputation matches against external threat intel feeds.
Requires CDB lists in /var/ossec/etc/lists/ maintained via
wazuh-makelists.
================================================================
-->
<group name="attack,">
<!-- 100100: AlienVault OTX blacklist match on any web/attack event -->
<rule id="100100" level="10">
<if_group>web|attack|attacks</if_group>
<list field="srcip" lookup="address_match_key">etc/lists/blacklist-alienvault</list>
<description>IP address found in AlienVault reputation database.</description>
</rule>
</group>
<!--
================================================================
Dovecot: mailcow noise suppression and reconnaissance detection
Range: 100200-100299
Suppression rules (100200-100203) prevent expected mailcow
internal traffic (watchdog, health checks, own-mailbox polling)
from creating alert noise.
Recon detection (100205-100206) catches port scanning against
IMAP/POP3/managesieve endpoints - the "no auth attempts"
signature that separates scanning from actual attacks.
================================================================
-->
<group name="dovecot,local,">
<!-- 100200: mailcow watchdog auth attempts against watchdog@invalid - suppress -->
<rule id="100200" level="0">
<if_sid>9705</if_sid>
<match>watchdog@invalid</match>
<description>Dovecot: mailcow watchdog health check (ignored)</description>
</rule>
<!-- 100201: Docker internal IMAP probe disconnects (172.22.1.x range) - suppress -->
<rule id="100201" level="0">
<if_sid>9707</if_sid>
<match>rip=172.22.1.</match>
<description>Dovecot: mailcow watchdog IMAP probe disconnect (ignored)</description>
</rule>
<!-- 100202: administrator's own mailbox routine disconnects - suppress -->
<rule id="100202" level="0">
<if_sid>9706</if_sid>
<match>imap(chris@wittenberger.us)</match>
<description>Dovecot: own-mailbox routine session disconnect (ignored)</description>
</rule>
<!-- 100203: managesieve health check disconnects (no auth attempts) - suppress -->
<rule id="100203" level="0">
<if_sid>9706</if_sid>
<match>managesieve-login: Disconnected: Connection closed (no auth attempts</match>
<description>Mailcow watchdog managesieve healthcheck - suppressed</description>
</rule>
<!-- 100205: no-auth disconnect primitive
Level 0 - individual events silent, correlation happens in 100206 -->
<rule id="100205" level="0">
<if_sid>9700</if_sid>
<match>no auth attempts</match>
<description>Dovecot: connection with no auth attempt from $(srcip)</description>
<group>dovecot,recon,</group>
</rule>
<!-- 100206: aggressive port scanning
Fires when a single source triggers 20 no-auth disconnects
within 5 minutes. Separates one-off scanners (Shodan, Censys)
from active reconnaissance sessions. -->
<rule id="100206" level="8" frequency="20" timeframe="300">
<if_matched_sid>100205</if_matched_sid>
<same_srcip />
<description>Dovecot: aggressive port scanning from $(srcip) - possible attack precursor</description>
<group>dovecot,recon,attack,</group>
</rule>
</group>
<!--
================================================================
Dovecot: successful login primitive (do not delete)
Range: 100204
Rule 100204 is intentionally suppressed (level 0) but MUST
continue to fire when 9701 matches. Rule 100311 (mail
compromise correlation) uses <if_sid>100204</if_sid> to detect
successful logins after brute force.
================================================================
-->
<group name="dovecot,authentication_success,">
<!-- 100204: successful Dovecot login - suppressed alert, still fires for correlation -->
<rule id="100204" level="0" overwrite="no">
<if_sid>9701</if_sid>
<description>Dovecot successful login - suppressed (routine IMAP polling)</description>
</rule>
</group>
<!--
================================================================
Gitea: authentication, account lifecycle, and repository events
Range: 100400-100499
Rule map:
100400 Gitea event (parent, level 0)
100401 router polling - suppressed
100402 router completed request - suppressed
100410 failed authentication attempt
100411 brute force (5+ failures in 2min)
100420 new user created
100421 user deleted
100430 SSH key added
100431 SSH key deleted
100440 access token activity
100450 password reset / recovery
100460 repository deleted
100470 2FA event
All rules chain off the gitea decoder (see local_decoder.xml).
================================================================
-->
<group name="gitea,">
<!-- 100400: parent rule - any Gitea event, gates all children -->
<rule id="100400" level="0">
<decoded_as>gitea</decoded_as>
<description>Gitea event (parent)</description>
</rule>
<!-- 100401, 100402: suppress high-volume HTTP request logs -->
<rule id="100401" level="0">
<if_sid>100400</if_sid>
<match>router: polling</match>
<description>Gitea: router polling - suppressed</description>
</rule>
<rule id="100402" level="0">
<if_sid>100400</if_sid>
<match>router: completed</match>
<description>Gitea: router completed request - suppressed</description>
</rule>
<!-- 100410: individual failed login -->
<rule id="100410" level="5">
<if_sid>100400</if_sid>
<match>Failed authentication attempt</match>
<description>Gitea: failed authentication attempt</description>
<group>authentication_failed,</group>
<mitre>
<id>T1110</id>
</mitre>
</rule>
<!-- 100411: brute force - 5+ failed logins in 2 minutes -->
<rule id="100411" level="10" frequency="5" timeframe="120">
<if_matched_sid>100410</if_matched_sid>
<description>Gitea: possible brute force (5+ failed logins in 2 min)</description>
<group>authentication_failures,</group>
<mitre>
<id>T1110</id>
</mitre>
</rule>
<!-- 100420, 100421: account lifecycle -->
<rule id="100420" level="5">
<if_sid>100400</if_sid>
<match>new user signed up|created user</match>
<description>Gitea: new user account created</description>
</rule>
<rule id="100421" level="7">
<if_sid>100400</if_sid>
<match>deleted user|DeleteUser</match>
<description>Gitea: user account deleted</description>
</rule>
<!-- 100430, 100431: SSH key management (grants repo access - worth monitoring) -->
<rule id="100430" level="5">
<if_sid>100400</if_sid>
<match>add public key|added SSH key|AddPublicKey</match>
<description>Gitea: SSH key added to account</description>
</rule>
<rule id="100431" level="3">
<if_sid>100400</if_sid>
<match>delete public key|deleted SSH key|DeletePublicKey</match>
<description>Gitea: SSH key removed from account</description>
</rule>
<!-- 100440: access token activity (alternate auth for Gitea) -->
<rule id="100440" level="5">
<if_sid>100400</if_sid>
<match>access token|AccessToken</match>
<description>Gitea: access token activity</description>
</rule>
<!-- 100450: password reset / recovery flows -->
<rule id="100450" level="5">
<if_sid>100400</if_sid>
<match>ResetPasswd|recover_account</match>
<description>Gitea: password reset / account recovery</description>
</rule>
<!-- 100460: repository deletion (destructive, higher severity) -->
<rule id="100460" level="7">
<if_sid>100400</if_sid>
<match>repository deleted|DeleteRepository</match>
<description>Gitea: repository deleted</description>
</rule>
<!-- 100470: 2FA state changes -->
<rule id="100470" level="5">
<if_sid>100400</if_sid>
<match>TwoFactor|two-factor|TOTP</match>
<description>Gitea: 2FA event</description>
</rule>
</group>
<!--
================================================================
Postfix / mail: brute force and compromise correlation
Range: 100300-100399
All rules depend on the mailcow-postfix-sasl-fail decoder for
srcip, src_subnet, and dstuser extraction (see local_decoder.xml).
Rule 100311 also requires rule 100204 (Dovecot success suppressed
alert) to detect successful logins after brute force.
Rule map by attack shape:
100310 Single-IP brute force (same_srcip)
100311 Compromise - success after brute force from same IP
100312 Distributed against one user (same_user, different_srcip)
100313 Persistent single-source (100310 3x in 24h)
100314 Subnet coordination (same_field src_subnet, 15/hour)
100315 Confirmed credential stuffing (same_user, 10 IPs, 10min)
100316 Volume alarm (50 failures/hour, no source correlation)
================================================================
-->
<group name="local,mail,postfix,">
<!-- 100310: single-source brute force
5 SASL failures from same IP within 2 minutes -->
<rule id="100310" level="10" frequency="5" timeframe="120">
<if_matched_sid>3332</if_matched_sid>
<same_srcip />
<description>Postfix SASL: brute force login attempt from $(srcip)</description>
<group>authentication_failed,brute_force,</group>
</rule>
<!-- 100311: account compromise
Successful Dovecot login (100204) from same IP that just
triggered brute force (100310), within 2 minutes -->
<rule id="100311" level="14" timeframe="120">
<if_matched_sid>100310</if_matched_sid>
<if_sid>100204</if_sid>
<same_srcip />
<description>Mail: successful login after brute force from $(srcip) - ACCOUNT COMPROMISED</description>
<group>authentication_success,brute_force_success,attack,</group>
</rule>
<!-- 100312: distributed brute force against one user
5 failures targeting same username from different IPs in 5min.
Catches credential stuffing that rotates IPs to evade
same_srcip rules. -->
<rule id="100312" level="10" frequency="5" timeframe="300">
<if_matched_sid>3332</if_matched_sid>
<same_user />
<different_srcip />
<description>Postfix SASL: distributed brute force targeting user $(dstuser)</description>
<group>authentication_failed,brute_force,distributed,</group>
</rule>
<!-- 100313: persistent single-source brute force
Same IP triggers 100310 three or more times in 24 hours.
Confirms an ongoing adversary rather than a one-off scan. -->
<rule id="100313" level="12" frequency="3" timeframe="86400">
<if_matched_sid>100310</if_matched_sid>
<same_srcip />
<description>Postfix SASL: persistent brute force from $(srcip) - 3+ bursts in 24h</description>
<group>authentication_failed,brute_force,persistent,</group>
</rule>
<!-- 100314: subnet-coordinated brute force
15+ failures from same /24 subnet within 1 hour.
Catches botnets rotating through hosting provider ranges.
Requires src_subnet field from mailcow-postfix-sasl-fail. -->
<rule id="100314" level="12" frequency="15" timeframe="3600">
<if_matched_sid>3332</if_matched_sid>
<same_field>src_subnet</same_field>
<description>Postfix SASL: coordinated brute force from subnet $(src_subnet).0/24</description>
<group>authentication_failed,brute_force,subnet_scan,</group>
</rule>
<!-- 100315: confirmed credential stuffing
Higher-confidence version of 100312. 10 attempts against
same user from 10 different IPs in 10 minutes. Very low
false positive rate. -->
<rule id="100315" level="10" frequency="10" timeframe="600">
<if_matched_sid>3332</if_matched_sid>
<same_user />
<different_srcip />
<description>Postfix SASL: credential stuffing against user $(dstuser) from multiple IPs</description>
<group>authentication_failed,brute_force,distributed,credential_stuffing,</group>
</rule>
<!-- 100316: high volume attack detection
50 SASL failures in 1 hour regardless of source or target.
Catches attacks that rotate BOTH IPs and usernames. -->
<rule id="100316" level="8" frequency="50" timeframe="3600">
<if_matched_sid>3332</if_matched_sid>
<description>Postfix SASL: high volume of authentication failures (50+ in 1h)</description>
<group>authentication_failed,brute_force,volume,</group>
</rule>
</group>