LinuxGod.net
Linux大神网——精选每一篇高品质的技术干货
  1. 首页
  2. 开源快讯
  3. 正文

LinuxKernel5.7引入了第三种方式:LSM扩展伯克利包过滤器

2023年1月8日 102点热度

前言

Linux Security Modules(LSM)是一个钩子的基于框架,用于在Linux内核中实现安全策略和强制访问控制。直到现在,能够实现实施安全策略目标的方式只有两种选择,配置现有的LSM模块(如AppArmor、SELinux),或编写自定义内核模块。

Linux Kernel 5.7引入了第三种方式:LSM扩展伯克利包过滤器(eBPF)(简称BPF LSM)。LSM BPF允许开发人员编写自定义策略,而无需配置或加载内核模块。LSM BPF程序在加载时被验证,然后在调用路径中,到达LSM钩子时被执行。实践出真知Namespaces 命名空间

现代操作系统提供了允许对内核资源进行partitioning的工具。例如FreeBSD有jails,Solaris有zones。Linux不一样,提供了一组看似独立的工具,每个进程都允许隔离特定的资源。他就是Namespaces,经过多年来不停迭代,孕育了Docker、lxc、firejail应用。大部分Namespaces是没有争议的,如UTS命名空间,它允许主机系统隐藏主机名和时间。其他的则比较复杂但简单明了————众所周知,NET和NS(mount)命名空间很难让人理解。最后,还有一个非常特殊、非常有趣的USER Namespaces。

USER Namespaces很特别,因为它允许所有者作为root操作。其工作原理超出了本文的范围,但是,可以说它是让Docker等工具不作为真正的root操作,或者说是rootless容器。

由于其特性,允许未授权用户访问USER Namespaces总是会带来很大的安全风险。其中最大的风险是提权。

【文章福利】小编推荐自己的Linux内核源码交流群:【869634926】整理了一些个人觉得比较好的学习书籍、视频资料共享在群文件里面,有需要的可以自行添加哦!!!前50名可进群领取红旗linux系统下载,并额外赠送一份价值600的内核资料包(含视频教程、电子书、实战项目及代码)!

linux漏洞修复_0day漏洞是被公布的未修复漏洞吗_linux bash 漏洞修复

点击下方链接即可免费领取内核相关学习资料哦

学习直通车:Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议栈

提权原理

提权是操作系统的常见攻击面。user获得权限的一种方法是通过unshare syscall将其命名空间映射到root空间,并指定CLONE_NEWUSER标志。这会告诉unshare创建一个具有完全权限的新用户命名空间,并将新用户和Group ID映射到以前的命名空间。即使用unshare(1)程序将root映射到原始命名空间:

$ id
uid=1000(fred) gid=1000(fred) groups=1000(fred) …
$ unshare -rU
# id
uid=0(root) gid=0(root) groups=0(root),65534(nogroup)
# cat /proc/self/uid_map
         0       1000          1

多数情况下,使用unshare是没有风险的,都是以较低的权限运行。但是,已经被用于提权了,比如CVE-2022-0492,那么本文就重点以这个场景为例。

Syscalls clone和clone3也很值得研究,都有CLONE_NEWUSER的功能。但在这篇文章中,我们将重点关注unshare。

Debian用add sysctl to disallow unprivileged CLONE_NEWUSER by default[8]补丁解决了这个问题,但它没有被合并到源码mainline主线中。另一个类似的补丁sysctl: allow CLONE_NEWUSER to be disabled 尝试合并到mainline,但被拒绝了。理由是在某些特定应用中无法切换到该特性。在Controlling access to user namespaces一文中,作者写道:

... 目前的补丁似乎没有一条通往mainline主线的捷径。

如你所示,补丁最终没有包含到vanilla内核中。

我们的解决方案LSM BPF

基于上面一些经验,可以看到限制USER Namespaces的代码似乎行不通,我们决定使用LSM BPF来规避这些问题。并且不需要修改内核,还可以自定义检测防御的规则。

寻找合适的候选钩子

首先,让我们跟踪我们的目标系统调用。我们可以在include/linux/syscalls.h文件中找到原型。

/* kernel/fork.c */

很清晰得看到,在kernel/fork.c文件中,注释部分中留下了下一个位置的线索。在ksys_unshare()那里调用。深入研究该函数,发现了一个对unshare_userns()的调用。这让我看到了希望。

现在,我们已经确定了syscall实现,但是接下来的问题是用哪些钩子?怎么选择合适的钩子?

