Linux 系统日志小结

Linux 系统日志小结,内附清理工具 LLC

Linux 系统日志小结

上周近距离观赏了一次 APT 组织的攻击,感受最深的是他们非常有组织,搞办公网和搞生产网的人属于不同的组;有纪律,与攻击目标无关的机器能不动就不动,用到的技术其实大家都会,能不能管好自己的手不乱动是比较难的。不过让我比较惊讶的是,所有被入侵的机器的日志都被清除了(好在这些机器都在云上,厂商有对在云服务器上执行的命令做日志),只有一台机器(恰好还是本地机房)的 lastlog 没有清除,这让我十分不解。如果没有这台机器的 lastlog,那么在梳理攻击的链条时就会在中间断掉,无法准确还原入侵的路线,所以学会给自己擦屁股还是很重要的。

我本来以为自己对 Linux 的系统日志这个知识点十拿九稳,在翘着二郎腿欣赏的过程中突然发现自己不能准确回答 lastlog 的用途,只有一个模糊的印象(虽然猜对了),之前似乎确实没有完整地过一遍,有点不爽,故有此文。

日志服务

syslogd

介绍

说到 Linux 的日志,就不得不提到 syslogd。它是大部分 Linux 发行版默认的日志守护进程,是一个记录日志的服务,子配置文件位于 /etc/syslog/etc/syslogd,默认配置文件为 /etc/syslog.conf。从架构来看,这个服务下面有一系列的子服务,例如 mail、cron 等等,这些子服务对外提供日志记录的功能,当其它的程序或服务需要记录日志的时候,就可以直接调用这些子服务将日志记录到指定的地方。

我们编写的代码可以通过 syslog 的接口进行 log,syslog 会通过 socket 将 log 发送给 syslogd,syslogd 在获取到 log 后,会对 log 进行处理,然后根据用户配置,将 log 保存到本地或者发送到其他服务器上去。

最简单的,用 logger -t '[test]' "I'm Tr0y" 就可以直接日志记录到 message 里:

1
Jun  9 19:23:52 localhost [test]: I'm Tr0y

再举个 python 使用 syslog 的例子吧:

1
2
3
4
5
In [1]: import syslog

In [2]: syslog.openlog('[test]')

In [3]: syslog.syslog("I'm Tr0y")

然后就可以在 /var/log/messages 里面看到它了:

1
Jun  9 20:01:05 localhost [test]: I'm Tr0y

配置文件详解

作为 syslogd 的配置文件,syslog.conf 指定了:

  1. 什么服务?
  2. 什么日志等级?
  3. 记录在哪里?

格式如下:

1
服务名称+条件运算符+信息等级    日志保存的位置

具体来说:

  1. 服务名称
    1. user:默认值
    2. auth/authpriv:主要是与认证有关的服务的日志,例如 login、ssh、su 等
    3. cron:定时任务的日志,包括 cron 和 at
    4. kern:即 kernel(用户进程是无法生成这种日志的)
    5. *:代表任意服务
    6. ...
    举例来说, sendmail 与 postfix 都会通过 syslog 记录日志,服务名称为 LOG_MAIL,所以它们产生的日志在 syslogd 看来就都是 mail 类型的。
  2. 条件运算符
    1. .:即 >=,大于等于该等级的日志都要记录下来
    2. .=:即 ==,等于该等级的日志都要记录下来
    3. .!:即 !=,除了该等级的日志都要记录下来
  3. 信息等级
    1. debug:debug 级别的日志,一般用于调试
    2. info:仅是一些基本的信息说明
    3. notice:有些信息需要强调一下
    4. warning:警示,可能会出问题。这个层级以及以下的,基本无需处理,知道就行。
    5. err:错误信息,导致程序/系统无法运行
    6. crit:出现了很严重的错误
    7. alert:出现了非常严重的错误,必须马上解决
    8. emerg:系统已经无法使用了
    9. none:无优先级
    10. *:任意优先级
  4. 日志保存的位置:通常是保存在本地文件里。当然也可以选择发送给指定的日志服务器保存。

最后还有 2 点需要注意:

  1. 多个服务,不同等级,要共用记录位置的话,可以用 ; 来合并:
    1
    2
    3
    4
    5
    # 表示 auth 的和 cron 的全部等级的日志都记录在 important 里
    # 然后再存一份它们 warn 等级的日志在 important.warn 里

    auth.*;cron.*      /var/log/important
    auth.=warn;cron.=warn  /var/log/important.warn
  2. 多个服务,共用等级的话,可以用 , 来合并:
    1
    2
    3
    4
    5
    6
    7
    # 表示所有日志都存在 messages 里
    # 然后通过设定 auth、cron 的日志等级为 none 来阻止它们记录日志

    *.*;auth,cron.none /var/log/messages

    # 当然这样也是可以的
    *.*;auth.none;cron.none /var/log/messages

