Kaynağa Gözat

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 gün önce
ebeveyn
işleme
5370099102

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

29
 
29
 
30
   <!-- A2-01: RDP traffic allowed through firewall
30
   <!-- A2-01: RDP traffic allowed through firewall
31
        Parent: 81603 (FortiGate base — NOT 81618 to avoid 3-level chain depth limit)
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
   <rule id="110311" level="12">
37
   <rule id="110311" level="12">
36
     <if_sid>81603</if_sid>
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
     <description>A2-01 [PROD] FortiGate: RDP (3389) traffic allowed</description>
40
     <description>A2-01 [PROD] FortiGate: RDP (3389) traffic allowed</description>
41
     <group>soc_prod,a2,rdp,</group>
41
     <group>soc_prod,a2,rdp,</group>
42
     <mitre><id>T1021.001</id></mitre>
42
     <mitre><id>T1021.001</id></mitre>
126
   </rule>
126
   </rule>
127
 
127
 
128
   <!-- A2-10: Traffic to known C2/malicious IP
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
   <rule id="110320" level="8">
133
   <rule id="110320" level="8">
131
     <if_sid>81603</if_sid>
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
     <description>A2-10 [PROD] FortiGate: traffic to known C2/malicious IP allowed</description>
136
     <description>A2-10 [PROD] FortiGate: traffic to known C2/malicious IP allowed</description>
135
     <group>soc_prod,a2,ioc,c2,</group>
137
     <group>soc_prod,a2,ioc,c2,</group>
136
     <mitre><id>T1071.001</id></mitre>
138
     <mitre><id>T1071.001</id></mitre>

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

22
 
22
 
23
   Fix history:
23
   Fix history:
24
     2026-03-19: Changed if_group=fortigate → if_sid; adapted A3-05 for IPsec tunnel-up
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
 <group name="soc_mvp,appendix_a,a3,vpn,fortigate,">
29
 <group name="soc_mvp,appendix_a,a3,vpn,fortigate,">
27
 
30
 
28
   <!-- A3-01: VPN authentication success by guest account (SSL-VPN)
31
   <!-- A3-01: VPN authentication success by guest account (SSL-VPN)
29
        Requires SSL-VPN to be enabled on FortiGate.
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
   <rule id="110331" level="12">
35
   <rule id="110331" level="12">
32
     <if_sid>81603</if_sid>
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
     <description>A3-01 [PROD] SSL-VPN: authentication success by guest account</description>
38
     <description>A3-01 [PROD] SSL-VPN: authentication success by guest account</description>
36
     <group>soc_prod,a3,vpn_guest,</group>
39
     <group>soc_prod,a3,vpn_guest,</group>
37
     <mitre><id>T1078.001</id></mitre>
40
     <mitre><id>T1078.001</id></mitre>
41
        Requires SSL-VPN and FortiGate geo-login tracking -->
44
        Requires SSL-VPN and FortiGate geo-login tracking -->
42
   <rule id="110332" level="12">
45
   <rule id="110332" level="12">
43
     <if_sid>81603</if_sid>
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
     <description>A3-02 [PROD] SSL-VPN: success from different country than last login</description>
48
     <description>A3-02 [PROD] SSL-VPN: success from different country than last login</description>
47
     <group>soc_prod,a3,vpn_geo,</group>
49
     <group>soc_prod,a3,vpn_geo,</group>
48
     <mitre><id>T1078</id></mitre>
50
     <mitre><id>T1078</id></mitre>
52
        Requires SSL-VPN -->
54
        Requires SSL-VPN -->
53
   <rule id="110333" level="12">
55
   <rule id="110333" level="12">
54
     <if_sid>81603</if_sid>
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
     <description>A3-03 [PROD] SSL-VPN: success after multiple prior failures (brute-force indicator)</description>
58
     <description>A3-03 [PROD] SSL-VPN: success after multiple prior failures (brute-force indicator)</description>
58
     <group>soc_prod,a3,vpn_bruteforce,</group>
59
     <group>soc_prod,a3,vpn_bruteforce,</group>
59
     <mitre><id>T1110.001</id></mitre>
60
     <mitre><id>T1110.001</id></mitre>