在高交互蜜罐环境中,ssh服务允许攻击者成功由ssh登录并获取到一个可进行各种操作的真实shell,同时需要记录shell中所有操作。严谨的方案是在bash源码及内核模块中做审计记录。这里给出另一种娱乐性质的方案,简单而tricky的方式达成该目标的基本功能,当然这种方案在了解的人面前很容易被绕过。
本文参考环境为centos 7.5 1804
这里记录shell执行命令的核心是bash内建的trap命令,trap可以看作bash的信号处理,同时有一些虚拟信号,这里要使用的是DEBUG信号。trap arg DEBUG
执行后,arg命令将在每一个命令执行前被执行,同时可以从变量BASH_COMMAND中读取到将要执行的命令。参考man trap
,man bash
。
交互式bash记录命令
先看一下例子,log_command
中可以替换为其他脚本,将记录到的命令发送到远程服务器并保存。这里仅简单保存日志文件。
/etc/bash_logger.sh1 2 3 4 5 6
| function log_command() { echo $(date) $USER $$ $PPID $PWD \"$BASH_COMMAND\" >> /tmp/bash_logger.log }
trap 'log_command' DEBUG
|
试验一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| [huyu@localhost ~]$ source /etc/bash_logger.sh [huyu@localhost ~]$ ps uxf USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND huyu 1684 0.0 0.0 154724 2452 ? S 19:57 0:00 sshd: huyu@notty huyu 1685 0.0 0.0 72164 2900 ? Ss 19:57 0:00 \_ /usr/libexec/openssh/sftp-server huyu 1565 0.0 0.0 154588 2436 ? S 19:50 0:00 sshd: huyu@pts/0 huyu 1566 0.0 0.0 115440 2036 pts/0 Ss 19:50 0:00 \_ -bash huyu 1722 0.0 0.0 127636 1180 pts/0 S+ 19:59 0:00 \_ screen -r huyu 1589 0.0 0.0 128520 2292 ? Ss 19:50 0:00 SCREEN huyu 1590 0.0 0.0 115440 1904 pts/1 Ss+ 19:50 0:00 \_ /bin/bash huyu 11916 0.0 0.0 115440 2084 pts/3 Ss 20:25 0:00 \_ /bin/bash huyu 11991 0.0 0.0 107992 616 pts/3 S+ 20:27 0:00 | \_ tail -n0 -f /tmp/bash_logger.log huyu 11973 0.0 0.0 115440 2092 pts/2 Ss 20:27 0:00 \_ /bin/bash huyu 11994 0.0 0.0 155324 1848 pts/2 R+ 20:27 0:00 \_ ps uxf
|
看看输出
1 2 3
| [huyu@localhost ~]$ tail -n0 -f /tmp/bash_logger.log Mon Aug 26 20:27:29 CST 2019 huyu 11973 1589 /home/huyu "ps uxf" Mon Aug 26 20:27:29 CST 2019 huyu 11973 1589 /home/huyu "printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}""
|
可以看到在期望的命令记录之外又多了一条,这是由于变量PROMPT_COMMAND
的影响,在提示符之前会执行该变量指定的命令,这个命令被trap捕获了。把它unset掉就可以了。
增加 unset PROMPT_COMMAND1 2 3 4 5 6 7 8
| function log_command() { echo $(date) $USER $$ $PPID $PWD \"$BASH_COMMAND\" >> /tmp/bash_logger.log }
unset PROMPT_COMMAND
trap 'log_command' DEBUG
|
为了让每个bash都能生效,在/etc/bashrc
文件末尾增加一行. /etc/bash_logger.sh
,这样在默认配置下每一个交互式bash都会加载新增的文件(除非通过参数指定bash不加载配置文件)。
- login bash的加载路径是
~/.bash_profile
加载 ~/.bashrc
加载 /etc/bashrc
。
- non-login bash的加载路径是
~/bashrc
加载 /etc/bashrc
。
非交互式bash记录命令
上一个例子里可以记录到交互式bash中调用的命令,那么再试一下通过bash执行的脚本文件是否可以记录到脚本文件内调用的命令,结果是无法记录。因为trap只能影响到当前bash,通过bash SCRIPT_NAME
和bash -c COMMAND
会启动一个子进程,执行非交互式bash,不会加载之前准备的配置文件。
之前说过非交互式bash会检查环境变量BASH_ENV
并尝试加载其指定的文件。因此在配置文件中再增加BASH_ENV
环境变量。
增加 BASH_ENV 环境变量1 2 3 4 5 6 7 8 9
| function log_command() { echo $(date) $USER $$ $PPID $PWD \"$BASH_COMMAND\" >> /tmp/bash_logger.log }
unset PROMPT_COMMAND export BASH_ENV="/etc/bash_logger.sh"
trap 'log_command' DEBUG
|
重启一个bash,验证一下
1 2 3 4 5 6 7
| [huyu@localhost ~]$ cat t.sh echo 1 echo 2 [huyu@localhost ~]$ bash t.sh 1 2 [huyu@localhost ~]$
|
看看日志
1 2 3 4 5
| [huyu@localhost ~]$ tail -n0 -f /tmp/bash_logger.log Mon Aug 26 20:58:55 CST 2019 huyu 12358 1589 /home/huyu "cat t.sh" Mon Aug 26 20:58:56 CST 2019 huyu 12358 1589 /home/huyu "bash t.sh" Mon Aug 26 20:58:56 CST 2019 huyu 12456 12358 /home/huyu "echo 1" Mon Aug 26 20:58:56 CST 2019 huyu 12456 12358 /home/huyu "echo 2"
|
bash函数内部记录命令
修改一下之前的脚本t.sh
,再执行
1 2 3 4 5 6 7 8 9 10
| [huyu@localhost ~]$ cat t.sh function echo_more() { echo 1 echo 2 } echo_more [huyu@localhost ~]$ bash t.sh 1 2
|
日志中
1 2 3 4
| [huyu@localhost ~]$ tail -n0 -f /tmp/bash_logger.log Tue Aug 26 21:33:53 CST 2019 huyu 13457 1589 /home/huyu "cat t.sh" Tue Aug 26 21:33:59 CST 2019 huyu 13457 1589 /home/huyu "bash t.sh" Tue Aug 26 21:33:59 CST 2019 huyu 13496 13457 /home/huyu "echo_more"
|
可以看到没有函数内部的调用记录。想要这部分记录也很简单,在配置文件中增加一行set -o functrace
,参考man set
,man bash
。
最终/etc/bash_logger.sh1 2 3 4 5 6 7 8 9 10 11
| function log_command() { echo $(date) $USER $$ $PPID $PWD \"$BASH_COMMAND\" >> /tmp/bash_logger.log }
unset PROMPT_COMMAND export BASH_ENV=/etc/bash_logger.sh
set -o functrace
trap 'log_command' DEBUG
|
将sh切换为bash
到现在bash的命令记录基本完成了,但如果调用sh开启一个新shell或通过sh执行一个脚本,还是无法记录到后续命令。这里不想关心sh的配置加载和trap相关了,把sh替换为bash就可以了,但centos 7.5下sh本身就是bash的软链,通过启动的命令参数判断执行逻辑。因此需要做的稍微多一点。
准备一段代码
main.c1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #include <stdio.h> #include <stdlib.h>
int main(int argc, char **argv, char **envp) { char *login_bash = "-bash"; char *nologin_bash = "bash";
if (argv[0][0] == '-') { argv[0] = login_bash; } else { argv[0] = nologin_bash; }
execve("/usr/bin/bash", argv, envp); return 0; }
|
编译,gcc main.c
删除sh的软链,替换为刚编译生成的可执行文件。
1 2
| sudo rm /bin/sh sudo cp a.out /bin/sh
|
现在sh也不是问题了。
白名单
经过上面的步骤,已经可以记录bash和sh调用的所有命令了,但是由于linux中大量使用shell做一些辅助工作,因此很多时候会有大量冗余的调用记录。比如执行一下man bash
,就会在日志文件中看到数十行调用记录,这里可以通过增加白名单的方式把明显无害的调用排除出记录范围。