SSH认证日志分析实战:用awk快速定位暴力破解与异常登录
发布时间:2026/9/26 10:18:44来源:尧图网络
1. 项目概述为什么“玄机”日志分析专盯 ssh 日志你有没有遇到过这种情况服务器突然变慢CPU 占用飙到 99%但 top 里找不到明显异常进程或者某天凌晨三点收到告警说某个 IP 在 30 秒内尝试了 287 次登录失败又或者运维同事甩给你一个几百 MB 的 auth.log 文件只说“查查有没有被黑”然后就去开会了——而你盯着满屏的Failed password for root from 192.168.123.45 port 54322 ssh2发呆不知道从哪下手这就是“玄机”日志分析——不是泛泛而谈的日志监控而是聚焦在SSH 认证日志auth.log这个最敏感、最浓缩、也最容易被忽视的“数字门禁记录本”上。它不讲大道理不堆监控平台就靠几条awk、grep、sort和uniq组合拳在终端里三分钟内揪出真实攻击痕迹。标题里的“玄机”二字不是故弄玄虚而是指真正的入侵线索往往藏在看似重复、枯燥、格式固定的日志行里——比如同一 IP 的登录失败次数、失败后紧跟着的成功登录、非工作时间的密钥登录、甚至一条被忽略的pam_unix(sshd:auth): authentication failure后面跟着的session opened for user admin。这些细节SIEM 平台可能因规则阈值设得太高而漏报但人眼合理命令组合却能一眼锁定。我做渗透测试和红蓝对抗支撑七年经手过 200 企业级靶场和真实生产环境发现一个铁律90% 以上的横向移动和初始访问都绕不开 SSH 这道门。而 auth.log 就是这扇门唯一的、未经修饰的出入登记簿。它比 web 日志更干净无 UA、Referer 干扰比数据库日志更实时认证失败/成功瞬间写入比系统日志更聚焦只记录与身份验证强相关的事件。所以“玄机”不是玄学是把有限精力精准投向最高价值日志源的实战策略。适合谁刚转安全的新手想快速建立日志敏感度运维工程师需要日常巡检脚本蓝队成员做应急响应时快速溯源甚至开发同学排查自己写的自动化部署脚本为何总连不上跳板机——只要你接触 Linux 服务器auth.log 就是你每天该扫一眼的“健康日报”。2. 核心思路拆解为什么不用 ELK 或 Splunk而死磕 awk很多人看到“日志分析”第一反应就是上 ELKElasticsearch Logstash Kibana或商业 SIEM。但“玄机”方案反其道而行之核心逻辑就一条在问题发生的第一时间、在最简陋的环境里用最基础的工具拿到最关键的结论。我不是反对大平台而是清楚它们的“失能时刻”——比如你正远程处理一台内存只剩 200MB 的老旧 CentOS 6 服务器Logstash 进程直接 OOM或者客户环境网络隔离连不上外网下载 Beats 客户端又或者应急响应中对方只给了你一个临时 shell 权限连pip都没装。这时候awk是唯一能指望的“瑞士军刀”。2.1 为什么是 auth.log而不是其他日志位置固定且权限可控Ubuntu/Debian 默认在/var/log/auth.logCentOS/RHEL 在/var/log/secure路径明确且只有 root 或 adm 组可读日志本身不易被篡改对比/var/log/messages可能被普通用户写入。事件粒度极细每行日志对应一次原子操作——Failed password、Accepted password、Connection closed、Invalid user、pam_faillock触发等没有聚合没有丢弃原始信息完整。字段结构高度标准化以 Ubuntu 22.04 的典型 auth.log 行为例Jan 15 14:22:33 ubuntu-server sshd[12345]: Failed password for invalid user admin from 192.168.1.100 port 54322 ssh2时间戳Jan 15 14:22:33、主机名ubuntu-server、服务名sshd、PID[12345]、事件类型Failed password、用户invalid user admin、来源 IPfrom 192.168.1.100、端口port 54322、协议ssh2——全部按空格分隔awk天然适配。提示别迷信“日志格式统一”。实际中会遇到 Debian 的Invalid user和 CentOS 的user unknown差异sshd_config中LogLevel VERBOSE开启后多出debug1: key_parse_private_pem: PEM_read_PrivateKey failed这类密钥解析失败日志甚至某些定制固件把时间戳写成2024-01-15T14:22:33.12345600:00。所以“玄机”的第一步不是写完美脚本而是先head -n 5 /var/log/auth.log看清你的日志长什么样。2.2 为什么选 awk 而不是 Python 或 grep启动零开销awk {print $1,$2,$3} file启动时间微秒级Python 脚本光 import sys 就要几十毫秒。对几百 MB 的日志awk流式处理全程内存占用 1MBPython pandas 加载可能直接爆内存。字段提取即写即得$1是第一个字段日期$9是用户名for root中的root$11是 IPfrom 192.168.1.100中的192.168.1.100无需正则编译、无需切片索引所见即所得。我试过用 Python re.match 处理 1GB auth.log耗时 47 秒同样逻辑的awk $9 ~ /root/ {print $0}只要 3.2 秒。内置聚合能力强大awk {count[$11]} END{for (ip in count) print ip, count[ip]}一行代码完成“按 IP 统计出现次数”比写 Python 字典循环简洁十倍且 C 实现性能碾压。当然awk不是万能的。它不适合做复杂状态机比如追踪同一个 IP 的“失败5次→成功1次→断开→再失败”完整会话也不适合输出 HTML 报表。但“玄机”的定位很清晰快、准、狠地回答三个问题——谁在扫扫得有多猛有没有扫成功其他需求交给专业工具。2.3 “玄机”的三层分析纵深设计“玄机”不是单条命令而是一个渐进式分析框架分三层递进表层扫描5秒级用grep快速定位关键词如grep Failed password /var/log/auth.log | tail -20看最新失败记录确认是否正在被暴力破解。中层聚合30秒级用awk做维度统计如awk /Failed password/ {ips[$11]} END{for (i in ips) if (ips[i]10) print i, ips[i]} /var/log/auth.log找出高频攻击 IP。深层关联2分钟级结合时间窗口和事件序列如awk /Failed password|Accepted password/ {print $1,$2,$3,$9,$11,$0} /var/log/auth.log | sort -k1,3再人工筛查“同一 IP 失败后紧跟成功”的可疑链。这三层不是割裂的而是像剥洋葱表层告诉你“有事”中层告诉你“谁干的”深层告诉你“怎么干的”。我在某次金融客户应急中表层扫描发现Invalid user骤增中层聚合锁定 IP185.143.221.77来自俄罗斯深层关联发现该 IP 在失败 42 次后于Jan 15 03:44:12成功登录user test而test是个本该被禁用的测试账号——这就是典型的“密码喷洒弱口令”攻击整个过程从发现到定位不足 90 秒。3. 核心细节解析auth.log 字段解剖与关键事件识别要让awk发挥威力必须吃透 auth.log 的“语言结构”。这不是背诵手册而是像老警察看笔录一样一眼抓住关键信息点。下面以 Ubuntu 22.04OpenSSH 8.9p1和 CentOS 7OpenSSH 7.4p1的典型日志为例逐字段拆解并标注哪些字段是awk分析的黄金靶点。3.1 标准字段位置与实战意义字段序号Ubuntu 示例值CentOS 示例值字段含义awk分析价值实操注意点$1JanJan月份辅助时间范围筛选如awk $1Jan注意date命令输出是Jan但日志可能为Jan、Feb大小写敏感$21515日期结合$3时间可精确到分钟awk $215 $303:00:00筛选当天凌晨数据$2是纯数字无前导零1而非01避免 01这类错误匹配$314:22:3314:22:33时间HH:MM:SS关键用于时间窗口分析如awk $302:00:00 $304:00:00找夜间攻击时间字符串比较按字典序03:00:0003:59:59成立但10:00:0003:00:00也成立$4ubuntu-serverlocalhost.localdomain主机名多服务器环境区分来源awk $4web01只分析 Web 服务器日志主机名可能含下划线、短横线确保匹配时加引号$5sshd[12345]sshd[6789]服务名PIDPID 可用于关联同一连接的多条日志如Connection closed和Accepted可能同 PID[12345]中的方括号是字面量awk $5 ~ /sshd\[.*\]/需转义方括号$6Failed passwordFailed password事件类型核心最高价值字段Failed password、Accepted password、Invalid user、Connection closed等Failed password和Invalid user都代表失败但后者说明用户名不存在攻击者可能在枚举用户名$7forfor固定介词通常忽略但可用于校验字段位置如$6Failed password $7for$8rootroot用户名成功时或invalid user xxx失败时黄金字段$8是用户名$9是invalid user时的用户名需动态判断awk $6 ~ /Failed/ {if ($8invalid) print $9; else print $8}可统一提取失败用户名$9fromfrom固定介词同$7辅助定位 IP$10192.168.1.100192.168.1.100来源 IP核心攻击溯源基石$10是 IP但注意Invalid user行中 IP 在$12致命陷阱Invalid user admin from 192.168.1.100中 IP 是$12而Failed password for root中 IP 是$11注意IP 字段位置不固定是awk新手最大坑。必须先awk {print NF, $0} /var/log/auth.log | head -5查看每行字段数NF再确定 IP 位置。Ubuntu 下Failed password行 IP 通常在$11Invalid user行在$12CentOS 可能全在$11。硬编码$11必翻车。3.2 必须掌握的 7 类关键事件及其awk提取模式暴力破解特征高频Failed password# 统计所有失败 IP 及次数兼容 Ubuntu/CentOS awk /Failed password|Invalid user/ { if ($6 ~ /Invalid/ $7 user) ip $12 else if ($6 ~ /Failed/ $7 for) ip $11 else next count[ip] } END { for (i in count) if (count[i] 50) print i, count[i] } /var/log/auth.log | sort -k2nr原理Failed password和Invalid user都是失败信号但字段位置不同需分支判断。阈值设为 50 次因正常用户输错密码极少超 5 次。成功登录线索Accepted password或Accepted publickey# 提取所有成功登录含密码和密钥按 IP 分组 awk /Accepted (password|publickey)/ { if ($6 Accepted $7 password) ip $11 else if ($6 Accepted $7 publickey) ip $11 else next print $1,$2,$3, $9, ip, $0 } /var/log/auth.log | sort -k1,3原理Accepted后跟password或publickey表明认证成功。$9是用户名for root中的root$11是 IP。排序后易发现“同一 IP 登录多个用户”的横向移动。异常时间登录凌晨 2-5 点的Accepted# 筛选凌晨成功登录假设日志时间是本地时区 awk /Accepted (password|publickey)/ $3 02:00:00 $3 05:00:00 { print $0 } /var/log/auth.log原理时间字符串字典序比较02:00:00到05:00:00覆盖凌晨窗口。注意若服务器时区为 UTC需换算。用户枚举证据大量Invalid user且用户名各异# 统计被枚举的用户名取前 20 个 awk /Invalid user/ {users[$8]} END { n 0 for (u in users) { if (n 20) print u, users[u] } } /var/log/auth.log | sort -k2nr原理Invalid user admin中$8是adminInvalid user test中$8是test。高频出现不同用户名说明攻击者在暴力猜用户名。会话劫持嫌疑Connection closed后无Accepted# 找出异常断开可能因防火墙拦截或攻击中断 awk /Connection closed/ !/preauth/ { if ($10 port $11 ~ /^[0-9]$/) print $0 } /var/log/auth.log原理Connection closed by [IP] port [端口]表明连接被主动关闭。若大量出现且无对应Accepted可能是扫描器被 WAF 拦截。密钥登录异常pam_unix(sshd:auth)失败后publickey成功# 关联 PAM 认证失败与后续密钥成功需时间排序 awk /pam_unix\(sshd:auth\): authentication failure|Accepted publickey/ { if ($0 ~ /authentication failure/) { fail_ip $12; fail_time $1 $2 $3 } else if ($0 ~ /Accepted publickey/) { acc_ip $11; acc_time $1 $2 $3 if (fail_ip acc_ip (mktime(acc_time) - mktime(fail_time)) 300) print Suspicious:, fail_ip, failed at, fail_time, then accepted at, acc_time } } /var/log/auth.log原理pam_unix失败说明密码错但紧接着同一 IP 用密钥登录成功暗示攻击者已窃取私钥或利用了密钥泄露。Root 权限滥用sudo相关日志需检查 secure# 在 /var/log/secure 中找 sudo 提权CentOS awk /sudo:.*COMMAND:/ {print $1,$2,$3,$9,$10,$11,$0} /var/log/secure | head -10原理sudo日志记录提权命令$9是执行用户$10是目标用户root$11是命令。虽不在 auth.log但属 SSH 后续行为必须关联。3.3sshd_config配置如何影响日志内容日志的“信息密度”直接受sshd_config控制。很多分析失效根源在于配置没调好。以下是关键参数及awk分析适配建议LogLevel默认INFO只记录Failed password、Accepted。设为VERBOSE后会多出debug1: key_parse_private_pem: ...等密钥解析细节awk可提取key_parse_private_pem字符串定位私钥格式错误但日志量暴增 3 倍。建议应急时临时sed -i s/^#*LogLevel.*/LogLevel VERBOSE/ /etc/ssh/sshd_config systemctl reload sshd分析完切回INFO。MaxAuthTries默认 6。设为 3 可加速触发pam_faillock锁定awk可捕获pam_faillock(sshd:auth): user root locked日志比单纯数失败次数更准。注意MaxAuthTries 3后Failed password日志仍会出现但第 4 次起变为pam_faillock事件。UsePAM yes启用 PAM 后pam_faillock、pam_tally2日志才生效。awk /pam_faillock|pam_tally2/可直接看到账户锁定状态比数Failed password更可靠。PermitRootLogin设为no后Invalid user root日志消失但Failed password for root仍存在因 root 账户未删。awk分析时需意识到root登录失败不等于 root 账户存在可能是攻击者盲目猜测。实操心得我曾在一个政府项目中发现sshd_config的LogLevel被设为QUIET导致 auth.log 几乎为空。awk再强也无米下炊。所以“玄机”第一步永远是cat /etc/ssh/sshd_config | grep -E ^(LogLevel|MaxAuthTries|UsePAM|PermitRootLogin)确认日志源质量。4. 实操全流程从原始日志到攻击画像的 7 步推演现在我们把前面所有知识点串起来走一遍完整的“玄机”实操流程。以下基于真实靶场场景你拿到一个auth.log文件约 120MB客户说“最近服务器疑似被控请分析”。整个过程严格遵循“表层→中层→深层”三层递进每步给出命令、输出示例、解读逻辑和避坑提示。4.1 第一步日志探针——确认文件状态与基础结构30秒# 1. 查看文件大小和最后修改时间 ls -lh /var/log/auth.log # 输出-rw-r----- 1 syslog adm 124M Jan 15 15:30 /var/log/auth.log → 确认是活跃日志 # 2. 查看开头和结尾确认格式 head -n 3 /var/log/auth.log # 输出Jan 15 00:01:02 ubuntu-server sshd[1234]: Invalid user admin from 192.168.1.100 port 54322 ssh2 # Jan 15 00:01:03 ubuntu-server sshd[1235]: Failed password for root from 192.168.1.100 port 54323 ssh2 # Jan 15 00:01:04 ubuntu-server sshd[1236]: Connection closed by 192.168.1.100 port 54323 [preauth] tail -n 3 /var/log/auth.log # 输出Jan 15 15:29:58 ubuntu-server sshd[98765]: Accepted password for admin from 192.168.1.200 port 54321 ssh2 # Jan 15 15:30:01 ubuntu-server sshd[98766]: pam_unix(sshd:session): session opened for user admin by (uid0) # Jan 15 15:30:05 ubuntu-server sshd[98766]: pam_unix(sshd:session): session closed for user admin # 3. 统计字段数定位 IP 位置 awk {print NF, $0} /var/log/auth.log | head -n 5 | column -t # 输出16 Jan 15 00:01:02 ubuntu-server sshd[1234]: Invalid user admin from 192.168.1.100 port 54322 ssh2 # 15 Jan 15 00:01:03 ubuntu-server sshd[1235]: Failed password for root from 192.168.1.100 port 54323 ssh2 # 14 Jan 15 00:01:04 ubuntu-server sshd[1236]: Connection closed by 192.168.1.100 port 54323 [preauth] # → 确认Invalid user 行 16 字段IP 在 $12Failed password 行 15 字段IP 在 $11解读这一步不是走过场。head/tail看到Invalid user和Failed password并存说明攻击者在枚举用户名tail显示Accepted password for admin表明已有成功登录字段数差异提醒你 IP 位置不固定。所有后续awk命令都必须基于此现场确认而非文档假设。4.2 第二步表层扫描——5秒定位最新攻击动态10秒# 快速查看最近 20 条失败记录 grep -E (Failed password|Invalid user) /var/log/auth.log | tail -n 20 | awk {print $1,$2,$3,$9,$11,$12} | column -t # 输出 # Jan 15 15:28:45 admin 192.168.1.100 - # Jan 15 15:28:46 root 192.168.1.100 - # Jan 15 15:28:47 test 192.168.1.100 - # ... # Jan 15 15:29:58 admin - 192.168.1.200 ← 注意最后一行是 Accepted$12 为空$11 是 IP解读column -t对齐显示一眼看出192.168.1.100在 15:28 频繁尝试admin/root/test而192.168.1.200在 15:29:58 成功登录admin。表层结论攻击者 A100在暴力枚举攻击者 B200已成功且目标是admin账户。避坑grep后直接awk提取关键字段避免tail -n 20后再awk导致管道延迟。4.3 第三步中层聚合——量化攻击强度与目标45秒# 统计所有攻击 IP 的失败次数兼容字段位置 awk /Failed password|Invalid user/ { if ($6 ~ /Invalid/ $7 user) ip $12 else if ($6 ~ /Failed/ $7 for) ip $11 else next count[ip] if ($6 ~ /Invalid/) user_enum[ip][$8] # 记录该 IP 枚举的用户名 } END { print Top 10 Attack IPs (Failures 10) for (i in count) { if (count[i] 10) { printf %-15s %5d\n, i, count[i] } } print \n User Enumeration per IP for (ip in user_enum) { if (count[ip] 50) { printf %-15s: , ip n 0 for (u in user_enum[ip]) { if (n 5) printf %s(%d) , u, user_enum[ip][u] } print } } } /var/log/auth.log输出示例 Top 10 Attack IPs (Failures 10) 192.168.1.100 287 185.143.221.77 42 10.0.0.55 18 User Enumeration per IP 192.168.1.100: admin(87) root(72) test(65) guest(32) user(21)解读192.168.1.100失败 287 次远超阈值且枚举了admin/root/test等 5 个常见用户名确认为暴力破解主力。185.143.221.77虽只有 42 次但来自俄罗斯需警惕。中层结论主攻 IP 是192.168.1.100目标明确指向admin账户。避坑user_enum[ip][$8]用二维数组记录每个 IP 的用户名频次避免用print $8 | sort | uniq -c导致子进程开销。4.4 第四步深层关联——追踪成功登录的完整路径2分钟# 提取所有成功登录事件并关联其前后 30 秒内的失败记录 awk /Failed password|Invalid user|Accepted (password|publickey)/ { if (/Failed password|Invalid user/) { time $1 $2 $3 ip ($6 ~ /Invalid/ ? $12 : $11) user ($6 ~ /Invalid/ ? $8 : $9) fail_log[ip,user,time] $0 } else if (/Accepted (password|publickey)/) { time $1 $2 $3 ip $11 user $9 acc_log[ip,user,time] $0 # 检查同一 IP 用户在 30 秒内是否有失败 for (key in fail_log) { split(key, a, ,) if (a[1] ip a[2] user (mktime(time) - mktime(a[3])) 30) { print ALERT: ip - user failed at a[3] then accepted at time print Fail: fail_log[key] print Acc: acc_log[ip,user,time] print --- } } } } /var/log/auth.log | head -n 20输出示例ALERT: 192.168.1.200 - admin failed at Jan 15 15:29:55 then accepted at Jan 15 15:29:58 Fail: Jan 15 15:29:55 ubuntu-server sshd[98764]: Failed password for admin from 192.168.1.200 port 54321 ssh2 Acc: Jan 15 15:29:58 ubuntu-server sshd[98765]: Accepted password for admin from 192.168.1.200 port 54321 ssh2 ---解读发现192.168.1.200在 15:29:55 输错密码15:29:58 就成功登录admin间隔仅 3 秒——这绝非巧合极可能是攻击者已掌握正确密码或利用了会话重放。深层结论192.168.1.200是已控 IPadmin账户凭证已泄露。避坑mktime()函数将Jan 15 15:29:55转为 Unix 时间戳确保时间差计算准确head -n 20防止输出过长。4.5 第五步横向移动探测——检查admin是否提权或访问其他服务1分钟# 1. 检查 admin 的 sudo 行为需查 auth.log 和 secure # 先看 auth.log 中 admin 的所有活动 awk $9 admin {print $0} /var/log/auth.log | head -n 10 # 2. 检查 /var/log/secure 中 admin 的 sudo 命令CentOS awk /sudo: admin : TTY/ $9 admin {print $1,$2,$3,$10,$11,$12,$0} /var/log/secure 2/dev/null | head -n 5 # 3. 检查 admin 是否通过 SSH 登录其他机器SSH 跳转 awk $9 admin /sshd\[.*\]: .*.* via ssh/ {print $0} /var/log/auth.log | head -n 5输出示例Jan 15 15:30:01 ubuntu-server sshd[98766]: pam_unix(sshd:session): session opened for user admin by (uid0) Jan 15 15:30:05 ubuntu-server sshd[98766]: pam_unix(sshd:session): session closed for user admin # → 无 sudo 日志secure 为空无跳转日志 → admin 未提权未横向移动解读admin会话仅持续 4 秒且无sudo或ssh
网站建设高端定制企业官网