恶意命令绕过与检测
0x01 指令执行方式
1. 通过glibc api执行系统指令
1 | 1) system() glibc api |
2. 通过syscall系统调用执行指令
除了通过glibc调用fork/execv之外,还可以绕过glibc,直接通过汇编触发“int80中断”,从而直接使用操作系统提供的系统调用能力
1 | 1) system syscall |
3. 通过Bash执行指令
1 | 1) Bash内置指令执行 |
4. Python执行命令
1 | 1) os.execv() |
5. 无文件进程启动方式
进程启动的本质是将一段汇编指令(shellcode)从某种媒介上加载到计算机的内存(RAM)中,并触发操作系统的cpu调度,从某个执行的内存地址中开始按照逻辑顺序执行。
操作系统真正需要的是内存中的shellcode代码以及制定入口点虚拟内存空间,至于这个shellcode从哪里来并不重要,可以是从物理磁盘,也可以是网络IO流,或者是虚拟内存设备。
在Linux系统中实现无文件执行ELF是渗透测试中一种非常有用的技术。这种方法较为隐蔽,可以绕过各种类型的反病毒保护机制、系统完整性保护机制以及基于硬盘监控的防护系统。通过这种方法,我们能够以最小的动静访问目标。
1 | 1) 基于linux内存镜像文件 |
0x02 绕过姿势
1. 通配符注入
glob 模式(globbing)也被称之为 shell 通配符,shell 通配符 / glob 模式通常用来匹配目录以及文件,而非文本
字符 | 解释 |
---|---|
* | 匹配任意长度任意字符 |
? | 匹配任意单个字符 |
[list] | 匹配指定范围内(list)任意单个字符,也可以是单个字符组成的集合 |
[^list] | 匹配指定范围外的任意单个字符或字符集合 |
[!list] | 同[^list] |
{str1,str2,...} |
匹配 srt1 或者 srt2 或者更多字符串,也可以是集合 |
shell 元字符
字符 | 作用 |
---|---|
IFS | 由 < space > 或 < tab > 或 < enter > 三者之一组成 |
CR | 由 < enter > 产生 |
= | 设定变量 |
$ | 作变量或运算替换 |
|重导向标准输出
<|重导向标准输入
||命令管线
&|重导向文件描述符,或将命令静默执行
( )|将其内的命令置于 nested subshell 执行,或用于运算或命令替换{ }
|将其内的命令置于 non-named function 中执行,或用在变量替换的界定范围
;|在前一个命令结束时,而忽略其返回值,继续执行下一个命令
&&|在前一个命令结束时,若返回值为 true,继续执行下一个命令||
|在前一个命令结束时,若返回值为 false,继续执行下一个命令
!|执行 history 中的命令
2. 命令注入姿势
a. 无空格绕过
1 | cat</etc/passwd |
b. 回车绕过
1 | $ something%0Acat%20/etc/passwd |
c. 编码绕过
1 | $ echo -e "\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64" |
d. 绕错字符过滤
没有反斜杠和斜杠的命令执行
1 | $ echo ${HOME:0:1} |
e. 绕过黑名单单词
1 | w'h'o'am'i # 使用单引号 |
3. execve绕过
绕过execve监控的关键点在于创建软链接,通过创建软链接的方式来混淆命令执行的路径
1 | $ ln -s /usr/bin/ps /tmp/pstest |
由于execve执行软链接,会自动执行目标文件,不会对软链接进行转换,从而实现隐藏
为避免执行ln命令暴露命令路径,可批量创建软连接
1 | cp -s /usr/bin/* /tmp/ |
0x03 检测方法
1. bash调试功能
利用bash调试功能检测命令注入,检测基于shell元字符的绕过方式,还原原始命令
sh是bash的软连接,本质上还是调用的bash。sh -x可以打印出shell脚本的运行过程,这样就可以看到真正的执行命令;使用sh -x作为沙箱,提取带+号的内容,即可还原命令
2. 文件哈希对比
针对软链接、命令替换等方式可利用文件哈希库对比检测
1 | 1) 文件哈希一致,但二进制文件名不同:检测软链接/命令复制重命名 |
3. 命令执行监控
a. 定制 bash 记录方式
定制 bash 方式本质上是为 bash 源程序增加审计日志的功能, 可以据此添加一些操作命令的上下文信息, 但很难记录子进程的信息
缺点
1 | 1. 容易被绕过, 用户可以使用 csh, zsh 等; |
b. snoopy 记录方式
snoopy 方式本质上是封装了 execv, execve 系统调用, 以系统预加载(preload)的方式实现记录所有的命令操作。目前大部分系统执行命令时都通过 execv, execve 系统调用执行, 这点和会话无关, 几乎所有的情况下, 只要通过这两个系统调用执行命令, 会将操作行为记录下来
优点
1 | 1. 难以绕过, 只要设置了 PRELOAD, 就肯定会记录; |
缺点
1 | 1. 仅支持 execv, execve 相关系统调用的操作; |
过滤规则
snoopy 记录方式能够详细的记录所有的命令操作信息,实际使用过程,可以通过过滤规则来避免产生过多的信息
过滤规则在 (filtering.c - snoopy_filtering_check_chain)
函数实现, 由 log.c - snoopy_log_syscall_exec
函数调用, 过滤规则为事后行为, 即在打印日志的时候判断是否满足过滤规则, 并非事前行为
1 | 1) 忽略 crond, my-daemon 守护进程, 忽略 nagios 用户 |
c. sysdig 记录方式
可以通过 strace 工具来简单追踪进程的行为,strace 程序是基于 ptrace 系统调用实现的, ptrace 为了能够获取到其他系统调用的详细信息, 需要做很多复杂的操作, 如果进程很繁忙, strace 就会对程序产生较大影响,不建议对线上程序使用
sysdig 以内核模块的方式监控获取所有的系统调用, 其使用方式类似 libpcap/tcpdump 的用法, 可以将一段时间内系统调用的数据暂存起来供以后的跟踪分析。对于 系统调用 而言, 用户态层面的操作最终都会陷入到内核态, 由内核去完成对应的功能。所以 sysdig 在内核态也就能很方便的获取到进程的上下文信息;另外 sysdig 以非阻塞(non-blocking), 零拷贝(zero-copy) 的方式获取数据, 所以在实际使用中对在线的业务只有很轻微的影响。
refer: https://github.com/draios/sysdig/wiki/Sysdig-Examples
d. auditd 记录方式
auditd 记录方式 本身存在内核层面(kauditd 进程)的支持, 它实现了一个大而全的框架, 几乎能监控所有想监控的指标, 不管是按照访问模式, 系统调用还是事件类型触发, 都能满足监控需求。因为其提供了内核层面的支持, 所以本质上比起 snoopy(仅封装 execv, execve 系统调用)要更加强大和健全
auditd 整体上为分离的架构, auditctl 可以控制 kauditd 生成记录的策略, kauditd 生成的记录事件会发送到 auditd 守护程序, audisp 可以消费 auditd 的记录到其它地方
name | description |
---|---|
auditd | audit 守护程序, audit 相关配置的加载,日志配置等都通过 auditd 完成 |
auditctl | 用来控制 kernel audit 相关的规则, 过滤通常使用 auditctl 实时修改 |
audisp | 与auditd 守护程序通信, 将收到的记录信息发送到别处, 比如发到 syslog 中 |
augenrules, ausearch, autrace, aureport | audit 辅助分析的工具 |
在实际的使用中, 对于connect, accept, execve 等都是日志高产的行为, 过滤策略需设置的足够详细, 比如忽略指定用户, crond 进程等。一些安全工具(比如 go-audit, hids)实现了与 kauditd 内核进程通信, 可以接收 audit 相关的日志, 这种方式替换了 auditd 服务, 灵活性很强, 可以做很多定制功能需求。
e. eBPF 记录方式
eBPF 在较新版本的 Linux 内核中实现, 提供了动态追踪的机制。bpftrace 和 bcc 是基于 eBPF 机制实现的工具, 方便大家对系统的调试和排错, bcc 提供了很多工具集, 从应用到内核, 不同层面的工具应有尽有, 比如 execsnoop 即可记录系统中所有的 execv, execve 相关的命令执行
eBPF 仅适用于 Linux 4.1+ 的版本, 在 kernel-4.10 之后的支持才相对全面, 线上在使用的时候尽量选择较高内核版本的发行版(比如 Centos 8, Debian 10 等)。另外 Readhat/Centos 7 从 7.6 (3.10.0-940.el7.x86_64) 版本开始支持 eBPF 特性, 不过内核版本较低, 并没有支持所有的特性
4. 进程创建监控
a. So preload方式
可以通过 so preload 来覆盖 libc.so 中的 execve等函数来监控进程的创建
背景知识
1 | 1) Linux 中大部分的可执行程序是动态链接的,常用的有关进程执行的函数例如 execve均实现在 libc.so 这个动态链接库中。 |
优点
1 | 轻量级,只修改库函数代码,不与内核进行交互 |
缺点
1 | 1) 只能检测在 preload 之后创建的进程 |
b. Netlink Connector
Netlink 是一个套接字家族(socket family),它被用于内核与用户态进程以及用户态进程之间的 IPC 通信。
Netlink Connector 是一种 Netlink ,它的 Netlink 协议号是 NETLINK_CONNECTOR,其中 connectors.c 和 cnqueue.c 是 Netlink Connector 的实现代码,而 cnproc.c 是一个应用实例,名为进程事件连接器,可以通过该连接器来实现对进程创建的监控
优点
1 | 轻量级,在用户态即可获得内核提供的信息。 |
缺点
1 | 仅能获取到 pid ,详细信息需要查 /proc/<pid>/,对于瞬时创建的进程,可能有数据丢失 |
c. Audit
Linux Audit 是 Linux 内核中用来进行审计的组件,可监控系统调用和文件访问,架构如下:
1 | 1) 用户通过用户态的管理进程配置规则(如图中的 go-audit ,也可替换为常用的 auditd ),并通过 Netlink 套接字通知给内核。 |
优点
1 | 1) 组件完善,使用 auditd 软件包中的工具即可满足大部分需求,无需额外开发代码。 |
缺点
1 | 性能消耗随着进程数量提升有所上升,需要通过添加白名单等配置来限制其资源占用 |
d. Syscall hook
可以通过安装内核模块的方式来对系统调用进行 hook来监控进程创建
原理
1 | 目前常用的 hook 方法是通过修改 sys_call_table( Linux 系统调用表)来实现,具体原理是系统在执行系统调用时是通过系统调用号在 sys_call_table中找到相应的函数进行调用,所以只要将 sys_call_table中 execve对应的地址改为我们安装的内核模块中的函数地址即可 |
优点
1 | 高定制化,从系统调用层面获取完整信息 |
缺点
1 | 兼容性差,需针对不同发行版和内核版本进行定制和测试 |
0xFF Reference
https://www.junmajinlong.com/shell/script_course/shell_cmdline_parse_eval/
https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Command%20Injection
https://www.cnblogs.com/divent/archive/2016/08/11/5762154.html
https://blog.spoock.com/2018/11/29/osquery-source-analysis-shell-history/
https://blog.arstercz.com/how-does-snoopy-log-every-executed-command/
https://blog.arstercz.com/how-to-audit-linux-system-operation/
https://unix.stackexchange.com/questions/457107/sending-bash-history-to-syslog
https://blog.arstercz.com/introduction_to_linux_dynamic_tracing/
https//github.com/ggrandes-clones/pmon/blob/master/src/pmon.c