rsyslogd

目前不少的 Linux 发行版已经用 rsyslogd 代替了 syslogd。rsyslogd 是 syslogd 的升级版,子配置文件位于 /etc/rsyslog.d,默认配置文件为 /etc/rsyslog.conf,由于其配置语法与 syslogd 的配置文件一致,这里就不重复说了。

那么在说各个具体的系统日志之前,我们先来看看 rsyslogd 的配置。因为有些系统日志是通过 rsyslogd 记录的,那么正如前面说的,rsyslog.conf 就决定了这些系统日志存放的位置。

如果你在网上搜索 “linux ssh 日志位置”,很大概率会告诉你是 /var/log/secure。但是这个其实是默认位置,是有可能被改掉的(不同发行版或者是恶意修改)。所以不管是应急也好,做威胁检测也好,最好要通过 rsyslog.conf 来确定特定系统日志真正的位置。

比如这是我虚拟机的配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
...

#### RULES ####

...

# 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 cron stuff
cron.* /var/log/cron

...

这下我们才可以确定,messages 存放了除了 mail、authpriv、cron 之外所有的 info 等级的日志(所以这个日志文件通常是比较大的);authpriv 存在 secure 里;cron 存在 cron 里。

最后有两个小经验:

  1. 如果攻击者把这类日志删除了的话,那么只要 rsyslogd 进程没有停止过(重启也算停止过),就可以从 /proc/pid/fd/n(pid 和 n 取决于实际情况)恢复日志。步骤非常简单,先用 sudo lsof +L1 看一下有没有被删除的 log 文件,有的话记下 pid,然后 ls -l /proc/pid/fd 找一下日志文件是对应的哪个 fd,直接 cp 即可
  2. 如果用 vim 打开了通过 rsyslogd 记录的日志的文件之后,执行了保存操作(打开后直接 wq 也算),那么该日志会立即停止记录。回想一下 vim 的 wq 原理,其实就是 cp+mv,cp 不用说,会创建一个新的 inode;mv 的文件在同一个分区下时,执行的其实是 rename;这一套流程下来,在 /proc/pid/fd/n 下面的表现就是后面多了一个 (deleted)(这里面的机制有些复杂,不仅仅是 inode 那么简单的,有机会的话详细介绍)。如果要恢复中断的记录,那么就需要从 /proc/pid/fd/n 下面 cp 出来,然后重启 rsyslogd;如果不想找回中断的记录,那么直接重启就行。

重要系统日志

messages

默认路径:/var/log/messages

它是默认的日志文件,记录 Linux 内核消息及各种应用程序的日志信息,包括:启动、IO 错误、网络错误、程序自定义日志等等。

虽然信息种类会多一些,但是这个一般用得少,就不多说了。

secure

默认路径:/var/log/secure

只要牵涉到需要输入帐号密码的程序,包括 login、有图形界面的 Linux 登录的时候、su、sudo、ssh、telnet、groupadd、useradd、passwd 等等,那么当登录时(不管成功或者失败)都会被记录在此。

secure 是应急响应中最常查看的文件,例如疑似存在 ssh 爆破的时候,我们就可以查看这个文件,这类应急基本上是在考验我们使用 grep、awk、sed 的熟练度。

cron

默认路径:/var/log/cron

该日志文件记录了 cron 运行的日志。包含:

  1. crontab 运行定时任务的记录,包含运行的命令、时间、pid、定时任务所属的用户等
  2. 更新定时任务(关键字 REPLACE/RELOAD)的时间点与用户
  3. 如果定时任务有直接输出的话,也会打到 cron 里去

cron 的作用比较专一,它一般有两种用途:

  1. cron 误删之后,可以根据日志直接恢复命令;至于定时任务的调度周期,可以尝试根据定时任务通过每次执行的间隔来尝试还原。
  2. 如果 crontab 被植入了恶意的定时任务,那么在这里可以找到植入后第一次运行的时间,结合调度周期,可以推断出被植入的时间范围。

登录相关日志

先来看一下整体的流程。

