在高交互蜜罐环境中,ssh服务允许攻击者成功由ssh登录并获取到一个可进行各种操作的真实shell,同时需要记录shell中所有操作。严谨的方案是在bash源码及内核模块中做审计记录。这里给出另一种娱乐性质的方案,简单而tricky的方式达成该目标的基本功能,当然这种方案在了解的人面前很容易被绕过。

本文参考环境为centos 7.5 1804

这里记录shell执行命令的核心是bash内建的trap命令,trap可以看作bash的信号处理,同时有一些虚拟信号,这里要使用的是DEBUG信号。trap arg DEBUG执行后,arg命令将在每一个命令执行前被执行,同时可以从变量BASH_COMMAND中读取到将要执行的命令。参考man trapman bash

交互式bash记录命令

先看一下例子,log_command中可以替换为其他脚本,将记录到的命令发送到远程服务器并保存。这里仅简单保存日志文件。

/etc/bash_logger.sh
1
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_COMMAND
1
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_NAMEbash -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 setman bash

最终/etc/bash_logger.sh
1
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.c
1
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,就会在日志文件中看到数十行调用记录,这里可以通过增加白名单的方式把明显无害的调用排除出记录范围。