Detection rules › Splunk
Linux Dirty Frag Kernel Privilege Escalation
The following analytic detects exploitation of Dirty Frag (CVE-2026-43284 and CVE-2026-43500), a Linux kernel local privilege escalation vulnerability. The exploit corrupts the kernel page cache via the IPsec ESP or RxRPC subsystems using a high-frequency splice() syscall loop, followed by a privilege transition to root in the same audit session. This analytic anchors on the exploit-specific behavioral signature, a sustained splice() spray (50 or more calls within a 60-second window) from an unprivileged process executing from a user-writable path (/home, /tmp, /dev/shm, /var/tmp, /run/user, /root), followed within 5 minutes by execution of a set-uid binary in the same auditd session.
Known false positives
- The splice volume threshold (50 calls per 60 seconds from a single process in a user-writable path) is calibrated against the V4bel public PoC, which produces 100-460 splice calls in 90 seconds depending on system state. Legitimate user-mode workloads that may produce splice() calls (web servers, mail transfer agents, language runtimes) typically execute from system paths (/usr/sbin, /usr/bin, /usr/lib) and are excluded by the user-writable path filter. Custom-compiled applications, developer test binaries, or container runtimes executing from /home or /tmp could theoretically reach the volume threshold during legitimate I/O-heavy workloads (large file copies, log rotation, archive operations). Operators should baseline splice volume in their environment before enabling as a notable event. If false positives occur, add an allowlist of trusted binary paths via the linux_dirty_frag_kernel_privilege_escalation_filter macro, or raise the splice_count threshold based on observed legitimate-workload baseline.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Privilege Escalation |
Telemetry coverage
| Platform | Record / event type |
|---|---|
| Linux | auditd SYSCALL record: System call event information |
Rule body
name: Linux Dirty Frag Kernel Privilege Escalation
id: 517e7bf8-d0aa-46ac-80e1-f9f498944b71
version: 1
creation_date: '2026-07-06'
modification_date: '2026-07-06'
author: Axsel Riando Soplanit, Alexander Wijaya, Matthew Roytua, Muhammad Rafdi Aufar Ahmad, Brian Stevan Ambarita, Ensign Infosecurity
status: production
type: TTP
description: |-
The following analytic detects exploitation of Dirty Frag (CVE-2026-43284 and CVE-2026-43500), a Linux kernel local privilege escalation vulnerability.
The exploit corrupts the kernel page cache via the IPsec ESP or RxRPC subsystems using a high-frequency splice() syscall loop, followed by a privilege transition to root in the same audit session.
This analytic anchors on the exploit-specific behavioral signature, a sustained splice() spray (50 or more calls within a 60-second window) from an unprivileged process executing from a user-writable path (/home, /tmp, /dev/shm, /var/tmp, /run/user, /root), followed within 5 minutes by execution of a set-uid binary in the same auditd session.
data_source:
- Linux Auditd Syscall
search: |-
`linux_auditd`
type=SYSCALL
key=splice_user
exe IN (
"/home/*",
"/tmp/*",
"/var/tmp/*",
"/dev/shm/*",
"/run/user/*",
"/root/*"
)
| bin _time span=60s
| eval user = coalesce(user, auid, AUID)
| stats count AS splice_count
values(exe) AS spray_binary
values(pid) AS spray_pid
values(user) AS user
earliest(_time) AS spray_start
latest(_time) AS spray_end
BY auid host ses _time
| where splice_count >= 50
| join type=inner auid host ses [
search `linux_auditd`
type=SYSCALL
key=process_creation
exe IN (
"/usr/bin/chfn",
"/usr/bin/chsh",
"/usr/bin/fusermount3",
"/usr/bin/gpasswd",
"/usr/bin/mount",
"/usr/bin/newgrp",
"/usr/bin/passwd",
"/usr/bin/su",
"/usr/bin/sudo",
"/usr/bin/umount",
"/usr/lib/dbus-1.0/dbus-daemon-launch-helper",
"/usr/lib/landscape/apt-update",
"/usr/lib/openssh/ssh-keysign",
"/usr/lib/polkit-1/polkit-agent-helper-1"
)
| stats earliest(_time) AS privesc_time
values(exe) AS privesc_binary
values(comm) AS privesc_comm
values(ppid) AS privesc_ppid
BY auid host ses
]
| eval window_secs = privesc_time - spray_end
| where window_secs >= 0 AND window_secs <= 300
| `security_content_ctime(spray_start)`
| `security_content_ctime(spray_end)`
| `security_content_ctime(privesc_time)`
| rename host AS dest
| table dest user auid ses spray_binary spray_pid
splice_count spray_start spray_end privesc_binary
privesc_comm privesc_ppid privesc_time window_secs
| `linux_dirty_frag_kernel_privilege_escalation_filter`
how_to_implement: |-
This analytic requires Linux auditd telemetry forwarded to Splunk
via the Splunk_TA_nix add-on with the linux_audit sourcetype (referenced through
the linux_auditd macro).
The detection depends on two audit rule keys, both of which
are standard in the Neo23x0 recommended ruleset (https://github.com/Neo23x0/auditd)
and the existing ESCU Copy Fail analytic (Linux Auditd Copy Fail Privilege Escalation),
so no detection-specific custom keys are introduced.
PREREQUISITE 1; splice_user key.
-a always,exit -F arch=b64 -S splice -S vmsplice -F success=1 -F auid>=1000 -F auid!=unset
-k splice_user
-a always,exit -F arch=b32 -S splice -S vmsplice -F success=1 -F auid>=1000 -F auid!=unset
-k splice_user
Operators should baseline splice volume in their environment before enabling.
Hosts running heavy zero-copy I/O workloads (specific: web servers, mail transfer agents, container runtimes) may need additional filtering.
PREREQUISITE 2; process_creation key.
-a always,exit -F arch=b64 -S execve -S execveat -F auid>=1000 -F auid!=unset -k
process_creation
-a always,exit -F arch=b32 -S execve -S execveat -F auid>=1000 -F auid!=unset -k
process_creation
known_false_positives: |-
The splice volume threshold (50 calls per 60 seconds from a single process in a user-writable path) is calibrated against the V4bel public PoC, which produces 100-460 splice calls in 90 seconds depending on system state.
Legitimate user-mode workloads that may produce splice() calls (web servers, mail transfer agents, language runtimes) typically execute from system paths (/usr/sbin, /usr/bin, /usr/lib) and are excluded by the user-writable path filter.
Custom-compiled applications, developer test binaries, or container runtimes executing from /home or /tmp could theoretically reach the volume threshold during legitimate I/O-heavy workloads (large file copies, log rotation, archive operations).
Operators should baseline splice volume in their environment before enabling as a notable event. If false positives occur, add an allowlist of trusted binary paths via the linux_dirty_frag_kernel_privilege_escalation_filter macro, or raise the splice_count threshold based on observed legitimate-workload baseline.
references:
- https://www.cve.org/CVERecord?id=CVE-2026-43284
- https://www.cve.org/CVERecord?id=CVE-2026-43500
- https://www.cve.org/CVERecord?id=CVE-2026-31431
- https://github.com/V4bel/dirtyfrag
- https://www.openwall.com/lists/oss-security/2026/05/07/8
- https://www.microsoft.com/en-us/security/blog/2026/05/08/active-attack-dirty-frag-linux-vulnerability-expands-post-compromise-risk/
- https://attack.mitre.org/techniques/T1068/
- https://attack.mitre.org/techniques/T1548/001/
finding:
title: Local privilege escalation attempt detected on $dest$ executed by $user$ using $spray_binary$
entity:
field: dest
type: system
score: 50
intermediate_findings:
entities:
- field: user
type: user
score: 50
message: Local privilege escalation attempt detected on $dest$ executed by $user$ using $spray_binary$
threat_objects:
- field: spray_binary
type: process_name
analytic_story:
- Linux Privilege Escalation
asset_type: Endpoint
cve:
- CVE-2026-43284
- CVE-2026-43500
- CVE-2026-31431
mitre_attack_id:
- T1548.001
- T1068
product:
- Splunk Enterprise
- Splunk Enterprise Security
- Splunk Cloud
category: endpoint
security_domain: endpoint
Stages and Predicates
Stage 1: search
`linux_auditd`
type=SYSCALL
key=splice_user
exe IN (
"/home/*",
"/tmp/*",
"/var/tmp/*",
"/dev/shm/*",
"/run/user/*",
"/root/*"
)
Stage 2: bucket
| bin _time span=60s
Stage 3: eval
| eval user = coalesce(user, auid, AUID)
Stage 4: stats
| stats count AS splice_count
values(exe) AS spray_binary
values(pid) AS spray_pid
values(user) AS user
earliest(_time) AS spray_start
latest(_time) AS spray_end
BY auid host ses _time
Stage 5: where
| where splice_count >= 50
Stage 6: join
| join type=inner auid host ses [
search `linux_auditd`
type=SYSCALL
key=process_creation
exe IN (
"/usr/bin/chfn",
"/usr/bin/chsh",
"/usr/bin/fusermount3",
"/usr/bin/gpasswd",
"/usr/bin/mount",
"/usr/bin/newgrp",
"/usr/bin/passwd",
"/usr/bin/su",
"/usr/bin/sudo",
"/usr/bin/umount",
"/usr/lib/dbus-1.0/dbus-daemon-launch-helper",
"/usr/lib/landscape/apt-update",
"/usr/lib/openssh/ssh-keysign",
"/usr/lib/polkit-1/polkit-agent-helper-1"
)
| stats earliest(_time) AS privesc_time
values(exe) AS privesc_binary
values(comm) AS privesc_comm
values(ppid) AS privesc_ppid
BY auid host ses
]
Stage 7: eval
| eval window_secs = privesc_time - spray_end
Stage 8: where
| where window_secs >= 0 AND window_secs <= 300
Stage 9: search
| `security_content_ctime(spray_start)`
Stage 10: search
| `security_content_ctime(spray_end)`
Stage 11: search
| `security_content_ctime(privesc_time)`
Stage 12: rename
| rename host AS dest
Stage 13: table
| table dest user auid ses spray_binary spray_pid
splice_count spray_start spray_end privesc_binary
privesc_comm privesc_ppid privesc_time window_secs
Stage 14: search
| `linux_dirty_frag_kernel_privilege_escalation_filter`
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
exe | in |
| field:"exe" kind:in |
key | eq |
| field:"key" kind:eq |
sourcetype | eq |
| field:"sourcetype" kind:eq value:"auditd" |
splice_count | ge |
| field:"splice_count" kind:ge value:"50" |
type | eq |
| field:"type" kind:eq value:"SYSCALL" |
window_secs | ge |
| field:"window_secs" kind:ge value:"0" |
window_secs | le |
| field:"window_secs" kind:le value:"300" |