bash命令审计,记录终端操作指令

前言

我们需要记录bash操作的指令,不管是root帐号,还是其他普通用户的帐号,都需要记录.
然后发送到远程端统一审计.
最终实现了以下的功能:

  • 记录操作指令到/var/log/messages, 对于普通用户同样支持.
  • 根据key的指纹区分登录的用户.
  • 记录了来源IP.
  • 新增了bash.spec文件, 如果你需要自己下载源码,可以参照去修改打包.

环境介绍

系统: CentOS Linux release 7.2.1511 (Core)
bash版本: bash 4.4

操作说明

修改bash-4.4.tar.gz 源码包

修改 config-top.h 文件

开启下面的两个注释选项,最终修改如下

1
2
#define SSH_SOURCE_BASHRC
#define SYSLOG_HISTORY

修改 bashhlist.c 文件

大约在文件的756行到777行, 我们注释 bash_syslog_history 函数,添加我们自己修改的,最终修改如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/*
void
bash_syslog_history (line)
const char *line;
{
char trunc[SYSLOG_MAXLEN];
static int first = 1;
if (first)
{
openlog (shell_name, OPENLOG_OPTS, SYSLOG_FACILITY);
first = 0;
}
if (strlen(line) < SYSLOG_MAXLEN)
syslog (SYSLOG_FACILITY|SYSLOG_LEVEL, "HISTORY: PID=%d UID=%d %s", getpid(), current_user.uid, line);
else
{
strncpy (trunc, line, SYSLOG_MAXLEN);
trunc[SYSLOG_MAXLEN - 1] = '\0';
syslog (SYSLOG_FACILITY|SYSLOG_LEVEL, "HISTORY (TRUNCATED): PID=%d UID=%d %s", getpid(), current_user.uid, trunc);
}
}
*/
void
bash_syslog_history (line)
const char *line;
{
char trunc[SYSLOG_MAXLEN];
static int first = 1;
const char *name_of_key;
const char *ssh_client_host;
name_of_key = getenv("NAME_OF_KEY");
ssh_client_host = getenv("SSH_CLIENT_HOST");
if (first)
{
openlog (shell_name, OPENLOG_OPTS, SYSLOG_FACILITY);
first = 0;
}
if (strlen(line) < SYSLOG_MAXLEN)
syslog (SYSLOG_FACILITY|SYSLOG_LEVEL, "HISTORY: PID=%d PPID=%d User=%s USER=%s HOST=%s CMD=%s", getpid(), getppid(), current_user.user_name, name_of_key, ssh_client_host, line);
else
{
strncpy (trunc, line, SYSLOG_MAXLEN);
trunc[SYSLOG_MAXLEN - 1] = '\0';
syslog (SYSLOG_FACILITY|SYSLOG_LEVEL, "HISTORY (TRUNCATED): PID=%d PPID=%d User=%s USER=%s HOST=%s CMD=%s", getpid(), getppid(), current_user.user_name, name_of_key, ssh_client_host, trunc);
}
}

之后重新打成tar.gz包, 注意的是,新包命名成bash-audit-4.4.tar.gz.因为这样是避免升级系统的bash, 我们将会把这个bash打包成bash-audit.
可以通过文章最后的链接去下载.

1
2
3
mv bash-4.4 bash-audit-4.4
tar cf bash-audit-4.4.tar bash-audit-4.4
gzip -c bash-audit-4.4.tar > bash-audit-4.4.tar.gz

dot_bash-audit.sh 文件

这个文件主要的作用是根据你的key指纹得到登录用户是谁, 其实最终的目的就是设置2个环境变量 NAME_OF_KEYSSH_CLIENT_HOST.

  • NAME_OF_KEY : 记录的是登录用户的用户名
  • SSH_CLIENT_HOST : 记录的是来源IP

