Detection rules › Splunk

Windows SQL Server Startup Procedure

Status
production
Severity
low
Group by
dest, risk_message, risk_score, signature_id, startup_procedure
Author
Michael Haag, Splunk
Source
github.com/splunk/security_content

This detection identifies when a startup procedure is registered or executed in SQL Server. Startup procedures automatically execute when SQL Server starts, making them an attractive persistence mechanism for attackers. The detection monitors for suspicious stored procedure names and patterns that may indicate malicious activity, such as attempts to execute operating system commands or gain elevated privileges.

MITRE ATT&CK coverage

Rule body splunk

name: Windows SQL Server Startup Procedure
id: 7bec7c5c-2262-4adb-ba56-c8028512bc58
version: 6
creation_date: '2025-02-13'
modification_date: '2026-05-13'
author: Michael Haag, Splunk
status: production
type: Anomaly
description: This detection identifies when a startup procedure is registered or executed in SQL Server. Startup procedures automatically execute when SQL Server starts, making them an attractive persistence mechanism for attackers. The detection monitors for suspicious stored procedure names and patterns that may indicate malicious activity, such as attempts to execute operating system commands or gain elevated privileges.
data_source:
    - Windows Event Log Application 17135
search: '`wineventlog_application` EventCode=17135 | rex field=EventData_Xml "<Data>(?<startup_procedure>[^<]+)</Data>" | rename host as dest | eval risk_score=case( match(lower(startup_procedure), "xp_|sp_|cmdshell|shell|exec"), 90, true(), 70 ) | eval risk_message="SQL Server startup procedure ''".startup_procedure."'' was launched on host ".dest | stats count min(_time) as firstTime max(_time) as lastTime by dest EventCode startup_procedure risk_message risk_score | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | `windows_sql_server_startup_procedure_filter`'
how_to_implement: To successfully implement this detection, you need to be ingesting Windows Application Event Logs from SQL Server instances. The detection specifically looks for EventID 17135 which indicates startup procedure execution. Ensure proper logging is enabled for SQL Server startup events and that the logs are being forwarded to your SIEM.
known_false_positives: Legitimate startup procedures may be used by database administrators for maintenance, monitoring, or application functionality. Common legitimate uses include database maintenance and cleanup jobs, performance monitoring and statistics collection, application initialization procedures, and system health checks. To reduce false positives, organizations should document approved startup procedures, maintain an inventory of expected startup procedures, monitor for changes to startup procedure configurations, and create exceptions for known good procedures.
references:
    - https://learn.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-procoption-transact-sql
    - https://www.netspi.com/blog/technical-blog/network-penetration-testing/sql-server-persistence-part-1-startup-stored-procedures/
    - https://attack.mitre.org/techniques/T1505/001/
drilldown_searches:
    - name: View the detection results for - "$dest$"
      search: '%original_detection_search% | search  dest = "$dest$"'
      earliest_offset: $info_min_time$
      latest_offset: $info_max_time$
    - name: View risk events for the last 7 days for - "$dest$"
      search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$dest$") | stats count min(_time) as firstTime max(_time) as lastTime values(search_name) as "Search Name" values(risk_message) as "Risk Message" values(analyticstories) as "Analytic Stories" values(annotations._all) as "Annotations" values(annotations.mitre_attack.mitre_tactic) as "ATT&CK Tactics" by normalized_risk_object | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`'
      earliest_offset: 7d
      latest_offset: "0"
intermediate_findings:
    entities:
        - field: dest
          type: system
          score: 20
          message: A SQL Server startup procedure "$startup_procedure$" was executed on host $dest$, which could indicate an attempt to establish persistence
        - field: startup_procedure
          type: other
          score: 20
          message: A SQL Server startup procedure "$startup_procedure$" was executed on host $dest$, which could indicate an attempt to establish persistence
analytic_story:
    - SQL Server Abuse
    - Hellcat Ransomware
asset_type: Windows
mitre_attack_id:
    - T1505.001
product:
    - Splunk Enterprise
    - Splunk Enterprise Security
    - Splunk Cloud
category: endpoint
security_domain: endpoint
tests:
    - name: True Positive Test
      attack_data:
        - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1505.001/simulation/sql_startupprocedure_widows-application.log
          sourcetype: XmlWinEventLog
          source: XmlWinEventLog:Application
      description: PORTED MANUAL TEST - The risk message is dynamically generated in the SPL and it needs to be manually tested for integration testing.
      test_type: experimental

Stages and Predicates

Stage 1: search

`wineventlog_application` EventCode=17135

Stage 2: rex

| rex field=EventData_Xml "<Data>(?<startup_procedure>[^<]+)</Data>"

Stage 3: rename

| rename host as dest

Stage 4: eval

| eval risk_score=case( match(lower(startup_procedure), "xp_|sp_|cmdshell|shell|exec"), 90, true(), 70 )
risk_score =
ifmatch(lower(startup_procedure), "xp_|sp_|cmdshell|shell|exec")90
else70

Stage 5: eval

| eval risk_message="SQL Server startup procedure '".startup_procedure."' was launched on host ".dest

Stage 6: stats

| stats count min(_time) as firstTime max(_time) as lastTime by dest EventCode startup_procedure risk_message risk_score

Stage 7: search

| `security_content_ctime(firstTime)`

Stage 8: search

| `security_content_ctime(lastTime)`

Stage 9: search

| `windows_sql_server_startup_procedure_filter`

Indicators

Each row is a field, operator, and value that the rule matches. The corpus column counts how many other rules in the catalog look for the same combination: high numbers point to widely-used, community-vetted indicators. Blank or 1 shows that the indicator is specific to this rule.

FieldKindValues
EventCodeeq
  • 17135