文章总结: 本文介绍利用条件竞争漏洞绕过登录接口的失败次数锁定机制。通过并行发送大量登录请求,在服务器更新失败计数器之前完成多次尝试,从而突破三次锁定限制。作者使用TurboIntruder工具配合单数据包攻击脚本实现并发爆破,并成功获取目标账户密码。该技术展示了看似严密的限制在并发面前可能存在的破绽,为渗透测试提供了一种绕过登录限速的实战技巧。 综合评分: 76 文章分类: 渗透测试,漏洞分析,web安全,红队,安全工具
一个条件竞争漏洞,让我用Turbo Intruder刷爆登录接口|赏金技巧
原创
升斗安全XiuXiu 升斗安全XiuXiu
升斗安全
2026年8月18日 07:45 广东
在小说阅读器读本章
去阅读
【文章说明】
- 目的:本文内容仅为网络安全技术研究与教育目的而创作。
- 红线:严禁将本文知识用于任何未授权的非法活动。使用者必须遵守《网络安全法》等相关法律。
- 责任:任何对本文技术的滥用所引发的后果自负,与本公众号及作者无关。
- 免责:内容仅供参考,作者不对其准确性、完整性作任何担保。
阅读即代表您同意以上条款。
锁得住三次,锁不住同一秒:一个登录限速的竞态(条件竞争)绕过
今天聊一个很有意思的场景:目标站点的登录接口做了防爆破,同一个用户名连续错三次就会被临时锁定。表面上看,这条路被堵死了。但如果换个思路:我不跟你拼次数,我跟你拼速度,在你的“计数器”还没反应过来之前,把所有尝试同时砸过去,会怎样?
先摸清它的防御逻辑
我先拿自己的账户做实验,故意连续输入错误密码。三次之后,账户果然被临时禁止登录了。
接着我换了一个随便编的用户名去试,返回的是正常的“用户名或密码无效”,而不是什么“账户已锁定”之类的提示。这说明限速逻辑不是按会话或者 IP 来做的,而是按用户名单独计数的。换句话说,服务器端为每个用户名维护着一个“失败次数计数器”。
这时候,一个经典的竞态问题就浮出水面了:
从“我提交登录请求”到“服务器把失败次数加一”之间,是否存在一个极短的时间窗口?
如果能抢在这个窗口内把一堆请求同时塞进去,那“三次锁定”这个限制可能就形同虚设了。
先按正常节奏走一遍
在 Burp 的历史记录里,我找到了那条带着自己账户错误密码的 POST /login 请求,把它送进 Repeater。
为了后面做对比,我先按顺序发送一组请求。操作上很简单:
- 把这个请求的标签页放进一个分组里
- 右键选择“复制标签页”,一口气复制出 19 个副本
- 然后让这 20 个请求按顺序、各自走独立连接发出去
结果完全在意料之中:在之前已经累积了两次失败的基础上,再错两次,账户就被锁了,后面的请求全部被拒。这个表现非常“守规矩”,防御逻辑工作得很正常。
关键一步:同时发出去
接下来,我把同一组请求再发一遍,但这次不是排队走,而是让它们并行,也就是几乎同时发出。
观察响应的时候,有意思的事情出现了:虽然账户已经触发了锁定,但依然有好几个请求拿到了正常的“用户名或密码无效”响应。也就是说,即使在“账户应该已经被锁”的状态下,依然有请求成功进入了密码校验流程。
这说明我们的猜想是对的:服务器在更新失败计数器之前,确实存在一个极小的窗口期。只要速度足够快,就能在锁定真正生效之前,多塞进去好几次尝试。
把窗口期变成武器
既然并行能突破三次限制,那接下来就把这个思路武器化。
我选中请求里的 password 参数,右键把它送到 Turbo Intruder。它会自动把密码位置标记成 %s,当作爆破点。
然后把用户名改成目标账户 admin,在模板下拉框里选择 examples/race-single-packet-attack.py。这个脚本的核心思路非常直接:把所有候选密码的请求先排队,但一个都不发;等所有请求都准备就绪之后,调用一次 engine.openGate(‘1’),让它们在同一个瞬间全部冲出去。
因为目标支持 HTTP/2,所以这里用 Engine.BURP2 配合单连接模式,效果最好。
完整的脚本逻辑如下,密码字典直接从你复制到剪贴板中读取:
def queueRequests(target, wordlists): # 目标支持 HTTP/2,使用单数据包攻击 # engine=Engine.BURP2 和 concurrentConnections=1 是关键 engine = RequestEngine(endpoint=target.endpoint, concurrentConnections=1, engine=Engine.BURP2 ) # 从剪贴板读取候选密码列表 passwords = wordlists.clipboard # 把每个密码对应的登录请求排队 # 'gate' 参数会拦住所有请求,直到 openGate() 被调用 for password in passwords: engine.queue(target.req, password, gate='1') # 所有请求就绪后,打开闸门,同时发送 engine.openGate('1')def handleResponse(req, interesting): table.add(req)
把准备好的候选密码复制到剪贴板,然后启动攻击。
如果一轮下来没有成功,就等账户锁定重置,把已经确认错误的密码从列表里删掉,再跑一轮。一旦看到某个请求返回了 302,就说明登录成功了,对应的密码就在“有效载荷”那一列里。
拿到密码之后,等锁定时间过去,就能正常登录 admin 的账户了,这样就完成了已知账号,在有限制的情况下,也能成功爆破。
整个过程的本质就是在跟服务器抢时间:它想锁,我们比它锁得更快。这种条件竞争型的漏洞,往往就藏在“校验”和“计数”之间那几毫秒的缝隙里。很多看似严密的限制,在并发面前都会露出破绽。
如果你觉得这篇文章对你有帮助,别忘了一键三连,点赞、在看、转发走一波!你们的支持是我持续更新更多实战技巧的最大动力。也欢迎把文章推荐给身边一起挖洞的朋友,说不定下次他们遇到类似目标时,就能用上这套思路。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:升斗安全 升斗安全XiuXiu 升斗安全XiuXiu《一个条件竞争漏洞,让我用Turbo Intruder刷爆登录接口|赏金技巧》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论