Detection rules › Splunk

MCP Filesystem Server Suspicious Extension Write

Status
production
Group by
dest, method
Author
Rod Soto
Source
github.com/splunk/security_content

This detection identifies attempts to create executable or script files through MCP filesystem server connections. Threat actors leveraging LLM-based tools may attempt to write malicious executables, scripts, or batch files to disk for persistence or code execution. The detection prioritizes files written to system directories or startup locations which indicate higher likelihood of malicious intent.

MITRE ATT&CK coverage

Rule body splunk

name: MCP Filesystem Server Suspicious Extension Write
id: fc2a024a-18c1-4d31-9480-7f04cf3ff293
version: 2
creation_date: '2026-02-17'
modification_date: '2026-05-13'
author: Rod Soto
status: production
type: Hunting
description: This detection identifies attempts to create executable or script files through MCP filesystem server connections. Threat actors leveraging LLM-based tools may attempt to write malicious executables, scripts, or batch files to disk for persistence or code execution. The detection prioritizes files written to system directories or startup locations which indicate higher likelihood of malicious intent.
data_source:
    - MCP Server
search: |
    `mcp_server` method IN ("write_file", "create_file") direction=inbound
    | spath output=file_path path=params.path
    | spath output=file_content path=params.content
    | eval dest=host
    | eval file_extension=lower(mvindex(split(file_path, "."), -1))
    | where file_extension IN (
        "exe", "dll", "ps1", "bat", "cmd", "vbs", "js", "scr", "msi", "hta", "wsf", "wsh", "pif", "com", "cpl",
        "sh", "bash", "zsh", "ksh", "csh", "tcsh", "fish",
        "py", "pl", "rb", "php", "lua", "awk",
        "so", "dylib", "bin", "elf", "run", "AppImage",
        "deb", "rpm", "pkg", "dmg",
        "plist", "service", "timer", "socket", "conf"
        )
    | eval
        file_path_lower=lower(file_path),
        is_system_path = if(match(file_path_lower, "(windows|system32|syswow64|program files|/usr|/bin|/sbin|/lib|/lib64|/etc|/opt)"), 1, 0),
        is_startup_path = if(match(file_path_lower, "(startup|autorun|cron\.d|crontab|launchd|launchagents|launchdaemons|systemd|init\.d|rc\.d|rc\.local|profile\.d|bashrc|zshrc|bash_profile)"), 1, 0),
        is_hidden_unix = if(match(file_path, "/\.[^/]+$"), 1, 0),
        content_length=len(file_content)
    | stats count min(_time) as firstTime max(_time) as lastTime values(file_path) as file_paths values(file_extension) as extensions max(is_system_path) as targets_system_path max(is_startup_path) as targets_startup_path max(is_hidden_unix) as targets_hidden_file avg(content_length) as avg_content_size by dest, method
    | eval
        targets_system_path=if(isnull(targets_system_path), 0, targets_system_path),
        targets_startup_path=if(isnull(targets_startup_path), 0, targets_startup_path),
        targets_hidden_file=if(isnull(targets_hidden_file), 0, targets_hidden_file)
    | sort - targets_startup_path, - targets_system_path, - targets_hidden_file, - count
    | `security_content_ctime(firstTime)`
    | `security_content_ctime(lastTime)`
    | table dest firstTime lastTime count method extensions file_paths targets_system_path targets_startup_path targets_hidden_file avg_content_size
    | `mcp_filesystem_server_suspicious_extension_write_filter`
how_to_implement: Install the MCP Technology Add-on from Splunkbase and ensure MCP filesystem server logging is enabled with proper field extraction for params.path and params.content. Schedule the search to run every 5-15 minutes and tune alerting based on whether system or startup paths are targeted.
known_false_positives: Legitimate developers using LLM assistants to generate scripts or automation tools, DevOps engineers creating deployment scripts, and system administrators generating batch files for maintenance tasks.
references:
    - https://splunkbase.splunk.com/app/8377
    - https://cymulate.com/blog/cve-2025-53109-53110-escaperoute-anthropic/
    - https://www.splunk.com/en_us/blog/security/securing-ai-agents-model-context-protocol.html
analytic_story:
    - Suspicious MCP Activities
asset_type: Web Application
mitre_attack_id:
    - T1059
product:
    - Splunk Enterprise
    - Splunk Enterprise Security
    - Splunk Cloud
category: application
security_domain: endpoint
tests:
    - name: True Positive Test
      attack_data:
        - data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/mcp/mcp.log
          sourcetype: mcp:jsonrpc
          source: mcp.log
      test_type: unit

