Shell 命令审计
目的与范围
本文档描述了 eCapture 的 shell 命令审计功能,该功能拦截并记录在 Bash 和 Zsh shell 中执行的命令,用于安全审计目的。该模块使用 eBPF uprobe 钩入 GNU readline 库,在不需要 shell 历史文件或配置更改的情况下捕获命令行输入和执行结果。
本页涵盖 Bash 和 Zsh 捕获模块。有关数据库查询审计,请参见数据库查询审计。有关通用模块架构,请参见模块系统与生命周期。
模块架构
shell 命令审计系统由两个共享相同底层架构的并行模块组成:一个用于 Bash,一个用于 Zsh。两个模块都钩入 readline 库以在命令执行前拦截用户输入。
系统组件
来源: cli/cmd/bash.go:1-56, cli/cmd/zsh.go:1-58, user/event/event_bash.go:1-134
配置
Bash 模块配置
Bash 模块接受以下配置参数:
| 参数 | 标志 | 类型 | 标志范围 | 默认值 | 描述 |
|---|---|---|---|---|---|
| Bashpath | --bash | string | 持久化 | 从 $SHELL 自动检测 | bash 可执行文件路径,例如 /bin/bash |
| Readline | --readlineso | string | 持久化 | 从 $BASH_PATH 自动检测 | readline.so 库路径 |
| ErrNo | -e, --errnumber | int | 本地 | module.BashErrnoDefault | 过滤:仅显示具有特定退出码的命令 |
配置结构:
bash 配置通过 config.NewBashConfig() 创建并传递给模块工厂。配置在 cli/cmd/bash.go:24 中实例化:
var bc = config.NewBashConfig()标志注册发生在 cli/cmd/bash.go:36-38 中,其中 --bash 和 --readlineso 作为持久化标志(由子命令继承),-e/--errnumber 作为本地标志(特定于 bash 命令)。
Zsh 模块配置
| 参数 | 标志 | 类型 | 标志范围 | 默认值 | 描述 |
|---|---|---|---|---|---|
| Zshpath | --zsh | string | 持久化 | 从 $SHELL 自动检测 | zsh 可执行文件路径,例如 /bin/zsh |
| ErrNo | -e, --errnumber | int | 本地 | module.ZshErrnoDefault | 过滤:仅显示具有特定退出码的命令 |
zsh 配置遵循与 bash 相同的模式,在 cli/cmd/zsh.go:27 中实例化并在 cli/cmd/zsh.go:39-40 中注册。
来源: cli/cmd/bash.go:24-39, cli/cmd/zsh.go:27-41
命令行用法
bash 和 zsh 命令分别在 cli/cmd/bash.go:39 和 cli/cmd/zsh.go:41 中注册,具有以下用法模式:
# 基本 bash 命令捕获(自动检测 $SHELL 和 readline)
ecapture bash
# 指定自定义 bash 二进制文件和 readline 库路径
ecapture bash --bash=/bin/bash --readlineso=/lib/x86_64-linux-gnu/libreadline.so
# 按退出码过滤:仅显示失败的命令(errno=1)
ecapture bash -e 1
# 按退出码过滤:仅显示成功的命令(errno=0)
ecapture bash -e 0
# 基本 zsh 命令捕获
ecapture zsh
# 带自定义路径和过滤的 zsh
ecapture zsh --zsh=/bin/zsh -e 0命令函数 cli/cmd/bash.go:53-55 和 cli/cmd/zsh.go:55-57 使用适当的模块名称(module.ModuleNameBash 或 module.ModuleNameZsh)和配置对象调用 runModule()。
来源: cli/cmd/bash.go:28-55, cli/cmd/zsh.go:30-57
事件结构
BashEvent 数据布局
从 shell 捕获的命令被编码在 BashEvent 结构中,它镜像了 eBPF 内核侧结构:
来源: user/event/event_bash.go:26-47
字段描述
| 字段 | 类型 | 大小 | 描述 |
|---|---|---|---|
| BashType | uint32 | 4 字节 | 事件类型标识符 |
| Pid | uint32 | 4 字节 | shell 的进程 ID |
| Uid | uint32 | 4 字节 | 执行命令的用户 ID |
| Line | [256]uint8 | 256 字节 | 命令行文本(可能是部分) |
| ReturnValue | uint32 | 4 字节 | 命令的退出码 |
| Comm | [16]byte | 16 字节 | 进程名称(通常是 "bash" 或 "zsh") |
| AllLines | string | 可变 | 聚合的完整命令(用于多块命令) |
最大数据大小: 超过 256 字节的命令在多个事件中捕获,并使用 AllLines 字段重新组装。
来源: user/event/event_bash.go:37-47
事件处理流程
从 Shell 到输出的数据流
来源: user/event/event_bash.go:49-80, cli/cmd/bash.go:38
基于 UUID 的事件关联
每个命令执行生成一个用于事件分组的唯一标识符:
UUID 格式: {Pid}_{Uid}_{Comm}
示例:1234_1000_bash
此 UUID 实现:
- 多块命令捕获的关联
- 来自同一 shell 会话的相关事件分组
- 事件工作器流程中的正确事件排序
来源: user/event/event_bash.go:123-125
退出码过滤
shell 审计模块支持按命令退出码过滤,允许用户关注特定的执行结果。
过滤配置
来源: cli/cmd/bash.go:38, cli/cmd/zsh.go:40
常见用例
| ErrNo 值 | 用例 | 描述 |
|---|---|---|
| 0 | 成功的命令 | 审计成功完成的命令 |
| 1 | 失败的命令 | 检测失败的执行尝试 |
| 127 | 命令未找到 | 识别拼写错误或缺失的二进制文件 |
| 126 | 权限被拒绝 | 跟踪权限问题 |
| 默认值 | 所有命令 | 综合审计跟踪 |
输出格式
文本输出结构
BashEvent.String() 方法为控制台显示格式化捕获的命令:
PID:1234, UID:1000, Comm:bash, Retvalue:0, Line:
ls -la /etc/passwd格式模板: PID:%d, UID:%d, \tComm:%s, \tRetvalue:%d, \tLine:\n%s
颜色编码: 输出使用 user/event/event_bash.go:72-75 的标准颜色方案,尽管具体颜色取决于终端支持。
来源: user/event/event_bash.go:72-75
十六进制输出模式
使用 --hex 标志运行时,命令通过 StringHex() 以十六进制格式显示:
PID:1234, UID:1000, Comm:bash, Retvalue:0, Line:
0000 6C 73 20 2D 6C 61 ls -la来源: user/event/event_bash.go:77-80
库检测与挂钩
Readline 库解析
模块采用多步骤过程定位 readline 库。bash 二进制文件和 readline 库路径通过 cli/cmd/bash.go:36-37 中的持久化标志配置:
readline 库检测遵循 cli/cmd/bash.go:36-37 中定义的标志层次:
- 首先:显式
--readlineso路径 - 其次:从
--bash二进制路径派生 - 第三:使用
$SHELL环境变量 - 最后:回退到系统默认路径
Uprobe 附加点
模块将 eBPF 探针附加到 readline 库中的两个关键点:
| 探针类型 | 函数目标 | 挂钩点 | 捕获的数据 |
|---|---|---|---|
| Uprobe | readline() | 函数入口 | PID(bpf_get_current_pid_tgid())、UID(bpf_get_current_uid_gid())、进程名称(bpf_get_current_comm()) |
| Uretprobe | readline() | 函数返回 | 返回值(指向命令字符串的指针)、通过 bpf_probe_read_user() 读取命令文本内容 |
eBPF 程序填充与 user/event/event_bash.go:26-47 中定义的 Go BashEvent 结构匹配的 bash_event 结构。内核侧结构通过名为 bash_events 的 perf 事件数组传输到用户空间,该数组由 perfEventReader 轮询。
注意: bash 模块挂钩 readline() 而不是 shell 内置命令,以捕获所有交互式命令,无论它们是通过 shell 的内部命令解析器还是外部二进制文件执行。
来源: user/event/event_bash.go:26-47
Protobuf 事件序列化
Shell 事件可以通过 Protobuf/WebSocket 接口导出以进行外部集成:
Protobuf 映射
特殊处理:
- Bash 事件没有网络上下文(SrcIP/DstIP 设置为
127.0.0.1,端口设置为0) - 时间戳使用
time.Now().Unix()而不是内核时间戳 - 有效载荷包含来自
AllLines的完整命令行文本
来源: user/event/event_bash.go:103-117
事件生命周期
事件类型分类
Shell 事件被分类为 TypeModuleData,这将它们路由通过模块特定的处理而不是通用事件处理器:
func (be *BashEvent) Clone() IEventStruct {
event := new(BashEvent)
event.eventType = TypeModuleData // 不是 TypeEventProcessor
return event
}这种分类意味着:
- 事件绕过 HTTP 协议解析
- 不生成 PCAP 输出
- 直接输出到控制台或日志文件
- 模块保持对格式化的控制
来源: user/event/event_bash.go:82-86
多块命令处理
超过 256 字节的命令在多个事件中捕获:
- 当用户按 Enter 时捕获第一个块
- eBPF 程序从 readline 缓冲区读取最多 256 字节
- 如果命令继续,则捕获后续块
- 用户空间代码使用 UUID 关联将块聚合到
AllLines字段中
最大命令长度: 受 eBPF 程序设计和事件聚合逻辑限制(在提供的文件中不可见,但结构通过 AllLines 字符串支持无限长度)。
来源: user/event/event_bash.go:37-47
与其他模块的集成
模块工厂注册
Bash 和 Zsh 模块都在模块工厂中注册并遵循标准模块生命周期。cli/cmd/bash.go:53-55 和 cli/cmd/zsh.go:55-57 中的命令函数使用不同的模块标识符调用 runModule():
module.ModuleNameBash -> MBashProbe -> BashConfig (bc)
module.ModuleNameZsh -> MZshProbe -> ZshConfig (zc)命令执行流程:
// 来自 cli/cmd/bash.go:53-55
func bashCommandFunc(command *cobra.Command, args []string) error {
return runModule(module.ModuleNameBash, bc)
}
// 来自 cli/cmd/zsh.go:55-57
func zshCommandFunc(command *cobra.Command, args []string) error {
return runModule(module.ModuleNameZsh, zc)
}runModule() 函数(在提供的文件中未显示)使用模块名称在工厂注册表中查找适当的模块构造函数,使用提供的配置实例化模块,并调用标准模块生命周期方法(Init()、Run()、Close())。
来源: cli/cmd/bash.go:53-55, cli/cmd/zsh.go:55-57
共享基础设施
Shell 模块利用通用的 eCapture 基础设施:
- 事件流程: 事件处理流程
- 模块生命周期: 模块系统与生命周期
- 配置系统: 配置系统
- 输出格式: 文本输出模式 和 Protobuf 与外部集成
安全考虑
审计跟踪完整性
Shell 命令审计提供:
- 不可绕过的捕获: 在库级别挂钩,在历史文件写入之前
- UID 跟踪: 将命令与用户身份关联
- 退出码跟踪: 区分成功与失败的尝试
- 进程关联: 将命令链接到特定的 shell 会话
隐私影响
该模块捕获:
- 所有交互式命令输入(包括键入的密码)
- 命令参数(可能包含敏感数据)
- 工作目录上下文(通过进程检查)
建议: 在生产环境中部署时使用退出码过滤和安全日志存储。
用例
安全审计
# 跟踪失败的命令尝试(潜在的侦察或拼写错误)
ecapture bash -e 1
# 捕获所有交互式 bash 命令进行取证分析
ecapture bash合规监控
# 捕获所有成功的管理命令
ecapture bash -e 0 -l /var/log/audit/bash_commands.log
# 监控 zsh 会话的合规性
ecapture zsh -e 0调试与支持
# 在故障排除会话期间记录所有命令
ecapture bash > debug_session.log
# 捕获成功和失败的命令
ecapture bash -e -1入侵检测
# 监控命令未找到错误(异常活动)
ecapture bash -e 127
# 监控权限被拒绝错误(权限提升尝试)
ecapture bash -e 126主机安全审计
如 README.md:40 和 README_CN.md:131 中所述,bash 和 zsh 模块的主要目的是"主机安全审计" - 提供交互式 shell 活动的全面审计跟踪,而不依赖可能被禁用或篡改的 shell 历史文件。
来源: cli/cmd/bash.go:28-33, user/event/event_bash.go:72-75, README.md:40, README_CN.md:131