<group name="mssql,database,">
    <!-- Parent rule: MSSQL Application channel events (18453/18454/18456 from MSSQLSERVER) -->
    <rule id="100000" level="0">
      <if_sid>61072,61071</if_sid>
      <decoded_as>windows_eventchannel</decoded_as>
      <field name="win.system.providerName" type="pcre2">^MSSQLSERVER$</field>
      <field name="win.system.channel">^Application$</field>
      <options>no_full_log</options>
      <description>MSSQL: Windows Event Log event from SQL Server Application channel.</description>
      <group>windows</group>
    </rule>

    <!-- Parent rule: MSSQL Security channel events (33205 from MSSQLSERVER) -->
    <rule id="100006" level="0">
      <if_sid>60001</if_sid>
      <decoded_as>windows_eventchannel</decoded_as>
      <field name="win.system.providerName" type="pcre2">^MSSQLSERVER\$\w+$</field>
      <field name="win.system.channel">^Security$</field>
      <options>no_full_log</options>
      <description>MSSQL: Windows Event Log event from SQL Server Audit (Security channel).</description>
      <group>windows</group>
    </rule>


  <!-- ============================================
       BASE EVENT DETECTION RULES
       ============================================ -->

  <!-- Login Success via standard Application log (Event IDs 18452, 18454) -->
  <rule id="100001" level="3">
    <if_sid>100000</if_sid>
    <field name="win.system.eventID">^18452$|^18454$</field>
    <description>MSSQL: Successful login - User: $(win.eventdata.data)</description>
    <group>authentication_success,mssql_login,</group>
  </rule>

  <!-- Login Failed via standard Application log (Event ID 18456) -->
  <rule id="100002" level="5">
    <if_sid>100000</if_sid>
    <field name="win.system.eventID">^18456$</field>
    <description>MSSQL: Failed login attempt - User: $(win.eventdata.data)</description>
    <group>authentication_failed,mssql_login,</group>
  </rule>

  <!--
    SQL Server Audit - Login Success (Event ID 33205, actionId LGIS)
    Structured fields available: serverPrincipalName, clientIp, databaseName
    These are used for CDB-based whitelist lookups below.
  -->
  <rule id="100003" level="3">
    <if_sid>100000,100006</if_sid>
    <field name="win.system.eventID">^33205$</field>
    <field name="win.eventdata.data" type="pcre2">action_id:LGIS</field>
    <description>MSSQL Audit: Login success - User: $(mssql.server_principal_name) from $(mssql.client_ip)</description>
    <group>authentication_success,mssql_audit,</group>
  </rule>

  <!--
    SQL Server Audit - Login Failed (Event ID 33205, actionId LGIF)
  -->
  <rule id="100004" level="5">
    <if_sid>100000,100006</if_sid>
    <field name="win.system.eventID">^33205$</field>
    <field name="win.eventdata.data" type="pcre2">action_id:LGIF</field>
    <description>MSSQL Audit: Login failed - User: $(mssql.server_principal_name) from $(mssql.client_ip)</description>
    <group>authentication_failed,mssql_audit,</group>
  </rule>

  <!--
    SQL Server Audit - Any other audited action (Event ID 33205, non-login actions)
  -->
  <rule id="100005" level="3">
    <if_sid>100000,100006</if_sid>
    <field name="win.system.eventID">^33205$</field>
    <field name="win.eventdata.data" type="pcre2">action_id:(?!LGIS|LGIF)\w</field>
    <description>MSSQL Audit: DB action $(mssql.action_id) by $(mssql.server_principal_name) from $(mssql.client_ip)</description>
    <group>mssql_audit,</group>
  </rule>


  <!-- ============================================
       AUTHORIZED RULES (CDB whitelist - 33205 events)
       Requires SQL Server Audit Specification enabled.
       ============================================ -->

  <!--
    AUTHORIZED: User+IP combination in whitelist (most specific match)
    win.eventdata.serverPrincipalName is used as KEY, clientIp as VALUE.
  -->
  <rule id="100060" level="2">
    <if_sid>100003,100005</if_sid>
    <list field="mssql.server_principal_name" lookup="match_key_value" check_value="mssql.client_ip">etc/lists/mssql_whitelist</list>
    <description>MSSQL: Authorized connection - User $(mssql.server_principal_name) from whitelisted IP $(mssql.client_ip)</description>
    <group>mssql_authorized,mssql_user_ip_match,</group>
  </rule>

  <!--
    AUTHORIZED: IP-only whitelist (any user allowed from trusted source)
    Covers backup servers, monitoring agents, etc.
  -->
  <rule id="100062" level="2">
    <if_sid>100003,100005</if_sid>
    <list field="mssql.client_ip" lookup="match_key">etc/lists/mssql_whitelist_ips</list>
    <description>MSSQL: Authorized connection from trusted IP $(mssql.client_ip) - User: $(mssql.server_principal_name)</description>
    <group>mssql_authorized,mssql_ip_only_match,</group>
  </rule>

  <!--
    UNAUTHORIZED: Neither user+IP combo nor IP-only list matched.
    High severity - unexpected connection source.
  -->
  <rule id="100010" level="15">
    <if_sid>100003,100005</if_sid>
    <list field="mssql.server_principal_name" lookup="not_match_key">etc/lists/mssql_whitelist</list>
    <list field="mssql.client_ip" lookup="not_match_key">etc/lists/mssql_whitelist_ips</list>
    <description>MSSQL ALERT: UNAUTHORIZED connection - User $(mssql.server_principal_name) from $(mssql.client_ip) not in any whitelist</description>
    <group>mssql_unauthorized,invalid_connection,pci_dss_10.2.4,gdpr_IV_35.7.d,hipaa_164.312.b,</group>
    <options>alert_by_email</options>
  </rule>

  <!--
    UNAUTHORIZED: Known user connecting from wrong IP (possible credential theft)
    User exists as a key in mssql_whitelist but the source IP doesn't match.
  -->
  <rule id="100013" level="13">
    <if_sid>100003,100005</if_sid>
    <list field="mssql.server_principal_name" lookup="match_key">etc/lists/mssql_whitelist</list>
    <list field="mssql.client_ip" lookup="not_match_key">etc/lists/mssql_whitelist_ips</list>
    <description>MSSQL ALERT: User $(mssql.server_principal_name) connecting from UNAUTHORIZED IP $(mssql.client_ip) - possible credential theft</description>
    <group>mssql_unauthorized,credential_misuse,pci_dss_10.2.4,</group>
    <options>alert_by_email</options>
  </rule>


  <!-- ============================================
       FAILED LOGIN RULES
       ============================================ -->

  <!--
    Audit: Failed login from authorized user+IP (lower severity, likely typo)
  -->
  <rule id="100061" level="4">
    <if_sid>100004</if_sid>
    <list field="mssql.server_principal_name" lookup="match_key_value" check_value="mssql.client_ip">etc/lists/mssql_whitelist</list>
    <description>MSSQL: Failed login from authorized user $(mssql.server_principal_name) at $(mssql.client_ip)</description>
    <group>mssql_authorized,authentication_failed,mssql_auth_failed_trusted,</group>
  </rule>

  <!--
    Audit: Failed login from trusted IP (any user, lower severity)
  -->
  <rule id="100063" level="4">
    <if_sid>100004</if_sid>
    <list field="mssql.client_ip" lookup="match_key">etc/lists/mssql_whitelist_ips</list>
    <description>MSSQL: Failed login from trusted IP $(mssql.client_ip) - User: $(mssql.server_principal_name)</description>
    <group>mssql_authorized,authentication_failed,mssql_auth_failed_trusted,</group>
  </rule>

  <!--
    Audit: Failed login from unauthorized source
  -->
  <rule id="100011" level="10">
    <if_sid>100004</if_sid>
    <list field="mssql.server_principal_name" lookup="not_match_key">etc/lists/mssql_whitelist</list>
    <list field="mssql.client_ip" lookup="not_match_key">etc/lists/mssql_whitelist_ips</list>
    <description>MSSQL ALERT: Failed login from UNAUTHORIZED source - User $(mssql.server_principal_name) from $(mssql.client_ip)</description>
    <group>mssql_unauthorized,authentication_failed,</group>
  </rule>

  <!--
    Standard log (18456): Failed login - username-only whitelist check.
    IP is unavailable as a structured field for these events.
    Use SQL Server Audit (33205) for full user+IP checking.
  -->
  <rule id="100015" level="7">
    <if_sid>100002</if_sid>
    <list field="win.eventdata.data" lookup="not_match_key">etc/lists/mssql_whitelist</list>
    <description>MSSQL ALERT: Failed login from unknown user $(win.eventdata.data) - enable SQL Server Audit for IP-based whitelist</description>
    <group>mssql_unauthorized,authentication_failed,</group>
  </rule>


  <!-- ============================================
       BRUTE FORCE DETECTION
       ============================================ -->

  <!--
    Multiple audit failed logins in short window (33205 LGIF events, any source, 3 in 60s).
    NOTE: Per-IP correlation is not possible without a custom decoder — the 33205 data blob
    contains unique per-event tokens (timestamp, connection_id, sequence_group_id) that
    prevent same_field matching. This fires on any burst of audit failures from the server.
  -->
  <rule id="100021" level="14" frequency="3" timeframe="60">
    <if_matched_sid>100004</if_matched_sid>
    <description>MSSQL CRITICAL: Burst of audit login failures on $(win.system.computer) - possible brute force</description>
    <group>mssql_unauthorized,mssql_brute_force,attack,</group>
    <options>alert_by_email</options>
  </rule>

  <!--
    Multiple failed logins from authorized source (5 in 120s - likely misconfiguration)
  -->
  <rule id="100020" level="10" frequency="5" timeframe="120">
    <if_matched_group>mssql_auth_failed_trusted</if_matched_group>
    <same_field>mssql.client_ip</same_field>
    <description>MSSQL: Multiple failed login attempts from authorized source $(mssql.client_ip)</description>
    <group>authentication_failures,mssql_brute_force,</group>
  </rule>

  <!--
    Multiple failed logins via standard Application log (18456), same username+IP.
    win.eventdata.data for 18456 is "username,  [CLIENT: IP]" — consistent per user+IP,
    so same_field correlation works correctly here.
    Fixed: was if_matched_sid=100002 but 100015 overrides 100002 on every event.
  -->
  <rule id="100022" level="10" frequency="5" timeframe="120">
    <if_matched_sid>100015</if_matched_sid>
    <same_field>win.eventdata.data</same_field>
    <description>MSSQL: Multiple failed login attempts for user $(win.eventdata.data)</description>
    <group>authentication_failures,mssql_brute_force,</group>
  </rule>


  <!-- ============================================
       SENSITIVE ACCOUNT MONITORING
       ============================================ -->

  <!--
    SA account login from unauthorized source (highest severity)
  -->
  <rule id="100031" level="15">
    <if_sid>100010,100013</if_sid>
    <field name="mssql.server_principal_name" type="pcre2">^[Ss][Aa]$</field>
    <description>MSSQL CRITICAL: SA account login from UNAUTHORIZED source $(mssql.client_ip)</description>
    <group>mssql_unauthorized,mssql_privileged,admin_login,attack,</group>
    <options>alert_by_email</options>
  </rule>

  <!--
    SA account login from authorized source (notable, lower severity)
  -->
  <rule id="100030" level="8">
    <if_sid>100060,100062</if_sid>
    <field name="mssql.server_principal_name" type="pcre2">^[Ss][Aa]$</field>
    <description>MSSQL WARNING: SA account login from $(mssql.client_ip)</description>
    <group>mssql_privileged,admin_login,</group>
  </rule>


  <!-- ============================================
       AFTER-HOURS MONITORING
       ============================================ -->

  <!--
    After-hours connection from unauthorized source
  -->
  <rule id="100041" level="14">
    <if_sid>100010,100013</if_sid>
    <time>22:00 - 06:00</time>
    <description>MSSQL CRITICAL: After-hours connection from UNAUTHORIZED source $(mssql.client_ip)</description>
    <group>mssql_unauthorized,mssql_afterhours,attack,</group>
    <options>alert_by_email</options>
  </rule>

  <!--
    After-hours connection from authorized source (policy notification)
  -->
  <rule id="100040" level="6">
    <if_sid>100060,100062</if_sid>
    <time>22:00 - 06:00</time>
    <description>MSSQL: After-hours database connection from $(mssql.client_ip) - User: $(mssql.server_principal_name)</description>
    <group>mssql_afterhours,policy_violation,</group>
  </rule>



  <!-- ============================================
       UNAUTHORIZED SUCCESSFUL LOGIN (Application channel 18454)
       Fires when a non-whitelisted IP successfully authenticates.
       NOTE: Keep whitelist IPs in sync with etc/lists/mssql_whitelist
             and etc/lists/mssql_whitelist_ips when those change.
       ============================================ -->

  <rule id="100070" level="12">
    <if_sid>100001</if_sid>
    <field name="win.eventdata.data" type="pcre2">\[CLIENT: (?!127\.0\.0\.1|::1|192\.168\.1\.227|192\.168\.1\.69|&lt;local machine&gt;)</field>
    <description>MSSQL ALERT: Successful login from non-whitelisted source - </description>
    <group>mssql_unauthorized,authentication_success,pci_dss_10.2.4,gdpr_IV_35.7.d,</group>
    <options>alert_by_email</options>
  </rule>

  <!-- Successful audit login (33205 LGIS) from non-whitelisted IP.
       Uses regex only - does not depend on the CDB whitelist path (100003).
       Mirrors the 100070 approach used for Application log events. -->
  <rule id="100071" level="12">
    <if_sid>100000,100006</if_sid>
    <field name="win.system.eventID">^33205$</field>
    <field name="win.eventdata.data" type="pcre2">action_id:LGIS</field>
    <field name="win.eventdata.data" type="pcre2">client_ip:(?!127\.0\.0\.1|::1|192\.168\.1\.227|192\.168\.1\.69)</field>
    <description>MSSQL ALERT: Audit successful login from non-whitelisted IP - </description>
    <group>mssql_unauthorized,authentication_success,pci_dss_10.2.4,gdpr_IV_35.7.d,</group>
    <options>alert_by_email</options>
  </rule>

  <rule id="100072" level="13">
    <if_sid>100001</if_sid>
    <field name="win.eventdata.data" type="pcre2">(?i)(?:^(?:sa|administrator),\s.*\[CLIENT:\s(?!127\.0\.0\.1|::1|192\.168\.1\.69|192\.168\.1\.227|&lt;local machine&gt;))|(?:^domain\\sqladmin,\s.*\[CLIENT:\s(?!192\.168\.1\.69|192\.168\.1\.227))</field>
    <description>MSSQL ALERT: Monitored user logged in from unexpected source: $(win.eventdata.data)</description>
    <group>mssql_unauthorized,authentication_success,credential_misuse,pci_dss_10.2.4,gdpr_IV_35.7.d,</group>
    <options>alert_by_email</options>
  </rule>

  <!-- Audit log (33205 LGIS): monitored user at unexpected IP.
       data field is a key:value blob, e.g. "...server_principal_name:sa...client_ip:1.2.3.4..." -->
  <rule id="100073" level="13">
    <if_sid>100000,100006</if_sid>
    <field name="win.system.eventID">^33205$</field>
    <field name="win.eventdata.data" type="pcre2">action_id:LGIS</field>
    <field name="win.eventdata.data" type="pcre2">(?i)(?:server_principal_name:(?:sa|administrator)\b.*client_ip:(?!127\.0\.0\.1|::1|192\.168\.1\.69|192\.168\.1\.227))|(?:server_principal_name:domain\\sqladmin\b.*client_ip:(?!192\.168\.1\.69|192\.168\.1\.227))</field>
    <description>MSSQL ALERT: Monitored user logged in from unexpected IP</description>
    <group>mssql_unauthorized,authentication_success,credential_misuse,pci_dss_10.2.4,gdpr_IV_35.7.d,</group>
    <options>alert_by_email</options>
  </rule>

</group>

