转自 https://www.freebuf.com/column/208928.html,本文已归纳的很好,如有补充我直接补充在文章中不单独标注说明了.

内核监控方式

目前来看,常见的获取进程创建的信息的方式有以下四种:

  • So preload
  • Netlink Connector
  • Audit
  • Syscall hook

So preload

原理

  1. Linux 中大部分的可执行程序是动态链接的,常用的有关进程执行的函数例如 execve均实现在 libc.so 这个动态链接库中。

  2. Linux 提供了一个 so preload 的机制,它允许定义优先加载的动态链接库,方便使用者有选择地载入不同动态链接库中的相同函数。

结合上述两点不难得出,我们可以通过 so preload 来覆盖 libc.so 中的 execve等函数来监控进程的创建。

演示

  1. 创建文件 hook.c ,内容如下:

    1
    #define _GNU_SOURCE
    2
    #include <stdio.h>
    3
    #include <unistd.h>
    4
    #include <dlfcn.h>
    5
    6
    typedef ssize_t (*execve_func_t)(const char* filename, char* const argv[], char* const envp[]);
    7
    static execve_func_t old_execve = NULL;
    8
    9
    int execve(const char* filename, char* const argv[], char* const envp[]) {
    10
    printf("Running hook\n");
    11
    printf("Program executed: %s\n", filename);
    12
    old_execve = dlsym(RTLD_NEXT, "execve");
    13
    return old_execve(filename, argv, envp);
    14
    }

    该文件的主要部分就是重新定义了 execve函数,在原始的 execve执行之前打印可执行文件的名字。

  2. 生成动态链接库:gcc hook.c-fPIC-shared-o hook.so

  3. 将上面生成的动态链接库注册成 preload :echo'/path/to/hook.so'>/etc/ld.so.preload

  4. 退出当前 shell 并重新登录(下面会讲原因),执行命令即可看到我们编写的代码已被执行:

图片描述

使用条件

该方法没有什么条件限制,只需有 root 权限即可(做入侵监控程序 root 权限是必需的,后面的几种方法默认也都是在 root 权限下)

优缺点

优点

  • 轻量级,只修改库函数代码,不与内核进行交互。

缺点

  • 只能影响在 preload 之后创建的进程

  • 无法监控静态链接的程序

  • 通过 int80h绕过 libc 直接调用系统调用

Netlink Connector

原理

Netlink 是一个套接字家族(socket family),它被用于内核与用户态进程以及用户态进程之间的 IPC 通信,我们常用的 ss命令就是通过 Netlink 与内核通信获取的信息。

Netlink Connector 是一种 Netlink ,它的 Netlink 协议号是 NETLINK_CONNECTOR,其代码位于 https://github.com/torvalds/linux/tree/master/drivers/connector 中,其中 connectors.c 和 cnqueue.c 是 Netlink Connector 的实现代码,而 cnproc.c 是一个应用实例,名为进程事件连接器,我们可以通过该连接器来实现对进程创建的监控。

图片描述

具体流程:

图片描述

图中的 ncp 为 Netlink Connector Process,即用户态我们需要开发的程序。

演示

在 Github 上已有人基于进程事件连接器开发了一个简单的进程监控程序:https://github.com/ggrandes-clones/pmon/blob/master/src/pmon.c ,其核心函数为以下三个:

  • nl_connect:与内核建立连接
  • set_proc_ev_listen:订阅进程事件
  • handle_proc_ev:处理进程事件

其执行流程正如上图所示。

我们通过 gcc pmon.c-o pmon生成可执行程序,然后执行该程序即可看到效果:

图片描述

获取到的 pid 之后,再去 /proc/目录下获取进程的详细信息即可。

使用条件

内核支持 Netlink Connector

  • 版本 > 2.6.14
  • 内核配置开启: cat /boot/config-$(uname -r)|egrep 'CONFIG_CONNECTOR|CONFIG_PROC_EVENTS'

优缺点

优点

  • 轻量级,在用户态即可获得内核提供的信息。

缺点

  • 仅能获取到 pid ,详细信息需要查 /proc/,这就存在时间差,可能有数据丢失。

Audit

Linux Audit 是 Linux 内核中用来进行审计的组件,可监控系统调用和文件访问。可用来

  • Watching file access
  • Monitoring system calls
  • Recording commands run by a user
  • Recording security events
  • Monitoring network access

具体架构如下 :

图片描述

  1. 用户通过用户态的管理进程配置规则(例如图中的 go-audit ,也可替换为常用的 auditd ),并通过 Netlink 套接字通知给内核。
  2. 内核中的 kauditd 通过 Netlink 获取到规则并加载。
  3. 应用程序在调用系统调用和系统调用返回时都会经过 kauditd ,kauditd 会将这些事件记录下来并通过 Netlink 回传给用户态进程。
  4. 用户态进程解析事件日志并输出。

演示

从上面的架构图可知,整个框架分为用户态和内核态两部分,内核空间的 kauditd 是不可变的,用户态的程序是可以定制的,目前最常用的用户态程序就是 auditd ,除此之外知名的 osquery 在底层也是通过与 Audit 交互来获取进程事件。下面我们就简单介绍一下如何通过 auditd 来监控进程创建。

首先安装并启动 auditd :

1
apt update && apt install auditd
2
systemctl start auditd && systemctl status auditd

auditd 软件包中含有一个命名行控制程序 auditctl,我们可以通过它在命令行中与 auditd 进行交互,用如下命令创建一个对 execve这个系统调用的监控:

