ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

CTF Web爆破全解析:从口令到token的实战技术指南

CTF Web爆破全解析:从口令到token的实战技术指南 1. 爆破到底是什么ctfshow的爆破专题在训练什么先说说我自己的感受。如果你玩过几道ctfshow的web入门题你大概已经发现了爆破这个专题放在web入门阶段位置相当巧妙它不要求你有多深的代码审计功底也不会让你拿着一堆看不懂的字节流发呆它本质上是让你学会跟服务器对话——通过反复的、有策略的请求把服务器藏在代码逻辑里的漏洞给问出来。很多新手第一次听到爆破会觉得这是一个纯体力活拿着burpsuite挂上字典跑就完事了。这么想就错了。爆破在CTF里其实是最讲性价比的一种手法什么情况下才值得爆破答案永远是枚举空间足够小且结果可以被自动判定。服务器端没有限速、没有锁定机制、没有有效校验这时候你才用得上爆破如果对方有WAF、有图形验证码、有高频封禁那你还在傻乎乎地跑大字典只会把自己跑进小黑屋。ctfshow的爆破专题题目恰恰把这几类该爆破和看似该爆破但其实要另想办法的场景都覆盖了这是它作为入门训练最值钱的地方。再说得直白一点爆破题训练的是三件事第一抓包和分析请求的能力第二选择合适payload和字典的能力第三确定成功信号的能力。最后这一条经常被忽略——很多人跑了半天其实flag早就出现在响应包里了但他盯着状态码看硬是没认出来。所以这篇文章我不打算只教你怎么点按钮而是想带着你把爆破的完整链路捋顺从分析请求包开始到配置工具到写脚本到判断结果最后再说说那些用血泪换来的避坑经验。对于刚准备入门CTF Web的新人这个专题也是性价比最高的起点之一。因为爆破牵扯到的知识点不会太深但覆盖面广HTTP协议、编码方式、Cookie机制、验证码逻辑、甚至一点点的Python脚本能力全都能在这里练到。你不需要先啃完一整本HTTP权威指南只需要开着burpsuite跟着题目一步一步看请求和响应很多概念自然就通了。2. 爆破题的常见目标拆解口令、验证码、token、cookie2.1 口令爆破最经典的弱口令与HTTP认证口令爆破是Web世界里最常见的爆破场景。你在ctfshow爆破专题里遇到的第一批题目大概率就是从登录框开始的一个用户名输入框一个密码输入框点击登录POST请求发出去服务器返回用户名或密码错误。这类题目的漏洞点通常不在加密而在没有任何防爆破机制。没有图形验证码没有登录失败次数锁定没有连续请求限制于是攻击者可以无限次地猜测口令。这时候爆破就变成了一个纯粹的穷举问题把字典里的密码一个一个试过去。实操上你需要先在burpsuite里抓到这个登录请求看清楚POST参数名。比如可能是usernameadminpassword123456这种也可能像某些题目那样参数名做了混淆比如namexxxpwdyyy甚至是用JSON格式提交的{username:admin,password:admin123}。参数名搞对了爆破就成功了一半。这里有个新手容易踩的坑参数位置选错。有时候你要爆破的字段不只一个比如用户名和密码都要猜这时候你需要把Intruder模块的Attack Type切换成Cluster bomb在username和password两个值上分别标记变量。如果你用Sniper模式只标了一个变量那等于一次只能试一个字段另一个字段永远是固定值自然什么都爆不出来。Sniper和Cluster bomb的区别一句话总结就是Sniper适合多个候选值依次替换一个位置Cluster bomb适合多个位置组合遍历。除了常规的POST表单登录HTTP Basic认证也是口令爆破的常客。ctfshow爆破专题里有一道典型题响应头里出现了WWW-Authenticate: Basic realm...浏览器弹出一个原生的用户名密码输入框。这种认证的原理是把用户名:密码用Base64编码后放到Authorization请求头里格式是Authorization: Basic base64(user:pass)。爆破这种题目有两种思路在burpsuite里对Authorization头的值标记变量payload类型选Custom iterator让burpsuite自动生成用户名:密码组合后做Base64编码——实际操作比较繁琐更推荐的方式写Python脚本把用户名和密码都读进来命令行里用base64.b64encode编码后拼接请求头循环发送请求。几行代码就能搞定。我个人的经验是在ctfshow这类靶场上遇到Basic认证不要急着用Intruder硬跑因为每次请求都要重新编码脚本处理的效率远高于GUI操作。而且脚本能方便地做二次判断——比如响应里出现了flag字样就停下来并打印当前请求信息这在跑大字典时省事得多。2.2 验证码绕过一次性校验、参数可控、可预测验证码爆破是ctfshow爆破专题里比较有意思的一类题目。表面上登录框带着一个图片验证码让你觉得这不是没法爆了吗但仔细一测发现验证码压根没起任何作用。常见的验证码缺陷有三种我分别说一下这都属于实战中你会反复遇到的类型第一种是验证码只校验一次、且校验过后可以复用。你随便输一个错误验证码点击登录服务器返回验证码错误但如果你抓包把验证码参数改成一次成功请求里用过的某个值再配合正确的用户名密码就能登录成功。这类题目的核心是验证码的校验状态在服务端没有正确更新或者校验逻辑放在了用户名密码正确之后才做——一旦登录成功验证码标记就失效了。第二种是验证码参数可控、可绕过。有的题目验证码校验逻辑是如果验证码参数为空则跳过验证或者如果验证码参数是一个特定值就放行再或者验证码值根本没存session而是藏在某个前端JS代码里。拿到源码稍微审计一下就能发现。第三种是验证码有效期太长、且可预测。有些验证码是4位数字、存了session、有效期内无限次校验通过那么你完全可以忽略图片识别直接对4位数字做0到9999的枚举爆破。服务器根本不会因为你试的次数多而更换验证码。验证码类题目的一个通用测试技巧先不提交验证码参数直接看看响应是不是和带了验证码参数时一样。如果一样说明服务端根本没做校验那你可以把验证码参数删掉专心爆破用户名和密码。如果响应不一样再考虑固定一个历史有效验证码值来复用。这个删参数对比响应的思路在ctfshow爆破专题里屡试不爽也是我在真实授权测试中排查登录接口时最先做的动作。2.3 token与时间戳爆破随机数不够随机有CTF经验的师傅应该都知道token类题目玩的是代码审计 爆破的组合拳。ctfshow爆破专题里token型题目的经典套路是这样的登录时需要提交一个token参数这个token不是随机的而是根据某个可预测的规则生成的比如md5(时间戳)、md5(用户名固定字符串)或者更简单的token本身就存在响应里的某个字段中你只需要把它动态取出来下一步请求时带上即可。很多新手看到token就开始慌觉得token这种东西没法爆破。其实完全相反凡是按规则生成的token比随机密码更容易搞定。因为密码你还是不知道取值范围token的生成规则一旦被你弄明白了枚举空间可能只有几万个甚至几百个。在处理这类题目时我习惯的流程是打开开发者工具看源码F12或者直接看题目是否贴出了部分PHP/Python源码在源码里找token相关的逻辑比如time()、rand()、mt_rand()、md5、substr这类关键词确认token的生成规则后写一个脚本同步生成token替换到你爆破请求的token字段里。这里有个重要的实操心得token的时效性往往很短。如果你用burpsuite的Intruder跑它不会自动帮你同步更新token你可能跑了几百次请求token字段全部用的是同一个值服务器在第几次请求之后就开始拒绝你了。这种场景下纯GUI工具反而不如写脚本灵活——脚本里的token生成函数可以做到每请求一次就重新算一次完全模拟合法客户端的逻辑。所以我一直建议玩到token类爆破题的时候尝试动手写Python脚本宁可多花半小时也不要拿Intruder硬刚。2.4 cookie字段爆破会话信息的越权遍历最后一类常见的爆破对象是cookie。Cookie里保存的某些字段如果设计得不够安全就变成了你的身份凭证且这个凭证是可以被枚举遍历的。ctfshow爆破专题里有一道题登录成功后服务器下发一个cookie里面有一个字段值类似user0或者vip0。你尝试手动把0改成1再访问一个需要管理员权限的页面就发现权限验证通过了。这种漏洞严格来说不算爆破而是逻辑漏洞中的水平越权或垂直越权。但有时候这个字段值不是简单改一个数能搞定的比如它经过某种编码或混淆此时就需要爆破出正确的cookie值。cookie类爆破的关键是搞清楚cookie值的生成规则。有些题目直接用自增数字作为cookie里的标识符你只要遍历1到1000就能撞出管理员也有的题目用md5(uid)或者固定盐值拼出来的hash这时候你第一反应应该是去源码里找生成这个cookie的函数而不是盲目爆。我在做这类题目时习惯先把响应头里的Set-Cookie字段和请求头里的Cookie字段全部看一遍用burpsuite的Repeater手动改几个值试试响应差异。如果改一个值就出现明显变化说明服务端对cookie值的校验很弱这种题往往不需要大规模爆破手工就能搞定。四类爆破目标的对比爆破对象典型漏洞成因判断要点推荐工具口令POST/HTTP Basic无限速、无锁定、弱口令POST参数或Authorization头响应差异明显Burp Intruder / Python脚本验证码校验未生效、可复用、可预测删除验证码参数观察响应变化Burp Repeater 脚本token生成规则可逆、可预测源码审计发现生成规律Python脚本cookie字段弱会话标识、可枚举字段Set-Cookie值简单、改值后响应不同Repeater手工 脚本3. 实操全流程从配置burpsuite到拿到flag3.1 环境准备代理、浏览器、证书一次配齐爆破的第一步不是开burpsuite而是先保证你能拿到完整的请求包。我见过太多新手在抓包环节就已经卡住了其实这个环节十分钟内就能搞定。工具准备其实就三样一个浏览器Chrome或Firefox都行、一个burpsuite社区版完全够用ctfshow的题目用不到专业版功能、以及一个可用的靶场账号。我第一次跑ctfshow爆破题时用的就是burpsuite社区版你连破解的需求都不会有。配代理的步骤我捋一遍打开burpsuite切到Proxy标签页确认Options里面的Proxy Listeners列表里有一条127.0.0.1:8080的记录。如果没有自己Add一条绑定地址写127.0.0.1端口8080浏览器设置里配置代理HTTP代理指向127.0.0.1端口8080浏览器访问http://burp下载CA证书并安装到系统信任列表。这一步是为了抓HTTPS流量ctfshow大部分题目都是HTTP但如果某些题目用了HTTPS你不装证书就只能看到无法解析的加密报文装完证书回到burpsuite的Proxy Intercept标签页确认Intercept按钮不是亮着的保持关闭状态这样流量会直接经过但不拦截方便你在HTTP history里翻请求记录。证书安装这块我提醒一句安装证书时一定要选择受信任的根证书颁发机构而不是随意导入。有些新手装完证书后发现浏览器仍然报证书错误多数是因为导入位置选错了。安装完记得重启浏览器让证书生效。3.2 抓包定位先看请求长什么样环境准备好之后打开ctfshow的爆破专题第一道题在登录框里随便输入一个用户名和密码点击登录。这时候切到burpsuite的HTTP history找到刚才那个POST请求。你要学会读这个请求里的几样东西请求行POST /login.php HTTP/1.1说明往login.php提交数据请求头Content-Type: application/x-www-form-urlencoded说明表单格式是普通的URL编码如果是application/json那你爆破时要注意payload的写法请求体usernameadminpassword123456这是你的爆破变量所在地响应体找一下你提交错误口令后服务器返回的内容比如用户名或密码错误这类的提示注意它的状态码是200还是302。拿到请求之后先把请求发送到Repeater右键Send to Repeater手动改几个密码再点Send观察响应变化。这一步的目的是确认爆破的可行性如果连续发10次请求服务器每次都照样返回错误信息没有验证码校验没有锁定提示那就可以放心开爆了。如果第3次请求后突然返回try again later之类的内容说明服务器有频率限制你需要在爆破策略里加延迟。3.3 Intruder配置标记两个坑位选对字典确认请求没问题后右键Send to Intruder。Intruder界面有四个标签页重点是前三个Target、Positions、Payloads。Target页保持默认攻击目标已经自动带出来了。Position页里你需要手动把要爆破的参数值选中点Add §标记变量。比如请求体是usernameadminpassword123456我把123456选中加§变成usernameadminpassword§123456§表示这里是要遍历的变量。攻击类型保持在Sniper因为我们只爆破一个密码字段用户名已知是admin。如果用户名也要爆那就把admin也选中标记攻击类型切换成Cluster bomb。Payloads页是关键。在Payload Options的Simple list里你可以手动往列表里输入几个常用密码也可以点Load从一个字典文件里导入。ctfshow爆破专题有些题目会直接给你一个候选密码集合比如题目描述里放了一个链接里面是几百个弱口令。这种情况直接用题目给的字典就行如果没有提供我一般用网上公开的常用弱口令字典几百条到几万条不等。选好字典后看右下角的Resource pool设置。社区版默认有一个线程池你可以把Maximum concurrent requests调成10到30。新手不要一上来就调500很多题目服务器根本扛不住而且会把你的行为变成明显的攻击特征。一个不严谨但实用的经验是先用20个线程跑一圈小字典看看响应情况再决定是否加大规模。设置完毕后点右上角Start Attack等待结果。弹窗里每一行代表一次请求对应各个payload。你要关注的列是Status和Length状态码出现异常值比如200变成302跳转、响应长度突然和大多数请求不同这两者都意味着可能爆破成功了。3.4 用Python脚本爆破比GUI更灵活的第二方案在ctfshow爆破专题里纯Intruder能解决的题目大概占一半左右剩下的一半要么是token需要动态同步更新要么是Basic认证需要编码要么是登录请求里有需要先获取的隐藏字段。这些场景下我建议你直接写Python脚本。一个最基础的POST登录爆破脚本结构大概是这样的import requests url http://target/login.php data { username: admin, password: 123456 } with open(passwords.txt, r, encodingutf-8) as f: passwords f.read().splitlines() for pwd in passwords: data[password] pwd resp requests.post(url, datadata, timeout5) if resp.status_code 302 or flag in resp.text or len(resp.text) ! 1000: print(f[] 命中: {pwd}, 状态码: {resp.status_code}, 响应长度: {len(resp.text)}) print(resp.text[:500]) break这个脚本的思路是把密码字典逐行读进来每次替换data里的password字段发送请求然后用resp.status_code、resp.text的长度、响应中是否出现flag关键字来做命中判定。只要服务器做出了不一样的响应就直接打印并停止循环。实战中你往往需要根据题目调整判断条件。比如有的题目登录成功后会跳转到index.php那么状态码302就是命中信号有的题目登录失败和登录成功返回的页面长度差几十个字节那就把长度阈值设进去。另外如果你的Linux环境没有requests库先执行pip install requests装一下。如果你要在Windows上跑中文路径的字典文件记得encoding那里用gbk否则读文件会乱码——我自己就在Windows上踩过这个坑读出来的密码全是乱码爆了一整轮才发现。3.5 命中判定别把flag漏在脚下爆破成功之后怎么确认这个环节我单独拿出来讲因为有一半的新手不是爆不出来而是爆出来了没发现。判定命中的三个主要信号分别是状态码变化最常见的形式是登录成功后302跳转失败时404或200原地刷新响应长度变化这个在Intruder结果列表里最直观。正常失败请求的响应长度通常是固定的比如1200字节一旦某个密码触发了不同的逻辑分支响应内容多了或少了长度就变了。你在Intruder结果页面按Length列排序看谁的长度和别人不一样响应内容特征比如成功响应里包含flag{、welcome、index.php、用户名或密码正确等字样。这个需要在Intruder的Settings里加一个Grep-Match输入flag等关键词burpsuite会在结果里自动标记命中。经验之谈跑完之后不要只看前几行结果直接把Length列点一下排序把长度异常的行单独拎出来看响应体。很多情况下正确密码产生的响应只比错误密码长几十个字节肉眼不容易看出来但排序一到差异立刻显现。4. 常见问题与排查技巧实录4.1 跑了半天一个都没中怎么办这种情况我遇到太多了先说结论90%的情况不是你字典不好而是你配置出了问题。逐一排查第一确认变量位置。回到Intruder的Positions页仔细看标记是否只圈住了你要爆破的值。如果§标的位置偏了一个字符比如把password§123456§标成了password§123456那等于把参数名后半部分也当成变量了永远不可能命中。第二确认请求头是否正确。有些登录接口要先带一个Cookie头或者Authorization头才能访问如果你抓的请求里带了这些头但Intruder跑的时候莫名其妙丢了一般是内置的Header处理设置问题会导致所有请求都被拒绝。对比一下原始请求和Intruder里的请求头逐一核对。第三确认是否有动态字段需要处理。服务器可能在登录页面里埋了一个csrf_token隐藏字段每次刷新页面都会变。你直接拿旧的csrf_token去爆破第一次请求就失败了后面全无效。这种情况你需要先写脚本在每次请求前GET一次页面把token提取出来再POST。本质上这已经属于用脚本做会话保持的爆破。第四确认响应判定条件。状态码200未必是失败302也未必是成功得看具体逻辑。有的题目登录失败时状态码就是200且返回登录页面登录成功时会重定向但302不一定出现在每一个题里。把其中一条成功响应的完整内容抓出来看看到底有什么特征是失败响应里没有的。4.2 线程开太高被封了或者被强制下线ctfshow这类靶场为了保护服务器有时会对高频请求做了限制。你如果一口气跑到几个小时的进程里没有限制地发请求可能触发服务器的防爆破策略之后所有请求都会返回同一个提示。处理方式很简单把线程降到5以下或者让脚本在每次请求之间sleep 0.1秒到0.5秒。Python脚本里加一行time.sleep(0.2)不会让整体速度慢太多但能大大降低触发防护的可能性。另外一个常见问题是爆破的过程中如果服务器返回了302requests默认会跟随跳转跳到登录成功后的页面这个过程可能会额外消耗时间甚至触发一些异常。建议在requests里设置allow_redirectsFalse只关心第一次响应结果需要跳转的话再手动GET目标页面。4.3 爆破结果全是一样的响应怎么快速定位问题这种情况通常是请求包本身有问题比如提交的数据格式错了。我整理了一个排查顺序表你可以照着检查现象可能原因下一步操作所有请求响应完全一致变量位置没标对、payload没实际替换检查Positions标记所有请求都是401/403缺少必要的认证头或Cookie补全请求头响应里有验证码错误提示验证码参数固定、但服务端要求动态值处理验证码逻辑或用脚本同步状态码全302但内容相同302可能是统一跳转到某错误页追踪Location头看跳转后内容少量请求长度异常字典里存在类似格式的干扰项查异常响应文本确认是否为目标内容排错的一个核心技巧是把Intruder的请求抓到Repeater里手动改payload发送一次。如果手动发送能得到不同结果那说明Intruder里跑出来的全一样一定是你配置层面的问题如果手动发送也得不到不同结果那就要考虑题目的正常入口是不是变了比如服务端已经改用了别的校验方式。4.4 一个必须反复强调的底线授权与靶场边界写到这里我要专门花一段篇幅说一件非常重要的事。爆破这个技术本身没有错但它的杀伤力取决于用在什么地方。ctfshow是一个合法的在线靶场平台它的爆破专题就是专门留给安全爱好者练习的——有明确的练习目的、有平台授权、有隔离环境在这里怎么折腾都行。但同样的爆破手法如果用在未经授权的真实系统上性质就完全不同了。私自在别人的网站上进行口令爆破属于违法攻击行为这一点没有任何灰色地带。所以我建议所有读者在做题时养成一个习惯每一次爆破操作前问自己一句我对这个目标是否有明确的权限答案如果是靶场CTF比赛自己的测试环境那可以放手去练答案如果是否定的就到此为止。这也是为什么我一直推荐通过ctfshow这类平台来学习爆破的原因它给你提供了足够真实的Web环境让你能把技术动作练扎实同时又把法律边界画得清清楚楚。真正的安全能力是既懂技术又懂边界。5. 我的个人体会与后续扩展方向爆破专题做完之后我的一个很明显的感觉是做题时对请求包的敏感度会大幅提升。以前看到一长串HTTP头部就直接跳过现在会习惯性地看一眼Cookie、Authorization、X-Forwarded-For这些字段遇到登录页面也会下意识想一下这里有没有限速。这种敏感度恰恰是Web安全的入场券。后面你再接触SQL注入、XSS、文件上传时会发现所有攻击手法的第一步都是同样的动作——抓包、看参数、理解服务端逻辑。爆破专题像是一个微缩模型把Web攻击最通用的操作流程浓缩在里面了跑完一遍你至少不会再对burpsuite感到陌生。如果你做完ctfshow爆破专题还想继续深入我有两个建议。第一把爆破脚本的灵活性再拔高一层试着用requests写一个支持多线程的爆破框架雏形比如用concurrent.futures做并发请求遇到命中就自动终止所有线程。这个练习能让你把并发编程和HTTP请求结合起来后续写复杂工具时很有用。第二往Web漏洞链路的方向延伸多关注登录接口之外的漏洞——验证码识别方案可以往OCR方向走token生成规则可以往伪随机数审计方向走cookie越权可以往会话安全问题走。最后我在实际做题过程中最深的体会是爆破题花的时间常常不在跑上而在分析上。分析清楚题目想让你利用哪个逻辑缺陷、正确的payload长什么样、成功响应里有什么特征这三件事做扎实了爆破跑起来往往几秒钟就出结果。反而是一上来就挂字典狂跑的人大概率跑个大半个小时颗粒无收。希望这篇文章能帮你把先分析、后动手的习惯培养起来这对你接下来走Web安全这条路会是一笔持续复利的投资。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进