从man-pages中了解到unshare用于改变task,那么,我们重点关注include/linux/lsm_hooks.h中的关于task的钩子。在函数unshare_userns()中,可以找到对prepare_creds()的调用。对于cred_prepare的HOOK来说看上去不错。为了验证对prepare_creds()的理解是否正确,接下来继续分析security_prepare_creds()的调用,可以确认,其最终会调用这个HOOK:

…
rc = call_int_hook(cred_prepare, 0, new, old, gfp);
…

暂不过多讨论这个问题,现在能确认的是这个HOOK比较合适,因为prepare_creds()正好在unshare_userns()中的create_user_ns()之前被调用,而unshare_userns()[23]是我们试图阻止的操作。

LSM BPF解决方案

我们将使用eBPF编译一次到处运行(CO-RE)的方法对代码进行编译。在不同版本内核的IDC中,会特别适用。(不过,国内外大部分五至十年的互联网公司,都有着大量低于5.0的内核版本)。本文的演示,将只对x86_64 CPU架构系统验证。ARM64的LSM BPF仍在开发中。你可以订阅BPF邮件列表来了解最新进展。

此解决方案在Kernel Version >=5.15上进行了测试,配置如下:

BPF_EVENTS

linux漏洞修复_0day漏洞是被公布的未修复漏洞吗_linux bash 漏洞修复

BPF_JIT BPF_JIT_ALWAYS_ON BPF_LSM BPF_SYSCALL BPF_UNPRIV_DEFAULT_OFF DEBUG_INFO_BTF DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT DYNAMIC_FTRACE FUNCTION_TRACER HAVE_DYNAMIC_FTRACE

如果CONFIG_LSM列表中不包含bpf,则需要你自己重新编译,并开启lsm=bpf选项.

内核空间代码

开始看内核空间代码:deny_unshare.bpf.c:

#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 

#define X86_64_UNSHARE_SYSCALL 272
#define UNSHARE_SYSCALL X86_64_UNSHARE_SYSCALL

CO-RE

接下来,我们以下列方式为CO-RE重新定位建立必要的结构:deny_unshare.bpf.c:

…
typedef unsigned int gfp_t;
struct pt_regs {
 long unsigned int di;
 long unsigned int orig_ax;
} __attribute__((preserve_access_index));
typedef struct kernel_cap_struct {
 __u32 cap[_LINUX_CAPABILITY_U32S_3];
} __attribute__((preserve_access_index)) kernel_cap_t;

linux漏洞修复_linux bash 漏洞修复_0day漏洞是被公布的未修复漏洞吗

struct cred { kernel_cap_t cap_effective; } __attribute__((preserve_access_index)); struct task_struct { unsigned int flags; const struct cred *cred; } __attribute__((preserve_access_index)); char LICENSE[] SEC("license") = "GPL"; …

用户空间

加载程序并将其附加到目标的钩子上是用户空间的功能。有几种方法可以做到这一点:

Cilium ebpf项目Rust bindingsebpf.io项目landscape展示的其他类库

这里,我们将使用原生libbpf

#include 
#include 
#include "deny_unshare.skel.h"

