Detection rules › Splunk
Ollama Suspicious Prompt Injection Jailbreak
Detects potential prompt injection or jailbreak attempts against Ollama API endpoints by identifying requests with abnormally long response times. Attackers often craft complex, layered prompts designed to bypass AI safety controls, which typically result in extended processing times as the model attempts to parse and respond to these malicious inputs. This detection monitors /api/generate and /api/chat endpoints for requests exceeding 30 seconds, which may indicate sophisticated jailbreak techniques, multi-stage prompt injections, or attempts to extract sensitive information from the model.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Initial Access | T1190 Exploit Public-Facing Application |
| Execution | T1059 Command and Scripting Interpreter |
Rule body splunk
name: Ollama Suspicious Prompt Injection Jailbreak
id: aac5df6f-9151-4da6-bdb2-5691aa6e376f
version: 4
creation_date: '2025-10-13'
modification_date: '2026-05-13'
author: Rod Soto
status: experimental
type: Anomaly
description: Detects potential prompt injection or jailbreak attempts against Ollama API endpoints by identifying requests with abnormally long response times. Attackers often craft complex, layered prompts designed to bypass AI safety controls, which typically result in extended processing times as the model attempts to parse and respond to these malicious inputs. This detection monitors /api/generate and /api/chat endpoints for requests exceeding 30 seconds, which may indicate sophisticated jailbreak techniques, multi-stage prompt injections, or attempts to extract sensitive information from the model.
data_source:
- Ollama Server
search: '`ollama_server` "GIN" ("*/api/generate*" OR "*/v1/chat/completions*") | rex field=_raw "\|\s+(?<status_code>\d+)\s+\|\s+(?<response_time>[\d\.]+[a-z]+)\s+\|\s+(?<src_ip>[\:\da-f\.]+)\s+\|\s+(?<http_method>\w+)\s+\"(?<uri_path>[^\"]+)\"" | rex field=response_time "^(?:(?<minutes>\d+)m)?(?<seconds>[\d\.]+)s$" | eval response_time_seconds=if(isnotnull(minutes), tonumber(minutes)*60+tonumber(seconds), tonumber(seconds)) | eval src=src_ip | where response_time_seconds > 30 | bin _time span=10m | stats count as long_request_count, avg(response_time_seconds) as avg_response_time, max(response_time_seconds) as max_response_time, values(uri_path) as uri_path, values(status_code) as status_codes by _time, src, host | where long_request_count > 170 | eval avg_response_time=round(avg_response_time, 2) | eval max_response_time=round(max_response_time, 2) | eval severity=case( long_request_count > 50 OR max_response_time > 55, "critical", long_request_count > 20 OR max_response_time > 40, "high", 1=1, "medium" ) | eval attack_type="Potential Prompt Injection / Jailbreak" | table _time, host, src, uri_path, long_request_count, avg_response_time, max_response_time, status_codes, severity, attack_type | `ollama_suspicious_prompt_injection_jailbreak_filter`'
how_to_implement: 'Ingest Ollama logs via Splunk TA-ollama add-on by configuring file monitoring inputs pointed to your Ollama server log directories (sourcetype: ollama:server), or enable HTTP Event Collector (HEC) for real-time API telemetry and prompt analytics (sourcetypes: ollama:api, ollama:prompts). CIM compatibility using the Web datamodel for standardized security detections.'
known_false_positives: Legitimate complex queries requiring extensive model reasoning, large context windows processing substantial amounts of text, batch processing operations, or resource-constrained systems experiencing performance degradation may trigger this detection during normal operations.
references:
- https://github.com/rosplk/ta-ollama
- https://github.com/OWASP/www-project-ai-testing-guide
drilldown_searches:
- name: View the detection results for - "$src$"
search: '%original_detection_search% | search src="$src$"'
earliest_offset: $info_min_time$
latest_offset: $info_max_time$
- name: View risk events for the last 7 days for - "$src$"
search: '| from datamodel Risk.All_Risk | search normalized_risk_object IN ("$src$") | 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: src
type: system
score: 20
message: Potential prompt injection or jailbreak attempt detected from $src$ with $long_request_count$ requests averaging $avg_response_time$ seconds, indicating possible attempts to bypass AI safety controls or extract sensitive information from the Ollama model.
analytic_story:
- Suspicious Ollama Activities
asset_type: Web Application
mitre_attack_id:
- T1190
- 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/ollama/server.log
sourcetype: ollama:server
source: server.log
test_type: experimental
description: This test is a legacy experimental test and may not be accurate.
Stages and Predicates
Stage 1: search
`ollama_server` "GIN" ("*/api/generate*" OR "*/v1/chat/completions*")
Stage 2: eval
| rex field=_raw "\|\s+(?<status_code>\d+)\s+\|\s+(?<response_time>[\d\.]+[a-z]+)\s+\|\s+(?<src_ip>[\:\da-f\.]+)\s+\|\s+(?<http_method>\w+)\s+\"(?<uri_path>[^\"]+)\""
Stage 3: rex
| rex field=response_time "^(?:(?<minutes>\d+)m)?(?<seconds>[\d\.]+)s$"
Stage 4: eval
| eval response_time_seconds=if(isnotnull(minutes), tonumber(minutes)*60+tonumber(seconds), tonumber(seconds))
Stage 5: eval
| eval src=src_ip
Stage 6: where
| where response_time_seconds > 30
Stage 7: bucket
| bin _time span=10m
Stage 8: stats
| stats count as long_request_count, avg(response_time_seconds) as avg_response_time, max(response_time_seconds) as max_response_time, values(uri_path) as uri_path, values(status_code) as status_codes by _time, src, host
Stage 9: where
| where long_request_count > 170
Stage 10: eval
| eval avg_response_time=round(avg_response_time, 2)
Stage 11: eval
| eval max_response_time=round(max_response_time, 2)
Stage 12: eval
| eval severity=case( long_request_count > 50 OR max_response_time > 55, "critical", long_request_count > 20 OR max_response_time > 40, "high", 1=1, "medium" )
severity =long_request_count > 50 OR max_response_time > 55"critical"long_request_count > 20 OR max_response_time > 40"high""medium"Stage 13: eval
| eval attack_type="Potential Prompt Injection / Jailbreak"
Stage 14: table
| table _time, host, src, uri_path, long_request_count, avg_response_time, max_response_time, status_codes, severity, attack_type
Stage 15: search
| `ollama_suspicious_prompt_injection_jailbreak_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.
| Field | Kind | Values |
|---|---|---|
long_request_count | gt |
|
response_time_seconds | gt |
|
sourcetype | eq |
|
Search terms
Bare-string tokens in the SPL search body. Splunk matches each token against _raw (the untyped raw event text) anywhere it appears, not against a specific field. These don't surface in the Indicators table because they aren't predicates on a known field.
| Stage | Term |
|---|---|
| 1 | "GIN" |
| 1 | "*/api/generate*" |
| 1 | "*/v1/chat/completions*" |