在 Linux 中,每当有一个用户登录时,如果有分配 tty,login 命令要负责:

  1. 在文件 lastlog 中查看用户的 UID,如果存在,则把用户上次登录、注销的时间和主机名写到标准输出中
  2. 然后在 lastlog 日志中记录新的登录时间
  3. 接着在 utmp 日志中记录用户新的登陆记录
  4. 接着在 wtmp 日志中记录用户新的登陆记录
  5. 当用户登录退出时
  6. 在 wtmp 日志中记录用户新的注销记录
  7. 最后在 utmp 日志中删除该用户对应的登陆记录

接下来详细看一下每个日志的用途,这些日志都是二进制格式的日志,用 cat、less 是看不了的。btmp、utmp 和 wtmp,它们的数据结构是一样的,而 lastlog 文件有自己的数据结构,关于它们的具体的数据结构可以使用 man 命令查询,例如 man utmp

lastlog

默认路径:/var/log/lastlog

该日志文件记录了每个用户最近一次成功的登录事件和最后一次不成功的登录事件,每次用户登录的时候都会查一下这个日志的记录。

一般用 lastlog 命令查看,会根据 UID 排序来显示用户名、tty、IP 和最后登录时间,如果某用户从来没有登录过,就显示为 **Never logged in**

wtmp

默认路径:/var/log/wtmp

该日志文件记录了每个用户登录、注销及系统的重启等事件,主要用于查看用户的登录记录。

一般用 last 命令查看,会以反序从后向前显示用户的登录记录,也能根据用户名、tty 或时间显示相应的记录:

utmp

默认路径:/var/run/utmp

该日志文件记录了当前登录的每个用户的信息。因此这个文件会随着用户登录、注销而不断变化,因为它只保留了当前已登录的用户记录,不会为用户保留永久的记录。

系统中需要查询当前用户状态的程序,如 who、w、users、finger 等其实就是从这个文件获取的信息:

btmp

默认路径:/var/log/btmp

该日志文件记录了失败的登录尝试。默认没有初始化,不会记录,可以执行一次 lastb 开始记录。

一般用 lastb 命令查看:

清理痕迹

清空日志

简单粗暴,但是很容易被发现,除非已经到了收割成果的阶段,被不被发现都无所谓了,那么可以这么做。需要注意的是,清空有两种做法,一种是用空字符串覆盖原文件,这种没什么问题;另一种是直接将文件删除,对于用 rsyslogd 记录的日志来说,如果没有停过 rsyslogd 那么是可以被恢复到,这一点上面也提到过了。

修改日志

对于用 rsyslogd 记录的日志来说,可读性很好,直接修改即可。

比如篡改自己使用的 ip:sed -i 's/192.168.166.85/192.168.1.1/g' secure;或者直接删掉有 ip 信息的行 sed -i '/192.168.166.85/d' test

稍微有些费劲是那些二进制格式的日志。对于登录相关日志来说,由于手动修改比较麻烦,所以我写了一个日志清理的工具:

https://github.com/Macr0phag3/LLC

以 utmp 为例:

用 w 我们可以看到 macr0phag3 在线:

清除:

再用 w 看看:

macr0phag3 就没有显示在线了。

如果觉得这样麻烦,其实也有办法避免记录登录日志。登录后,在分配伪 tty(建议看下资料 1)的时候才会出现这些登录日志(btmp 除外),那如果没有分配的话,就不会做记录,这也是这个命令的原理:

1
ssh -T user@host /bin/bash -i

-T 代表不要分配 tty,-i 代表要一个交互型的 bash,去掉其实也能用,甚至整个 /bin/bash -i 都可以去掉,就是不太美观,用起来不爽。

比如我们可以创建一个新的用户来试试,用上述方式登录:

可以看到除了爆破失败的记录之外,其他登录日志均无记录。

当然了,隐藏/清理痕迹的手法有很多,有各种姿势,像这种 ssh 的算烂大街了。等有时间我专门写一篇文章来总结这些知识。


日志在应急中是非常重要的。现在很多运维在突发事故上吃了亏,都会对日志做备份,甚至有专门的日志备份服务器,但是基本上是出于运维角度的考虑。其实安全事件也算突发事故,如果意识到了日志对于应急响应的作用,应该会有更多人做备份吧。当然如果有实时备份的话,攻击者直接清理日志就没啥用了,但是可以想办法让日志停止记录。

攻与防,始终是相互促进的嘛

资料

  • Linux TTY/PTS 概述: https://segmentfault.com/a/1190000009082089

如果橘友们是这个 APT 组织的成员
会记得清理日志吗?
会漏掉那个关键的 lastlog 吗?


Linux 系统日志小结
https://www.tr0y.wang/2021/06/15/linux-system-log/
作者
Tr0y
发布于
2021年6月15日
更新于
2024年6月3日
许可协议