瀏覽代碼

fix A2/A3 rule OR-trap: replace multi-<match> with single <regex> lookaheads

Root cause: multiple <match> elements in a Wazuh rule are ORed, not ANDed.
Rule 110311 (A2-01 RDP allowed) matched every FortiGate traffic log (~5M
hits/day) because type=traffic OR action=accept is almost always true.

Fixes:
- 110311: single pcre2 regex (?=.*dstport=3389)(?=.*action=accept) to
  require BOTH dstport=3389 AND action=accept in same raw log.
  Note: dstport/action are Wazuh static fields — <field> tag rejected;
  PCRE2 lookaheads in <regex> are the correct AND mechanism.
- 110320: single pcre2 regex for type=traffic AND threat_label=known-c2.
- 110331: pcre2 regex for ssl-login-success AND user=guest (AND fix).
- 110332: pcre2 regex for ssl-login-success AND previous_country=.
- 110333: pcre2 regex for ssl-login-success AND failed_attempts_before_success=.

Verified with wazuh-logtest: dstport=3389+accept fires 110311; dstport=161
only fires parent 81618.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Tum 1 天之前
父節點
當前提交
5370099102

+ 11 - 9
wazuh-docker/single-node/config/wazuh_cluster/rules/soc-a2-fortigate-fw-rules.xml

@@ -29,14 +29,14 @@
29 29
 
30 30
   <!-- A2-01: RDP traffic allowed through firewall
31 31
        Parent: 81603 (FortiGate base — NOT 81618 to avoid 3-level chain depth limit)
32
-       81618 uses if_sid=81603; chaining from 81618 creates a 3-level chain that
33
-       Wazuh 4.x does not evaluate. Use sibling pattern: if_sid=81603 + type=traffic.
34
-       Real data: dstport=3389, action="accept" confirmed in archives -->
32
+       Fix 2026-03-22: replaced multiple <match> (ORed) with single <regex> (AND).
33
+       Multiple <match> elements in Wazuh fire if ANY one matches — old rule
34
+       matched every traffic log via type=traffic or action=accept (~5M hits/day).
35
+       dstport/action are static Wazuh fields — cannot use <field>; use PCRE2
36
+       lookaheads in a single <regex> to require BOTH conditions in the raw log. -->
35 37
   <rule id="110311" level="12">
36 38
     <if_sid>81603</if_sid>
37
-    <match>type="traffic"|type=traffic</match>
38
-    <match>dstport=3389</match>
39
-    <match>action="accept"|action=accept</match>
39
+    <regex type="pcre2">(?=.*\bdstport=3389\b)(?=.*\baction="?accept"?)</regex>
40 40
     <description>A2-01 [PROD] FortiGate: RDP (3389) traffic allowed</description>
41 41
     <group>soc_prod,a2,rdp,</group>
42 42
     <mitre><id>T1021.001</id></mitre>
@@ -126,11 +126,13 @@
126 126
   </rule>
127 127
 
128 128
   <!-- A2-10: Traffic to known C2/malicious IP
129
-       Parent: 81603 (FortiGate base — sibling pattern, same fix as 110311) -->
129
+       Parent: 81603 (FortiGate base — sibling pattern, same fix as 110311)
130
+       Fix 2026-03-22: single <regex> with PCRE2 lookaheads ANDs both conditions.
131
+       threat_label is not extracted by Wazuh's built-in FortiGate decoder so
132
+       raw log matching via regex is required. -->
130 133
   <rule id="110320" level="8">
131 134
     <if_sid>81603</if_sid>
132
-    <match>type="traffic"|type=traffic</match>
133
-    <match>threat_label="known-c2"|threat_label=known-c2</match>
135
+    <regex type="pcre2">(?=.*\btype="?traffic"?)(?=.*threat_label="?known-c2"?)</regex>
134 136
     <description>A2-10 [PROD] FortiGate: traffic to known C2/malicious IP allowed</description>
135 137
     <group>soc_prod,a2,ioc,c2,</group>
136 138
     <mitre><id>T1071.001</id></mitre>

+ 8 - 7
wazuh-docker/single-node/config/wazuh_cluster/rules/soc-a3-fortigate-vpn-rules.xml

@@ -22,16 +22,19 @@
22 22
 
23 23
   Fix history:
24 24
     2026-03-19: Changed if_group=fortigate → if_sid; adapted A3-05 for IPsec tunnel-up
25
+    2026-03-22: 110331-110333 — replaced dual <match> (ORed) with single <regex> (both
26
+                conditions ANDed in one expression) to prevent false-positives when
27
+                SSL-VPN is enabled.
25 28
 -->
26 29
 <group name="soc_mvp,appendix_a,a3,vpn,fortigate,">
27 30
 
28 31
   <!-- A3-01: VPN authentication success by guest account (SSL-VPN)
29 32
        Requires SSL-VPN to be enabled on FortiGate.
30
-       Parent: 81603 (generic base — ssl-login-success is in the raw log) -->
33
+       Parent: 81603 (generic base — ssl-login-success is in the raw log)
34
+       Single <regex> ANDs both conditions (dual <match> would be ORed). -->
31 35
   <rule id="110331" level="12">
32 36
     <if_sid>81603</if_sid>
33
-    <match>ssl-login-success</match>
34
-    <match>user="guest"|user=guest</match>
37
+    <regex type="pcre2">ssl-login-success.*user="?guest"?</regex>
35 38
     <description>A3-01 [PROD] SSL-VPN: authentication success by guest account</description>
36 39
     <group>soc_prod,a3,vpn_guest,</group>
37 40
     <mitre><id>T1078.001</id></mitre>
@@ -41,8 +44,7 @@
41 44
        Requires SSL-VPN and FortiGate geo-login tracking -->
42 45
   <rule id="110332" level="12">
43 46
     <if_sid>81603</if_sid>
44
-    <match>ssl-login-success</match>
45
-    <match>previous_country=</match>
47
+    <regex type="pcre2">ssl-login-success.*previous_country=</regex>
46 48
     <description>A3-02 [PROD] SSL-VPN: success from different country than last login</description>
47 49
     <group>soc_prod,a3,vpn_geo,</group>
48 50
     <mitre><id>T1078</id></mitre>
@@ -52,8 +54,7 @@
52 54
        Requires SSL-VPN -->
53 55
   <rule id="110333" level="12">
54 56
     <if_sid>81603</if_sid>
55
-    <match>ssl-login-success</match>
56
-    <match>failed_attempts_before_success=</match>
57
+    <regex type="pcre2">ssl-login-success.*failed_attempts_before_success=</regex>
57 58
     <description>A3-03 [PROD] SSL-VPN: success after multiple prior failures (brute-force indicator)</description>
58 59
     <group>soc_prod,a3,vpn_bruteforce,</group>
59 60
     <mitre><id>T1110.001</id></mitre>