🎯 CVE-2026-43284 深度技术分析:漏洞根因 · PoC/EXP · 检测指纹
CVE-2026-43284 深度技术分析
摘要:CVE-2026-43284 是 Linux 内核 xfrm/ESP 子系统中的一个本地权限提升漏洞,NVD 评分为 CVSS 7.8(High)。漏洞根源是 IPv4/IPv6 datagram append 路径在使用 MSG_SPLICE_PAGES 将管道页面拼接到 UDP skb 时,遗漏了 SKBFL_SHARED_FRAG 标志,导致后续 ESP 输入路径把外部共享页误判为 skb 私有页,并走 no-COW 快速路径进行 in-place 解密,最终把解密数据直接写入非私有页面。公开 PoC(dirtyfrag-arm64)已验证可在 AWS Graviton / Ubuntu 24.04.4 上通过 ESP 路径覆写 /usr/bin/su;相关 Dirty-Frag 项目还涉及容器逃逸场景。CISA KEV 目前尚未收录该漏洞,Exploit-DB 暂无完整公开 EXP,但可利用性已充分暴露。
📌 漏洞概述
CVE 编号:CVE-2026-43284
CVSS 评分:7.8(High)
漏洞类型:Linux 内核 skb frag 所有权判断错误导致的本地权限提升 / 页缓存写原语
受影响组件:Linux kernel xfrm/ESP 子系统、IPv4/IPv6 datagram splice 路径
- Linux 网络栈为减少拷贝,通过
MSG_SPLICE_PAGES将管道页直接作为 skb frag 使用。TCP 路径在skb_splice_from_iter()后会设置SKBFL_SHARED_FRAG,表示这些 frag 背后的页面不是 skb 独占。 - IPv4/IPv6 datagram append 路径(
ip_append_data()/ip6_append_data())在把 splice 页装入 UDP skb 时漏设了该标志。 - ESP 输入路径对“未克隆、无 frag_list、非线性 skb”采取 no-COW 快速路径,直接 in-place 解密。由于上述标志缺失,内核无法识别 frag 实际由外部共享页提供,进而对共享页产生写操作,形成类似 Dirty Pipe 的页缓存写入原语。
已确认受影响的运行环境包括 Ubuntu 24.04.4 LTS 的 linux-aws 6.17.0
🧪 PoC 复现
从 GitHub 公开仓库抓取的实际 PoC 代码(仓库)。
📋 代码元数据语言md来源linnemanlabs/dirtyfrag-arm64针对性✅ 已验证与漏洞相关(代码含 CVE 引用)依赖见代码注释/README用法详见代码注释中的使用说明
# dirtyfrag-arm64
arm64/aarch64 port of [V4bel/dirtyfrag](https://github.com/V4bel/dirtyfrag) (CVE-2026-43284,CVE-2026-43500).
Tested on Ubuntu 24.04.4 LTS with `linux-aws 6.17.0-1013-aws` on AWS Graviton (newest available as of this writing).
>**Full writeup with AppArmor bypass analysis,hardening notes,
and detection notes:** [linnemanlabs.com/posts/porting-dirtyfrag-arm64](https://linnemanlabs.com/posts/porting-dirtyfrag-arm64/)
## ⚠️ Ubuntu AppArmor userns restrictions do not reliably prevent this exploit
Ubuntu has two AppArmor sysctls:
- kernel.apparmor_restrict_unprivileged_userns
- kernel.apparmor_restrict_unprivileged_unconfined
Both can be bypassed by chaining `aa-exec` with itself using profiles present on the standard Ubuntu cloud and installer images I tested:
```
aa-exec -p crun -- aa-exec -p crun -- ./dirtyfrag_arm64 --force-esp
```
For more info see [Two Hops and a Shell](https://linnemanlabs.com/posts/two-hops-and-a-shell/) for the full analysis of the Ubuntu AppArmor bypass.
## What's different on arm64
The upstream x86\_64 PoC uses two exploit paths: an ESP/xfrm path that corrupts `/usr/bin/su`,
and an rxrpc/rxkad fallback that corrupts `/etc/passwd`. On arm64 the rxrpc path kernel-oopses and cannot be used. The ESP path works cleanly.
### rxrpc crash: `flush_dcache_page`
On x86\_64,`flush_dcache_page()` is a no-op. x86 has hardware-coherent data/instruction caches. On arm64,
it performs real dcache maintenance and dereferences the `struct page*` metadata. When the rxrpc crypto path (`rxkad_secure_packet` ->`crypto_pcbc_encrypt` ->`skcipher_walk_done`) calls `flush_dcache_page` on a page whose reference has been manipulated through the splice/vmsplice chain,
x86\_64 silently skips it but arm64 hits a translation fault and oopses:
```
pc : flush_dcache_page+0x18/0x58
lr : skcipher_walk_done+0xbc/0x260
crypto_pcbc_encrypt+0xe8/0x1c8 [pcbc]
crypto_skcipher_encrypt+0x48/0xb8
rxkad_secure_packet+0x108/0x270 [rxrpc]
rxrpc_send_data+0x264/0x550 [rxrpc]
```
On the arm64 systems I tested,
denying the `uid_map` write removed the working ESP path. The namespace may still be created,but the process cannot map itself to root inside it or gain the namespaced capabilities needed for XFRM setup. The rxrpc fallback did not provide a working namespace-free privilege-escalation path on arm64,it oopsed the kernel instead.
### ESP-only operation
On arm64,
only the ESP path was viable in my testing. This path requires creating a user and network namespace and then successfully mapping the calling user to root inside that namespace. Distro hardening can break that path in different ways: Ubuntu can deny the `uid_map` write through AppArmor userns restrictions,I have not tested on Debian/RHEL.
### AppArmor: blocked by default,
bypassable on Ubuntu
On the Ubuntu 24.04 AWS image I tested,`apparmor_restrict_unprivileged_userns=1` blocked direct exploitation from my normal SSH shell by denying the `uid_map` write inside the new namespace.
However,with the default `apparmor_restrict_unprivileged_unconfined=0`,
an unconfined user can transition into an existing complain-mode profile (e.g. `runc`) via `aa-exec` and bypass the restriction:
```bash
aa-exec -p runc -- ./dirtyfrag_arm64 --force-esp
```
Setting `kernel.apparmor_restrict_unprivileged_unconfined=1` blocks this path,and is widely recommended currently as a solution to block all paths. However,
adding another aa-exec bypasses that also:
```
aa-exec -p crun -- aa-exec -p crun -- ./dirtyfrag_arm64 --force-esp
```
See the earlier linked posts for details.
### Architecture-specific payload
The exploit overwrites `/usr/bin/su` in the page cache with a minimal static ELF. The upstream PoC embeds an x86\_64 ELF with x86\_64 shellcode. This port replaces it with an equivalent aarch64 ELF:
- ELF `e_machine`: `EM_AARCH64` (183) instead of `EM_X86_64` (62)
- Shellcode: aarch64 instructions using `svc #0` instead of `syscall`
- Syscall numbers: setgid=144,
setuid=146,setgroups=159,execve=221 (vs 106,105,116,59 on x86\_64)
- Fixed 4-byte instruction width (vs x86\_64 variable-length),resulting in a slightly larger payload (~216 bytes vs 192)
## Build &
run
```bash
# Clone
git clone https://github.com/linnemanlabs/dirtyfrag-arm64.git
# Build
cd dirtyfrag-arm64
gcc -O0 -Wall -o dirtyfrag_arm64 dirtyfrag_arm64.c -lutil
# Run
./dirtyfrag_arm64 --force-esp
```
The `--force-esp` flag skips the rxrpc path entirely to ensure avoiding the arm64 kernel oops.
## Tested environment
|Property |Value ||---|---||Instance |AWS t4g.micro (Graviton2) ||OS |
Ubuntu 24.04.4 LTS ||Kernel |`6.17.0-1013-aws #13~24.04.1-Ubuntu` (built 2026-04-24) ||Architecture |aarch64 ||`unprivileged_userns_clone` |1 (enabled) ||esp4 module |available,loadable ||rxrpc module |available,loadable (but crashes on arm64) ||Configuration |stock ubuntu 24.04 cloud image,default kernel modules |As of 2026-05-09,the latest available Ubuntu 24.04 aws kernel (`6.17.0-1013-aws`,
built April 24) ships without patches for either Copy Fail (CVE-2026-31431,disclosed April 29) or Dirty Frag (CVE-2026-43284/43500,disclosed May 7).
## Immediate mitigation
Blacklist the vulnerable modules,
apply appropriate system hardening for your distro.
### Blacklist the vulnerable modules
Safe on any system not actively using IPsec transport mode or AFS. To prevent loading of the modules put the following into `/etc/modprobe.d/dirtyfrag.conf`:
```
install esp4 /bin/false
install esp6 /bin/false
install rxrpc /bin/false
```
### Update initramfs
Ubuntu also recommends regenerating initramfs so the blacklist is present during early boot:
```bash
update-initramfs -u -k all
``
### Remove read permissions from SUID binaries
Blocks this splice-based page-cache attack class against those SUID binary targets. The exploit needs read permissions to the target file for `splice()`. Users can still execute the binaries.
**Do not implement this without testing it in your environment and across all of your tooling.**
```bash
chmod o-r /usr/bin/su
```
This is not a fix,
only a mitigation. There are many more paths to privilege escalation.
### Unload the modules
To unload the modules from the running system:
```bash
rmmod esp4 esp6 ipcomp4 ipcomp6 rxrpc 2>/dev/null
```
Ensure they are unloaded:
```bash
grep -qE '^(esp4|esp6|rxrpc) ' /proc/modules \
&&echo "Affected modules are loaded" \
||
echo "Affected modules are NOT loaded"
```
### Flush page cache
Flushing the page cache should remove the malicious contents and cause the files to be read from disk again.
```bash
echo 3 >/proc/sys/vm/drop_caches
```
Note: I have had some inconsistent results with this,but the more I try to reproduce it the more it's working as expected. For this PoC,
you can check the md5sum on /usr/bin/su,and if it doesn't match,reboot.
### Cleaning up after testing
Run the flush page cache step,
then verify after with:
```bash
sha256sum /usr/bin/su
# Or from package manager:
dpkg -V util-linux # Debian/Ubuntu
rpm -V util-linux # RHEL/Amazon Linux
```
### Proactive moves
For a more proactive posture that addresses this entire vulnerability class (not just the specific CVEs),see the full writeup for AppArmor userns restrictions,module preloading prevention,
and Tetragon-based runtime detection as well as YARA rules.
## Credits
- [Hyunwoo Kim (@v4bel)](https://github.com/V4bel) - original vulnerability research,disclosure,and x86\_64 PoC
- [SiCk](https://afflicted.sh) - bypass-pwn research on Ubuntu AppArmor bypass
- [Keith Linneman / LinnemanLabs](https://linnemanlabs.com) - arm64 port,`flush_dcache_page` crash analysis,AppArmor research,
detection notes
## Legal
This tool is intended for authorized security testing and research only.
Unauthorized use against systems you do not own or have explicit permission to test is illegal and unethical.
## License
MIT. Copy it,steal it,modify it,learn from it,share your improvements with me. Or don't. It's code,do what you want with it.⚔️ EXP 利用代码
截至分析时,Exploit-DB 未收录该 CVE 的公开利用代码。可利用上述 PoC 进行验证,或关注 Exploit-DB 更新。
🕵️ 检测指纹
针对该 CVE 的自动化检测规则(可直接用于扫描与审计)。
🛡️ Semgrep 审计规则: CVE-2026-43284.yaml
📋 代码元数据语言yaml来源rules/semgrep/CVE-2026-43284.yaml针对性✅ 按 CVE 匹配依赖semgrep用法semgrep --config CVE-2026-43284.yaml
rules:
- id: CVE-2026-43284-offbyone-c
languages: [c]
severity: ERROR
message: "Potential off-by-one or fragmentation vulnerability in kernel space (CVE-2026-43284). Avoid improper flush_dcache_page usage and rxrpc path errors on arm64."
patterns:
- pattern: flush_dcache_page(...)
- pattern: rxrpc(...)
fix: "Ensure proper page alignment and bounds checking;
use flush_dcache_page() only with validated struct page pointers."
metadata:
cwe: "CWE-131"
owasp: "A6: Security Misconfiguration"
technology: linux-kernel
references:
- "https://nvd.nist.gov/vuln/detail/CVE-2026-43284"
- id: CVE-2026-43284-kernel-oops-c
languages: [c]
severity: ERROR
message: "Potential kernel oops due to incorrect page cache handling or flush_dcache_page on unsupported paths in arm64."
patterns:
- pattern: flush_dcache_page($PAGE)
- pattern-not: flush_dcache_page(NULL)
fix: "Add architecture-specific condition checks before calling flush_dcache_page();
verify !PageHighMem() and proper mapping."
metadata:
cwe: "CWE-476"
owasp: "A6: Security Misconfiguration"
technology: linux-kernel
references:
- "https://nvd.nist.gov/vuln/detail/CVE-2026-43284"🛡️ CodeQL 审计规则: CVE-2026-43284.ql
📋 代码元数据语言ql来源rules/codeql/CVE-2026-43284.ql针对性✅ 按 CVE 匹配依赖codeql用法codeql database run
/**
* @kind problem
* @id cpp/kernel-memory-corruption/cve-2026-43284
* @name Potential memory corruption in rxrpc path (CVE-2026-43284)
* @description Potential use-after-free or double-free in kernel memory management related to dirtyfrag pattern
* @problem.severity error
* @tags security
* external/cwe/cwe-416
*/
import cpp
import semmle.code.cpp.dataflow.DataFlow
import semmle.code.cpp.dataflow.TaintTracking
class KernelAllocSink extends DataFlow::ExprNode {
KernelAllocSink() {exists(FunctionCall fc |fc.getTarget().getName() = "kmalloc" and
fc.getArgument(0) = this.asExpr()
) or
exists(FunctionCall fc |fc.getTarget().getName() = "kzalloc" and
fc.getArgument(0) = this.asExpr()
)
}}class KernelFreeSource extends DataFlow::FunctionCallNode {KernelFreeSource() {
this.getTarget().getName() = "kfree" and
not this.getTarget().getFile().getRelativePath().matches("%test%") and
not this.getTarget().getFile().getRelativePath().matches("%kunit%")
}}class KernelMemoryDirtyFragConfig extends TaintTracking::Configuration {KernelMemoryDirtyFragConfig() {this = "KernelMemoryDirtyFragConfig" }override predicate isSource(DataFlow::Node source) {
source.asExpr().(FunctionCall).getTarget().getName().matches("rxrpc%") or
source.asExpr().(FunctionCall).getTarget().getName().matches("%dirty%") or
source.asExpr().(FunctionCall).getTarget().getName().matches("flush_dcache_page")
}override predicate isSink(DataFlow::Node sink) {sink instanceof KernelAllocSink or
sink instanceof KernelFreeSource
}
override predicate isAdditionalTaintStep(DataFlow::Node node1,DataFlow::Node node2) {exists(Assignment a |a.getLValue() = node1.asExpr() and
a.getRValue() = node2.asExpr()
) or
exists(FunctionCall fc |fc.getArgument(0) = node1.asExpr() and
fc.getTarget().getName().matches("memcpy") and
fc.getArgument(1) = node2.asExpr()
)
}}from DataFlow::Node source,
DataFlow::Node sink,KernelMemoryDirtyFragConfig config
where config.hasFlow(source,sink)
select sink,"Potential memory corruption flow from $@ to $@",source,"kernel operation",sink,"allocation/free site"🤖 本文由漏洞情报系统自动聚合生成 · 2026-08-06 08:58 · 数据源: NVD/GitHub-Advisory/OSV/CISA-KEV/Exploit-DB/PoC-in-GitHub + 检测规则库