Upload files to "configs"
This commit is contained in:
@@ -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>
|
||||||
@@ -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>
|
||||||
Reference in New Issue
Block a user