Stages and Predicates

Stage 1: search

`mcp_server` method IN ("write_file", "create_file") direction=inbound

Stage 2: spath

| spath output=file_path path=params.path

Stage 3: spath

| spath output=file_content path=params.content

Stage 4: eval

| eval dest=host

Stage 5: eval

| eval file_extension=lower(mvindex(split(file_path, "."), -1))

Stage 6: where

| where file_extension IN (
    "exe", "dll", "ps1", "bat", "cmd", "vbs", "js", "scr", "msi", "hta", "wsf", "wsh", "pif", "com", "cpl",
    "sh", "bash", "zsh", "ksh", "csh", "tcsh", "fish",
    "py", "pl", "rb", "php", "lua", "awk",
    "so", "dylib", "bin", "elf", "run", "AppImage",
    "deb", "rpm", "pkg", "dmg",
    "plist", "service", "timer", "socket", "conf"
    )

Stage 7: eval

| eval
    file_path_lower=lower(file_path),
    is_system_path = if(match(file_path_lower, "(windows|system32|syswow64|program files|/usr|/bin|/sbin|/lib|/lib64|/etc|/opt)"), 1, 0),
    is_startup_path = if(match(file_path_lower, "(startup|autorun|cron\.d|crontab|launchd|launchagents|launchdaemons|systemd|init\.d|rc\.d|rc\.local|profile\.d|bashrc|zshrc|bash_profile)"), 1, 0),
    is_hidden_unix = if(match(file_path, "/\.[^/]+$"), 1, 0),
    content_length=len(file_content)
is_hidden_unix =
ifmatch(file_path, "/\.[^/]+$")1
else0
is_startup_path =
ifmatch(file_path_lower, "(startup|autorun|cron\.d|crontab|launchd|launchagents|launchdaemons|systemd|init\.d|rc\.d|rc\.local|profile\.d|bashrc|zshrc|bash_profile)")1
else0
is_system_path =
ifmatch(file_path_lower, "(windows|system32|syswow64|program files|/usr|/bin|/sbin|/lib|/lib64|/etc|/opt)")1
else0

Stage 8: stats

| stats count min(_time) as firstTime max(_time) as lastTime values(file_path) as file_paths values(file_extension) as extensions max(is_system_path) as targets_system_path max(is_startup_path) as targets_startup_path max(is_hidden_unix) as targets_hidden_file avg(content_length) as avg_content_size by dest, method

Stage 9: eval

| eval
    targets_system_path=if(isnull(targets_system_path), 0, targets_system_path),
    targets_startup_path=if(isnull(targets_startup_path), 0, targets_startup_path),
    targets_hidden_file=if(isnull(targets_hidden_file), 0, targets_hidden_file)
targets_hidden_file =
ifisnull(targets_hidden_file)0
elsetargets_hidden_file
targets_startup_path =
ifisnull(targets_startup_path)0
elsetargets_startup_path
targets_system_path =
ifisnull(targets_system_path)0
elsetargets_system_path

Stage 10: sort

| sort - targets_startup_path, - targets_system_path, - targets_hidden_file, - count

Stage 11: search

| `security_content_ctime(firstTime)`

Stage 12: search

| `security_content_ctime(lastTime)`

Stage 13: table

| table dest firstTime lastTime count method extensions file_paths targets_system_path targets_startup_path targets_hidden_file avg_content_size

Stage 14: search

| `mcp_filesystem_server_suspicious_extension_write_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
directioneq
  • inbound
file_extensionin
  • "AppImage"
  • "awk"
  • "bash"
  • "bat"
  • "bin"
  • "cmd"
  • "com"
  • "conf"
  • "cpl"
  • "csh"
  • "deb"
  • "dll"
  • "dmg"
  • "dylib"
  • "elf"
  • "exe"
  • "fish"
  • "hta"
  • "js"
  • "ksh"
  • "lua"
  • "msi"
  • "php"
  • "pif"
  • "pkg"
  • "pl"
  • "plist"
  • "ps1"
  • "py"
  • "rb"
  • "rpm"
  • "run"
  • "scr"
  • "service"
  • "sh"
  • "so"
  • "socket"
  • "tcsh"
  • "timer"
  • "vbs"
  • "wsf"
  • "wsh"
  • "zsh"
methodin
  • "create_file"
  • "write_file"
sourcetypeeq
  • mcp:jsonrpc