内容如下,你也可以根据自己的需求更改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#!/bin/bash
# bash的命令记录
# 主要是为了设置 NAME_OF_KEY 和 SSH_CLIENT_HOST 环境变量
authorized_file="$HOME/.ssh/authorized_keys"
key_finger="/var/log/ssh_key_finger"
tmp_file=$(mktemp -uq /tmp/key.log.XXXXX)
# sshd_log_file 值的设置在脚本末尾有说明
sshd_log_file="/var/log/secure"
RSA_KEY=$(sudo /usr/bin/cat ${sshd_log_file}|awk -v p="$PPID" '/Found matching RSA key/ && $0 ~ p {f=$NF}END{print f}')
# 授权,普通用户可读写
if [ "$UID" == "0" ];then
ppid=$PPID
else
#如果不是root用户,验证指纹的是另外一个进程号
ppid=`/bin/ps -ef | grep $PPID |grep 'sshd:' |awk '{print $3}'`
fi
# 得到各个key的finger并放在${key_finger}
if [ -f "${authorized_file}" ];then
while read line
do
echo "${line}" >${tmp_file}
NAME=$(echo "${line}"|awk '{print $3}')
KEY_FING=$(ssh-keygen -l -f ${tmp_file}|awk '{print $2}')
grep "$KEY_FING $NAME" ${key_finger} >/dev/null 2>&1 || echo "$KEY_FING $NAME" >>${key_finger}
done < ${authorized_file}
fi
#得到 NAME_OF_KEY 和 SSH_CLIENT_HOST
NAME_OF_KEY='(null)'
if [ -n "$RSA_KEY" -a -f ${key_finger} ];then
NAME_OF_KEY=$(awk -v key="$RSA_KEY" '$1 == key{print $NF}' ${key_finger})
fi
SSH_CLIENT_HOST=$(echo "$SSH_CLIENT"|awk '{if($1 == "::1" || $1 ~ "localhost"){print "127.0.0.1"}else{print $1}}')
# 参数设置为只读
#readonly NAME_OF_KEY
#readonly SSH_CLIENT_HOST
export NAME_OF_KEY SSH_CLIENT_HOST
[ -f ${tmp_file} ] && /bin/rm ${tmp_file}
# 远程登录执行时
# 请添加到$HOME/.bashrc 或者 /etc/bashrc, "# bash audit"和末尾的 "# end bash audit" 也需要保留
: '
# bash audit
if [ -n "${BASH_EXECUTION_STRING}" ];then
[ -f "/etc/profile.d/bash-audit.sh" ] && source /etc/profile.d/bash-audit.sh
if [ -z "${NAME_OF_KEY}" ];then
NAME_OF_KEY="(null)"
fi
logger -t "-bash[${PPID}]" -s "HISTORY: PID=${PPID} PPID=${PPID} User=${USER} USER=${NAME_OF_KEY} HOST=${SSH_CLIENT_HOST} CMD=${BASH_EXECUTION_STRING}" >/dev/null 2>&1
fi
# end bash audit
'
# 在 /etc/ssh/sshd_config 中设置
# 如果 SyslogFacility 设置为默认的`AUTHPRIV`, 那就不需要更改
# 请按下面的对应表设置 'sshd_log_file' 变量的值
# AUTHPRIV: /var/log/secure
# AUTH: /var/log/messages
# 如果你设置了其他值, 请对应的修改脚本上的 'sshd_log_file' 变量的值, 同时把对应的权限添加到 /etc/sudoers.d/bash-audit
:<<!
SyslogFacility AUTHPRIV
LogLevel VERBOSE
!

dot_bash-audit.sudoer 文件

设置sudo权限, 因为上面的 dot_bash-audit.sh 脚本中,需要通过查找 /var/log/messages 或者 /var/log/secure 文件中的指纹来获取登录的用户信息的.
但是这2个文件,普通账户都没有权限查看,所以为了也记录普通帐号的操作,需要普通帐号有权限查看这2个文件.
这个文件放置的位置在 /etc/sudoers.d/bash-audit
请注意:如果你的sshd_config中设置的 SyslogFacility 的值不在AUTHPRIV或者AUTH设备,那需要你自己修改下面的权限.

1
2
3
4
5
6
7
8
9
10
11
# bash-audit
# bash审核的时候,普通用户没有权限查看日志的权限
# 所以在这里配置sudo免密码查看.
# 把这个文件放置在 /etc/sudoers.d/bash-audit
## BashAudit
Cmnd_Alias BASHAUDIT = /usr/bin/cat /var/log/messages, /usr/bin/cat /var/log/secure, /usr/bin/grep /var/log/messages, /usr/bin/grep /var/log/secure
Defaults !requiretty
ALL localhost=(ALL) NOPASSWD: BASHAUDIT

bash-audit.spec 文件

