Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions detections/endpoint/local_llm_model_dns_queries.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
name: Local LLM Model DNS Queries
id: e0cda953-b926-4778-b7df-df61dab7bb78
version: 1
date: '2025-11-12'
author: Rod Soto
status: production
type: Hunting
description: Detects DNS queries related to local LLM models on endpoints by monitoring Sysmon DNS query events (Event ID 22) for known LLM model domains and services.
data_source:
- Sysmon EventID 22
search: |
`sysmon` | spath
| search Event.System.EventID=22
| eval host='Event.System.Computer'
| eval QueryName=mvindex('Event.EventData.Data', mvfind('Event.EventData.Data{@Name}', "^QueryName$"))
| eval Image=mvindex('Event.EventData.Data', mvfind('Event.EventData.Data{@Name}', "^Image$"))
| search QueryName="*huggingface*" OR
QueryName="*ollama*" OR
QueryName="*jan.ai*" OR
QueryName="*gpt4all*" OR
QueryName="*nomic*" OR
QueryName="*koboldai*" OR
QueryName="*lmstudio*" OR
QueryName="*modelscope*" OR
QueryName="*civitai*" OR
QueryName="*github.com*llama*" OR
QueryName="*github.com*oobabooga*" OR
QueryName="*github.com*koboldai*" OR
QueryName="*replicate*" OR
QueryName="*anthropic*" OR
QueryName="*openai*" OR
QueryName="*openrouter*" OR
QueryName="*api.openrouter*" OR
QueryName="*aliyun*" OR
QueryName="*alibabacloud*" OR
QueryName="*dashscope.aliyuncs*"
| search Image!="*MsMpEng.exe*" AND Image!="*Windows Defender*" AND Image!="*system32*" AND Image!="*ProgramData*" AND Image!="*syswow64*"
| stats count by _time, host, Image, QueryName
| sort -count
| `local_llm_model_dns_queries_filter`
how_to_implement: Ensure Sysmon is deployed across Windows endpoints and configured to capture DNS query events (Event ID 22). Configure Sysmon's XML configuration file to log detailed command-line arguments, parent process information, and full process image paths. Ingest Sysmon event logs into Splunk via the Splunk Universal Forwarder or Windows Event Log Input, ensuring they are tagged with `sourcetype=XmlWinEventLog:Microsoft-Windows-Sysmon/Operational`. Verify the `sysmon` macro exists in your Splunk environment and correctly references the Sysmon event logs. Create or update the `local_llm_model_dns_queries_filter` macro in your detections/filters folder to exclude approved systems, authorized developers, sanctioned ML/AI workstations, or known development/lab environments as needed. Deploy this hunting search to your Splunk Enterprise Security or Splunk Enterprise instance and schedule it to run on a regular cadence to detect unauthorized LLM model DNS queries and shadow AI activities. Correlate findings with endpoint asset inventory and user identity data to prioritize investigation.
known_false_positives: Legitimate DNS queries to LLM model hosting platforms by authorized developers, ML engineers, and researchers during model training, fine-tuning, or experimentation. Approved AI/ML sandboxes and lab environments where LLM model downloads are expected. Automated ML pipelines and workflows that interact with LLM model hosting services as part of their normal operation. Third-party applications and services that access LLM model platforms for legitimate purposes.
references:
- https://docs.microsoft.com/en-us/sysinternals/downloads/sysmon
- https://www.splunk.com/en_us/blog/artificial-intelligence/splunk-technology-add-on-for-ollama.html
- https://blogs.cisco.com/security/detecting-exposed-llm-servers-shodan-case-study-on-ollama
tags:
analytic_story:
- Suspicious Local Llm Frameworks
asset_type: Endpoint
mitre_attack_id:
- T1590
product:
- Splunk Enterprise
- Splunk Enterprise Security
- Splunk Cloud
security_domain: endpoint
drilldown_searches:
- name: View the detection results for - "$host$"
search: '%original_detection_search% | search host="$host$"'
earliest_offset: $info_min_time$
latest_offset: $info_max_time$
- name: View risk events for the last 7 days for - "$host$"
search: |
| from datamodel Risk.All_Risk
| search normalized_risk_object="$host$" starthoursago=168
| 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: $info_min_time$
latest_offset: $info_max_time$
tests:
- name: True Positive Test
attack_data:
- data: https://raw.githubusercontent.com/splunk/attack_data/master/datasets/suspicious_behaviour/local_llms/sysmon_local_llms.txt
sourcetype: XmlWinEventLog:Microsoft-Windows-Sysmon/Operational
source: XmlWinEventLog:Microsoft-Windows-Sysmon/Operational
92 changes: 92 additions & 0 deletions detections/endpoint/suspicious_local_llm_framework.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
name: Suspicious Local LLM Framework
id: 657b022a-9c50-4910-8572-f036cc4a0248
version: 1
date: '2025-11-12'
author: Rod Soto
status: production
type: Hunting
description: This detection identifies suspicious execution of unauthorized local LLM frameworks (Ollama, LM Studio, GPT4All, Jan, Llama.cpp, KoboldCPP, NutStudio, Llama-Run) and related AI/ML libraries (HuggingFace, LangChain, Transformers) by monitoring Windows process creation events (Event ID 4688). The search correlates process names, command-line arguments, and parent processes to classify framework activity and flag potential shadow AI deployments or unauthorized model inference operations on endpoints.
data_source:
- Windows Event Log Security 4688
search: |
`wineventlog_security` EventID=4688 | spath
| rename "Event.System.Computer" as host, "Event.System.EventID" as EventID
| eval NewProcessName=mvindex('Event.EventData.Data', mvfind('Event.EventData.Data{@Name}', "NewProcessName"))
| eval ParentProcessName=mvindex('Event.EventData.Data', mvfind('Event.EventData.Data{@Name}', "ParentProcessName"))
| eval CommandLine=mvindex('Event.EventData.Data', mvfind('Event.EventData.Data{@Name}', "CommandLine"))
| eval SubjectUserName=mvindex('Event.EventData.Data', mvfind('Event.EventData.Data{@Name}', "SubjectUserName"))
| search (
NewProcessName="*ollama*" OR
NewProcessName="*llama*" OR
NewProcessName="*llama-run*" OR
NewProcessName="*gpt4all*" OR
NewProcessName="*lmstudio*" OR
NewProcessName="*nutstudio*" OR
NewProcessName="*koboldcpp*" OR
NewProcessName="*jan*" OR
NewProcessName="*jan.exe*" OR
CommandLine="*transformers*" OR
CommandLine="*langchain*" OR
CommandLine="*huggingface*" OR
CommandLine="*llama-run*" OR
CommandLine="*nutstudio*" OR
ParentProcessName="*ollama*" OR
ParentProcessName="*lmstudio*" OR
ParentProcessName="*nutstudio*" OR
ParentProcessName="*gpt4all*" OR
ParentProcessName="*jan*" OR
ParentProcessName="*llama-run*"
)
| eval Framework=case(
like(NewProcessName, "%ollama%") OR like(ParentProcessName, "%ollama%"), "Ollama",
like(NewProcessName, "%lmstudio%") OR like(NewProcessName, "%LM Studio%") OR like(ParentProcessName, "%lmstudio%"), "LM Studio",
like(NewProcessName, "%nutstudio%") OR like(ParentProcessName, "%nutstudio%") OR like(CommandLine, "%nutstudio%"), "NutStudio",
like(NewProcessName, "%gpt4all%") OR like(ParentProcessName, "%gpt4all%"), "GPT4All",
like(NewProcessName, "%jan%") OR like(ParentProcessName, "%jan%") OR like(NewProcessName, "%jan.exe%"), "Jan",
like(NewProcessName, "%koboldcpp%") OR like(CommandLine, "%koboldcpp%"), "KoboldCPP",
like(NewProcessName, "%llama-run%") OR like(ParentProcessName, "%llama-run%") OR like(CommandLine, "%llama-run%"), "Llama-Run",
like(CommandLine, "%transformers%") OR like(CommandLine, "%huggingface%"), "HuggingFace/Transformers",
like(CommandLine, "%langchain%"), "LangChain",
like(NewProcessName, "%llama%") OR like(NewProcessName, "%llama.cpp%") OR like(ParentProcessName, "%llama%"), "Llama.cpp",
1=1, "Related Activity"
)
| stats count by host, Framework, EventID, ParentProcessName
| sort host, Framework, -count | `suspicious_local_llm_framework_filter`
how_to_implement: This search can be implemented by configuring Splunk to ingest Windows Security Event logs, specifically monitoring Event ID 4688 (process creation). Ensure that process creation events include detailed command-line arguments. Deploy the search as a scheduled saved search or real-time alert within Splunk Enterprise Security or Splunk Cloud to continuously detect suspicious local LLM framework activity. Tune the search by updating the process name and command-line keywords as new frameworks emerge. Integrate with asset and identity data to enrich context and reduce false positives.
known_false_positives: Legitimate development and data science workflows where developers, ML engineers, and researchers install and run local LLM frameworks for experimentation, fine-tuning, or prototyping. Approved AI/ML sandboxes and lab environments, open-source and educational use cases, third-party software bundling HuggingFace/Transformers/LangChain libraries as dependencies, and system administrators deploying frameworks as part of containerized services. Parent process and command-line keyword overlap with unrelated tools (e.g., "llama-backup", tools using "--transformers" flags for non-LLM purposes). Recommended tuning — baseline approved frameworks and users, exclude sanctioned development/lab systems, require additional context (user role, peer group, model artifacts, network exfiltration signals) before escalating to incidents.
references:
- https://splunkbase.splunk.com/app/8024
- https://www.ibm.com/think/topics/shadow-ai
- https://www.splunk.com/en_us/blog/artificial-intelligence/splunk-technology-add-on-for-ollama.html
- https://blogs.cisco.com/security/detecting-exposed-llm-servers-shodan-case-study-on-ollama
drilldown_searches:
- name: View the detection results for - "$host$"
search: '%original_detection_search% | search host="$host$"'
earliest_offset: $info_min_time$
latest_offset: $info_max_time$
- name: View risk events for the last 7 days for - "$host$"
search: |
| from datamodel Risk.All_Risk
| search normalized_risk_object="$host$" starthoursago=168
| 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: $info_min_time$
latest_offset: $info_max_time$
tags:
analytic_story:
- Suspicious Local Llm Frameworks
asset_type: 'Endpoint'
mitre_attack_id:
- T1543
product:
- Splunk Enterprise
- Splunk Enterprise Security
- Splunk Cloud
security_domain: endpoint
tests:
- name: True Positive Test
attack_data:
- data: https://raw.githubusercontent.com/splunk/attack_data/master/datasets/suspicious_behaviour/local_llms/4688_local_llms.txt
sourcetype: XmlWinEventLog
source: XmlWinEventLog:Security
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: Suspicious Local LLM Framework Download and Execution via Sysmon
id: e27da0be-838a-40f2-bd4c-2dac4e7f6031
version: 1
date: '2025-11-12'
author: Rod Soto
status: production
type: Hunting
description: Detects execution of local LLM frameworks on Windows endpoints by monitoring Sysmon process creation events (Event ID 1) for known LLM tools including Ollama, GPT4All, LM Studio, Jan, llama.cpp, KoboldCPP, and Oobabooga. This hunting detection identifies shadow AI deployments and unauthorized local model inference operations by analyzing process execution patterns of LLM frameworks.
data_source:
- Sysmon EventID 1
search: |
`sysmon` | spath
| eval EventID='Event.System.EventID'
| eval host='Event.System.Computer'
| eval Image=mvindex('Event.EventData.Data', mvfind('Event.EventData.Data{@Name}', "^Image$"))
| eval TargetFilename=mvindex('Event.EventData.Data', mvfind('Event.EventData.Data{@Name}', "^TargetFilename$"))
| eval QueryName=mvindex('Event.EventData.Data', mvfind('Event.EventData.Data{@Name}', "^QueryName$"))
| search ( Image="*ollama*" OR Image="*gpt4all*" OR Image="*lmstudio*" OR Image="*kobold*" OR Image="*jan*" OR Image="*llama-run*" OR Image="*llama.cpp*" OR Image="*oobabooga*" OR Image="*text-generation-webui*" OR TargetFilename="*.gguf*" OR TargetFilename="*ollama*" OR TargetFilename="*jan*" OR QueryName="*huggingface.co*" OR QueryName="*ollama.com*" )
| search Image!="*MsMpEng*" AND Image!="*defender*" AND Image!="*windows/system32*" AND Image!="*syswow64*" AND Image!="*winlogon*" AND Image!="*svchost*"
| eval Framework=case(
match(Image, "(?i)ollama") OR match(TargetFilename, "(?i)ollama") OR match(QueryName, "(?i)ollama"), "Ollama",
match(Image, "(?i)lmstudio") OR match(Image, "(?i)lm-studio") OR match(TargetFilename, "(?i)lmstudio"), "LMStudio",
match(Image, "(?i)gpt4all") OR match(TargetFilename, "(?i)gpt4all"), "GPT4All",
match(Image, "(?i)kobold"), "KoboldCPP",
match(Image, "(?i)jan") OR match(TargetFilename, "(?i)jan"), "Jan AI",
match(Image, "(?i)llama-run") OR match(Image, "(?i)llama-b") OR match(Image, "(?i)llama.cpp"), "llama.cpp",
match(Image, "(?i)oobabooga") OR match(Image, "(?i)text-generation-webui"), "Oobabooga",
1=1, "Other"
)
| search Framework!="Other" | stats count by Framework, host, Image | sort -count | `suspicious_local_llm_framework_download_and_execution_via_sysmon_filter`
how_to_implement: Ensure Sysmon is deployed across Windows endpoints and configured to capture process creation events (Event ID 1). Configure Sysmon's XML configuration file to log detailed command-line arguments, parent process information, and full process image paths. Ingest Sysmon event logs into Splunk via the Splunk Universal Forwarder or Windows Event Log Input, ensuring they are tagged with `sourcetype=XmlWinEventLog:Microsoft-Windows-Sysmon/Operational`. Verify the `sysmon` macro exists in your Splunk environment and correctly references the Sysmon event logs. Create or update the `suspicious_local_llm_framework_download_and_execution_via_sysmon_filter` macro in your detections/filters folder to exclude approved systems, authorized developers, sanctioned ML/AI workstations, or known development/lab environments as needed. Deploy this hunting search to your Splunk Enterprise Security or Splunk Enterprise instance and schedule it to run on a regular cadence to detect unauthorized LLM framework execution and shadow AI deployments. Correlate findings with endpoint asset inventory and user identity data to prioritize investigation.
known_false_positives: Legitimate creation of LLM model files by authorized developers, ML engineers, and researchers during model training, fine-tuning, or experimentation. Approved AI/ML sandboxes and lab environments where model file creation is expected. Automated ML pipelines and workflows that generate or update model files as part of their normal operation. Third-party applications and services that manage or cache LLM model files for legitimate purposes.
references:
- https://docs.microsoft.com/en-us/sysinternals/downloads/sysmon
- https://www.splunk.com/en_us/blog/artificial-intelligence/splunk-technology-add-on-for-ollama.html
- https://blogs.cisco.com/security/detecting-exposed-llm-servers-shodan-case-study-on-ollama
tags:
analytic_story:
- Suspicious Local Llm Frameworks
asset_type: Endpoint
mitre_attack_id:
- T1543
product:
- Splunk Enterprise
- Splunk Enterprise Security
- Splunk Cloud
security_domain: endpoint
drilldown_searches:
- name: View the detection results for - "$host$"
search: '%original_detection_search% | search host="$host$"'
earliest_offset: $info_min_time$
latest_offset: $info_max_time$
- name: View risk events for the last 7 days for - "$host$"
search: |
| from datamodel Risk.All_Risk
| search normalized_risk_object="$host$" starthoursago=168
| 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: $info_min_time$
latest_offset: $info_max_time$
tests:
- name: True Positive Test
attack_data:
- data: https://raw.githubusercontent.com/splunk/attack_data/master/datasets/suspicious_behaviour/local_llms/sysmon_local_llms.txt
sourcetype: XmlWinEventLog:Microsoft-Windows-Sysmon/Operational
source: XmlWinEventLog:Microsoft-Windows-Sysmon/Operational
Loading