1
auditctl -a exit,always -F arch=b64 -S execve

再通过 auditd 软件包中的 ausearch来检索 auditd 产生的日志:

1
ausearch -sc execve | grep /usr/bin/id

整个过程的执行结果如下:

图片描述

至于其他的使用方法可以通过 man auditdman auditctl来查看。

使用条件

内核开启 Audit

  • cat/boot/config-$(uname-r)|grep^CONFIG_AUDIT

优缺点

优点

  • 组件完善,使用 auditd 软件包中的工具即可满足大部分需求,无需额外开发代码。
  • 相比于 Netlink Connector ,获取的信息更为全面,不仅仅是 pid 。

缺点

  • 性能消耗随着进程数量提升有所上升,需要通过添加白名单等配置来限制其资源占用。

关于性能消耗:

开启了osquery的审计功能之后,会在两个方面存在性能损耗:

  1. 当开启了内核中审计功能之后并且存在审计规则,那么内核每次都会比对审计规则和实际产生的审计事件。
  2. 这个audit consumer(在本例中是osquery)将会从内核netlink socket中接受数据,然后将数据解析为了其内部定义的表的格式(socket_eventsprocess_events)并保存在早RocksDB中。最后一旦这个数据被查询,那么这个数据就会被写入到文件或者是通过日志插件发送。

每一次在内核中产生系统调用,就会产生相应的审计事件。如果这样的系统调用越多,那么内核产生这些系统调用的审计事件的工作量就越大,同时osquery解析这些审计事件并且保存在数据库中的工作量也越大。

Syscall hook

上面的 Netlink Connector 和 Audit 都是 Linux 本身提供的监控系统调用的方法,如果我们想拥有更大程度的可定制化,我们就需要通过安装内核模块的方式来对系统调用进行 hook 。

原理

目前常用的 hook 方法是通过修改 sys_call_table( Linux 系统调用表)来实现,具体原理就是系统在执行系统调用时是通过系统调用号在 sys_call_table中找到相应的函数进行调用,所以只要将 sys_call_tableexecve对应的地址改为我们安装的内核模块中的函数地址即可。

上述具体的实现细节可参考 YSRC 的这篇关于驭龙 HIDS 如何实现进程监控的文章:https://mp.weixin.qq.com/s/ntE5FNM8UaXQFC5l4iKUUw ,这里贴出文章里的一张图方便大家对整个流程有个直观地了解:

图片描述

演示

关于 Syscall hook 的 Demo ,我在 Github 上找了很多 Demo 代码,其中就包括驭龙 HIDS 的 hook 模块,但是这些都无法在我的机器上( Ubuntu 16.04 Kernel 4.4.0-151-generic )正常运行,这也就暴露了 Syscall hook 的兼容性问题。

最后我决定使用 Sysdig 来进行演示,Sysdig 是一个开源的系统监控工具,其核心原理是通过内核模块监控系统调用,并将系统调用抽象成事件,用户根据这些事件定制检测规则。作为一个相对成熟的产品,Sysdig 的兼容性做得比较好,所以这里用它来演示,同时也可以方便大家自己进行测试。

具体步骤如下:

1.通过官方的安装脚本进行安装:

1
curl -s https://s3.amazonaws.com/download.draios.com/stable/install-sysdig | sudo bash

2.检测内核模块是否已经安全:lsmod|grep sysdig

3.启动对 execve的监控:sysdig evt.type=execve

最终的执行效果如下:

图片描述

有关于 Sysdig 的更多信息可以访问其 wiki 进行获取,另外,Sysdig 团队推出了一个专门用于安全监控的工具 Falco ,Falco 在 Sysdig 的基础上抽象出了可读性更高的检测规则,并支持在容器内部署,同样,大家如果感兴趣可以访问其 wiki 获取更多信息。

使用条件

  • 可以安装内核模块。
  • 需针对不同 Linux 发行版和内核版本进行定制。

优缺点

优点

  • 高定制化,从系统调用层面获取完整信息。

缺点

  • 开发难度大。
  • 兼容性差,需针对不同发行版和内核版本进行定制和测试。

总结

本文共讲了4种常见的监控进程创建的方法,这些方法本质上是对库函数或系统调用的监控,各有优劣,这里我再各用一句话总结一下:

1
So preload :Hook 库函数,不与内核交互,轻量但易被绕过。
2
Netlink Connector :从内核获取数据,监控系统调用,轻量,仅能直接获取 pid ,其他信息需要通过读取 proc/<pid>/来补全。
3
Audit :从内核获取数据,监控系统调用,功能多,不只监控进程创建,获取的信息相对全面。
4
Syscall hook :从内核获取数据,监控系统调用,最接近实际系统调用,定制度高,兼容性差。

对我个人来讲,单纯地看监控进程创建这方面,我还是更推荐使用 Netlink Connector 的方式,这种方式在保证从内核获取数据的前提下又足够轻量,方便进行定制化开发。如果是想要进行全方面的监控包括进程、网络和文件,Audit 是一个不错的选择。

参考:

https://blog.csdn.net/whuzm08/article/details/87267956

https://blog.spoock.com/2019/01/13/auditing-with-osquery/

https://segmentfault.com/a/1190000019828080

https://4hou.win/wordpress/?p…

https://tech.meituan.com/2019…

https://www.ibm.com/developer…

https://linux-audit.com/confi…

https://my.oschina.net/macwe/…

https://mp.weixin.qq.com/s/nt…

https://mp.weixin.qq.com/s?__…

https://github.com/draios/sysdig

https://github.com/falcosecur…