提供了一个模板,最终安装的位置在 /usr/local/bash-audit-4.4, 注意安装的时候,有修改帐号的 shell 的.如果你不需要,请取消.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
%define _prefix /usr/local/bash-audit-4.4
Name: bash-audit
Version: 4.4
Release: 1%{?dist}
Summary: The GNU Bourne Again shell
Group: System Environment/Shells
License: GPLv3+
URL: https://yoncan.github.io
Packager: kaiser
BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
Source0: bash-audit-%{version}.tar.gz
Source1: dot_bash-audit.sh
Source2: dot_bash-audit.sudoer
#BuildRequires:
#Requires:
%description
The GNU Bourne Again shell (Bash) is a shell or command language
interpreter that is compatible with the Bourne shell (sh). Bash
incorporates useful features from the Korn shell (ksh) and the C shell
(csh). Most sh scripts can be run by bash without modification.
%prep
%setup -q -n %{name}-%{version}
%build
%configure --prefix=%{_prefix} --with-bash-malloc=no --with-afs
make %{?_smp_mflags}
%install
# clear buildroot
[ "%{buildroot}" != "/" ] && rm -rf %{buildroot}
make install DESTDIR=%{buildroot}
# copy file
mkdir -p %{buildroot}/etc/profile.d
install -c -m644 %SOURCE1 %{buildroot}/etc/profile.d/bash-audit.sh
# create key finger
mkdir -p %{buildroot}/var/log
touch %{buildroot}/var/log/ssh_key_finger
chmod 666 %{buildroot}/var/log/ssh_key_finger
# copy sudoers
mkdir -p %{buildroot}/etc/sudoers.d
install -c -m644 %SOURCE2 %{buildroot}/etc/sudoers.d/bash-audit
%post
grep "%{_prefix}/bin/bash" /etc/shells || echo "%{_prefix}/bin/bash" >>/etc/shells
sed -i "s#\(SHELL=\).*#\1%{_prefix}/bin/bash#" /etc/default/useradd
for u in `awk -F ':' '$NF == "/bin/bash" || $NF == "/bin/sh" || $NF ~ "bash-audit" {print $1}' /etc/passwd`
do
chsh -s %{_prefix}/bin/bash $u
done
if ! `grep '# bash audit' /etc/bashrc >/dev/null 2>&1`;then
cat >>/etc/bashrc<<EOF
# bash audit
if [ -n "\${BASH_EXECUTION_STRING}" ];then
[ -f "/etc/profile.d/bash-audit.sh" ] && source /etc/profile.d/bash-audit.sh
if [ -z "\${NAME_OF_KEY}" ];then
NAME_OF_KEY='(null)'
fi
logger -t "-bash[\${PPID}]" -s "HISTORY: PID=\${PPID} PPID=\${PPID} User=\${USER} USER=\${NAME_OF_KEY} HOST=\${SSH_CLIENT_HOST} CMD=\${BASH_EXECUTION_STRING}" >/dev/null 2>&1
fi
# end bash audit
EOF
fi
%preun
for u in `awk -F ':' '$NF ~ "bash-audit" {print $1} ' /etc/passwd`
do
chsh -s /bin/bash $u
done
#[ -f "/etc/profile.d/bash-audit.sh" ] && rm /etc/profile.d/bash-audit.sh
if `grep '^# bash audit' /etc/bashrc >/dev/null 2>&1`;then
sed -i "/# bash audit/,/# end bash audit/ d" /etc/bashrc
fi
sed -i "s#\(SHELL=\).*#\1/bin/bash#" /etc/default/useradd
sed -i "/bash-audit/d" /etc/shells
%clean
[ "$RPM_BUILD_ROOT" != "/" ] && rm -rf "$RPM_BUILD_ROOT"
rm -rf $RPM_BUILD_DIR/%{name}-%{version}
%files
%defattr(-,root,root,-)
%{_prefix}
/etc/profile.d/bash-audit.sh
/var/log/ssh_key_finger
/etc/sudoers.d/bash-audit
#%doc %{prefix}/docs
%changelog
* Fri Jan 06 2017 YangCan <yoncan@qq.com> - 4.4.0-1
- Rebase to bash-4.4
Resolves: #1376609

使用下面的指令打包

1
rpmbuild -ba --clean bash-audit-4.4.spec

最终应该会生成如下包

1
2
3
[root@localhost SOURCES]# ll /root/rpmbuild/RPMS/x86_64/*
-rw-r--r--. 1 root root 1715508 Jan 16 18:22 /root/rpmbuild/RPMS/x86_64/bash-audit-4.4-1.el7.centos.x86_64.rpm
-rw-r--r--. 1 root root 1741176 Jan 16 18:22 /root/rpmbuild/RPMS/x86_64/bash-audit-debuginfo-4.4-1.el7.centos.x86_64.rpm

修改 /etc/ssh/sshd_config 文件

修改的值如下,记得修改之后,重启sshd服务.

1
2
SyslogFacility AUTHPRIV
LogLevel VERBOSE

SyslogFacility 这个值影响了用户登录时,sshd产生的日志信息的位置,日志的位置变动了,同样的/etc/profile.d/bash-audit.sh脚本中sshd_log_file的变量也需要同样的修改.
请按下面的对应表设置 ‘sshd_log_file’ 变量的值

  • AUTHPRIV: /var/log/secure
  • AUTH: /var/log/messages
    如果你设置了其他值, 请对应的修改脚本上的 ‘sshd_log_file’ 变量的值, 同时把对应的权限添加到 /etc/sudoers.d/bash-audit

如何获取

代码并没托管到github, 可以通过下面的点击下载

结果

最终可以得到的结果

1
2
3
# tail -f /var/log/messages
Jan 16 19:11:21 localhost -bash[22053]: HISTORY: PID=22053 PPID=22050 User=root USER=ycan@kaiser HOST=192.168.10.1 CMD=df -h
Jan 16 19:11:25 localhost -bash[22053]: HISTORY: PID=22053 PPID=22050 User=root USER=ycan@kaiser HOST=192.168.10.1 CMD=ps aux

你可以把这个结果发送到远程端.

参考

分享即快乐,谢谢你请思哲小朋友吃糖!