static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
{
    return vfprintf(stderr, format, args);
}
int main(int argc, char *argv[])
{
    struct deny_unshare_bpf *skel;
    int err;
    libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
    libbpf_set_print(libbpf_print_fn);
    // Loads and verifies the BPF program
    skel = deny_unshare_bpf__open_and_load();
    if (!skel) {
        fprintf(stderr, "failed to load and verify BPF skeletonn");
        goto cleanup;
    }
    // Attaches the loaded BPF program to the LSM hook

err = deny_unshare_bpf__attach(skel); if (err) { fprintf(stderr, "failed to attach BPF skeletonn"); goto cleanup; } printf("LSM loaded! ctrl+c to exit.n"); // The BPF link is not pinned, therefore exiting will remove program for (;;) { fprintf(stderr, "."); sleep(1); } cleanup: deny_unshare_bpf__destroy(skel); return err; }

Makefile

最后,进行编译linux漏洞修复,这里使用Makefile

CLANG ?= clang-13
LLVM_STRIP ?= llvm-strip-13
ARCH := x86
INCLUDES := -I/usr/include -I/usr/include/x86_64-linux-gnu
LIBS_DIR := -L/usr/lib/lib64 -L/usr/lib/x86_64-linux-gnu
LIBS := -lbpf -lelf
.PHONY: all clean run
all: deny_unshare.skel.h deny_unshare.bpf.o deny_unshare
run: all
 sudo ./deny_unshare
clean:
 rm -f *.o
 rm -f deny_unshare.skel.h
#
# BPF is kernel code. We need to pass -D__KERNEL__ to refer to fields present
# in the kernel version of pt_regs struct. uAPI version of pt_regs (from ptrace)

# has different field naming. # See: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=fd56e0058412fb542db0e9556f425747cf3f8366 # deny_unshare.bpf.o: deny_unshare.bpf.c $(CLANG) -g -O2 -Wall -target bpf -D__KERNEL__ -D__TARGET_ARCH_$(ARCH) $(INCLUDES) -c $< -o $@ $(LLVM_STRIP) -g $@ # Removes debug information deny_unshare.skel.h: deny_unshare.bpf.o sudo bpftool gen skeleton $< > $@ deny_unshare: deny_unshare.c deny_unshare.skel.h $(CC) -g -Wall -c $< -o $@.o $(CC) -g -o $@ $(LIBS_DIR) $@.o $(LIBS) .DELETE_ON_ERROR:

结果

打开一个新终端,运行命令

make run
…
LSM loaded! ctrl+c to exit.

在另一个终端里,可以看到成功的被阻止了。

$ unshare -rU
unshare: unshare failed: Cannot allocate memory
$ id
uid=1000(fred) gid=1000(fred) groups=1000(fred) …

这个策略有个附加的特性linux漏洞修复,可以允许传递授权。

$ sudo unshare -rU
# id
uid=0(root) gid=0(root) groups=0(root)

在无特权场景中,系统调用会提前中止。有特权情况下的性能影响是什么?

性能对比

我们将使用一行unshare命令来映射用户命名空间,并在中执行一个命令来进行测量:

$ unshare -frU --kill-child -- bash -c "exit 0"

使用系统调用unshare enter/exit的CPU周期分辨率,我们将以root用户身份测量以下内容:

命令在没有策略的情况下运行与策略一起运行的命令

我们将使用ftrace记录测量结果:

$ sudo su

0day漏洞是被公布的未修复漏洞吗_linux漏洞修复_linux bash 漏洞修复

# cd /sys/kernel/debug/tracing # echo 1 > events/syscalls/sys_enter_unshare/enable ; echo 1 > events/syscalls/sys_exit_unshare/enable

此时,我们将专门为unshare启用对系统调用enter/exit的跟踪。现在linux开源软件,我们设置enter/exit调用的time-resolution来计算CPU周期:

# echo 'x86-tsc' > trace_clock 

接下来,我们开始评测

# unshare -frU --kill-child -- bash -c "exit 0" &
[1] 92014

在新终端里运行策略,执行下一个syscall

# unshare -frU --kill-child -- bash -c "exit 0" &
[2] 92019

现在,我们收集到两种CALLS结果进行对比

# cat trace
# tracer: nop
#
# entries-in-buffer/entries-written: 4/4   #P:8
#
#                                _-----=> irqs-off
#                               / _----=> need-resched
#                              | / _---=> hardirq/softirq
#                              || / _--=> preempt-depth
#                              ||| / _-=> migrate-disable
#                              |||| /     delay
#           TASK-PID     CPU#  |||||  TIMESTAMP  FUNCTION
#              | |         |   |||||     |         |
         unshare-92014   [002] ..... 762950852559027: sys_unshare(unshare_flags: 10000000)
         unshare-92014   [002] ..... 762950852622321: sys_unshare -> 0x0
         unshare-92019   [007] ..... 762975980681895: sys_unshare(unshare_flags: 10000000)
         unshare-92019   [007] ..... 762975980752033: sys_unshare -> 0x0

分别是:

可以看到二者之间有6,844(~10%)个周期的差异,还行。

两次测量之间有6,844(~10%)个周期损失。不错嘛!

这些数字是针对单个系统调用的,代码调用的频率越高,这些数字就越多。Unshare通常在任务创建时调用,在程序的正常执行期间不会重复调用。对于你的场景,需要仔细考虑评估。

结尾

我们了解了LSM BPF是什么,如何使用unshare将user映射到root,以及如何通过在eBPF中实现程序来解决真实场景的问题。跟踪准确的钩子不是一件容易的事,需要有丰富的经验,以及丰富的内核代码经验。这些一个策略代码是用C语言编写的,所以我们可以根据因地制宜,不同的问题做不同的策略,代码经过轻微调整,就可以快速扩展,增加其他钩子点等。最后,我们对比了这个LSM程序的性能影响,性能与安全的权衡,是你需要考虑的问题。

Cannot allocate memory(无法分配内存)不是拒绝权限的最准确的描述。我们提出了一个补丁,用于将错误代码从cred_prepare挂钩传到调用堆栈。

最后,我们的结论就是eBPF LSM钩子非常适合实时修复Linux内核漏洞,你要来试试吗?

原文链接:Live-patching security vulnerabilities inside the Linux kernel with eBPF Linux Security Module

本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: 钩子程序
最后更新:2023年1月8日

Linux大神网

每日更新,欢迎收藏♥ 不积跬步无以至千里,加油,共勉。

点赞
< 上一篇
下一篇 >

Linux大神网

每日更新,欢迎收藏♥
不积跬步无以至千里,加油,共勉。

最新 热点 随机
最新 热点 随机
查看系统版本 linux “”的有关知识,不少人都会遇到这样的困境 Linux在内核中是如何记录进程资源的?你能从C语言源代码层面分析下吗? 【】原操作系统配备内核源代码,更不能进行内核模块实验 【Linux基础知识】与文件权限管理的Linux关系 linux/管理员管理员发布于5年前34(图) Linux和Unix操作系统之间有区别但也有联系?(一) Ubuntu系统如何升级和更新Linux内核版本?将推荐 Linux内核技术组成、组织和重要的数据结构等(组图) 【报错日志】一下升级linux内核的启动顺序为0 Linux终端窗口中输入mysql-V以显示MySQL版本信息sky 计算机操作系统全新版装系统盘点、理念与系统的优缺点 linux中常用的用户管理命令:1groups?列出当前用户所属 Linux下如何设置开机启动启动脚本?学算法 Linux中已经安装好了mysql命令 Linux系统用户系统上的三种类型的帐户的介绍 Linux下的开机启动设置方法是什么?脚本或服务 谷歌更新Linux内核构建的公共内核库:添加对kokoro作业的支持 linux下mysql中可以使用REVOKE语句来删除某个用户的权限 内核的角度来看,调用hotplug和通常的hotplug环境 Linux系统在开机的时候自动加载某些脚本或系统服务
Linux5.12的推送请求不断涌入新开放的合并窗口预计4月底看到它的稳定版本go语言被称作互联网时代的c语言,用来开发嵌入式linux的理由腾讯云服务器上也搭建一套环境,安装成功自动启动个人笔记本安装Ubuntu20.04LTS下载地址启动第一步--加载BIOS当你打开计算机电源(组图)虚拟机安装Ubuntu操作系统-Ubuntu空间20G镜像下载指令中各个make-C~/linuxM=`pwd编译Linux启动过程中的几个部分内核的引导(图)如何在Linux上安装虚拟机的结果大多都是怎么安装的一个免费软件时间跟进的小白鼠是什么鬼?专题计算机是如何启动的?、内核操作系统的启动流程学习Linux最简单、最实用的环境就是虚拟机环境(上)基于命令修改文件的权限命令-ld1.Linux磁盘分区和目录Linux发行版本之间的差别很少?国内性价比很高的Linux虚拟主机系统安装的流程是什么?Linux下修改文件权限的权限与所有权的实现就显得很有必要linux到底难不难学呢?推荐可以查看Linux命令大全Linux中修改文件权限的命令、创建者所在组、所有人Linux的内核放在了哪里?/boot的启动目录一览阿里云>社区>主题地图S>查看存储推荐
Linux系统性能调优工具的说明书常规的用法介绍 【报错日志】一下升级linux内核的启动顺序为0 Linux换国内镜像源(概念问题)--Linux 版本号的信息有以下几个:内核版本号编译器发行系统版本号 0渗透操作0x02绕过disable_functions插件(组图) Linux版本(1)RedHatLinux内核0.02版正式诞生 查看系统版本 linux “”的有关知识,不少人都会遇到这样的困境 基于linux搭建一个,记录一下关键的坑怎么处理? 一个/sys/bus/iio/devices:device0传感器 Linux基础命令推荐书籍:《Linux就该这么学》 virtualbox安装virtualbox增强功能,虚拟机下载 linux访问ftp服务器命令 在文档中查找字符串用grep命令的使用:几个 普联技术有限公司程序的安装包使用方法通过-LINK Linux总结出来的11个炫酷的Linux终端命令大全! RedHat查看内核版本和厂商版本1.查看版本的命令 成为一名合格的嵌入式Linux开发工程师需要学习哪些知识? 2.5创建和配置虚拟机2.5.1创建虚拟机双击桌面的VMwarePlayer Ubuntu18验证dhcp服务器配置是否成功?.04的配置 启动第一步--加载BIOS当你打开计算机电源(组图)
标签聚合
linux系统 文件目录 shell 软件 linux服务器 命令模式 unix 虚拟机 sudo 电脑
书籍
课程
技术群
技术干货大合集↓
  • 2023年2月 / 26篇
  • 2023年1月 / 161篇
  • 2022年12月 / 187篇
  • 2022年11月 / 76篇

COPYRIGHT © 2023 linuxgod.net ALL RIGHTS RESERVED.