Web后台弱口令批量检测实战:判定、限速与复核

发布时间:2026/11/6 2:10:35
Web后台弱口令批量检测实战:判定、限速与复核

简介:面向网络安全从业者与渗透测试学习者的Web后台弱口令检测工具WebCrack,以开源免费形式提供,聚焦弱口令与万能密码的批量探测,适用于授权范围内的网站安全审计、漏洞排查及检测原理学习。压缩包共18个文件、约114KB,主体由12个Python脚本构成,辅以3个文本字典与地址配置、说明文档和示意图,整体轻量,便于阅读源码、调整字典及二次开发。已有413人学习下载。目录按解析、爆破任务、字典生成、请求头与日志等模块拆分,读者可据此理解批量检测流程,并参考配置项组织用户名密码字典、观察任务输出与日志记录。对需要开展合规安全测试、学习弱口令检测原理及积累工具排错思路的读者,这份结构完整的小型项目具有较强参考价值。

1. 后台弱口令批量检测:先想清楚「谁在试你的门」

去年做一次内部自查,我拿一份两千多条的口令字典,对自家六十多个后台入口跑了一遍Web 后台弱口令批量检测,十七分钟出结果:9 个账号能直接登进去,其中 7 个是运维随手建的测试号,口令是admin888和test123这种。当时第一反应不是「工具真猛」,而是「这些入口挂了两年没人管」。WebCrack这类开源工具的价值不在爆破本身,而在于它把「你的后台到底有多好猜」这件事量化了。这篇写给两类人:一类要给自己系统做体检的安全同学,一类是接手了一堆老后台、想知道从哪儿下手的运维和开发。下面讲判定逻辑、并发取舍、可复现的自查流程,以及五个我踩过的坑。

2. 判定逻辑拆解:一次登录请求里藏着四个判断点

批量检测听着玄学,拆开看就是四件事:请求怎么构造、成功怎么判定、万能密码为什么能过、字典怎么选。这四件事任何一件判断错,跑出来的结果要么全是误报,要么一条都命不中。我在第一次跑的时候就吃过「全站 100% 成功」的亏,后面会讲。

2.1 请求构造:字段名是读出来的,不是猜出来的

后台登录绝大多数是 POST 表单,字段名五花八门:username、user、userid、loginname、name、account,密码侧是password、passwd、pwd、pass。这些不能靠猜,得从登录页 HTML 里读<input>的name属性。工具的配置项里通常有一栏「用户名参数」「密码参数」,填错就是全量失败,而且失败得很安静——所有请求都返回登录页,你会以为是字典太弱。

除了字段名,还有三种情况会让请求构造翻车:

第一种是两步登录。页面先 GET 一次拿 CSRF token,藏在<input type="hidden" name="csrf_token" value="...">里,POST 时必须带上,而且 token 和会话绑定。这种情况工具如果只发一次 POST,服务端直接拒绝,返回的还是登录页,判定逻辑一不留神就当成「成功」。

第二种是 Content-Type 不是表单。有些前后端分离的后台走application/json,body 是{"username":"admin","password":"123456"};还有些走multipart/form-data,字段名后面跟一串 boundary。这三种编码的请求体结构完全不同,配置时不区分,等于把字典往空气里打。

第三种是自定义请求头。很多后台强制要求X-Requested-With: XMLHttpRequest,或者校验Referer是不是本站。缺了这些头,服务端返回 403 或者一段 JSON 错误,看着像「被拦了」,其实是自己请求不完整。

我一般会先用浏览器开发者工具把一次正常登录的请求完整复制出来,逐项对照:URL、方法、Content-Type、必填头、body 结构、token 来源。这一步做扎实,后面的批量才有意义。

2.2 成功判定:只看 200 状态码是最典型的翻车

大部分人第一次写判定就是if status_code == 200: 成功。这个判断在后台登录场景里基本等于没判——登录失败页通常也是 200。更糟的是,有些系统失败时返回 200 加一段「用户名或密码错误」,成功时返回 302 跳转,你把 200 当成功,结果就是满屏命中。

比较可靠的判定是四要素叠加:

  • 状态码与跳转:成功常见 302 跳/dashboard、/index、/main,失败留在原页面返回 200。有些系统成功也返回 200,但 body 里带location.href的 JS 跳转。
  • Set-Cookie 变化:成功会下发新的会话 cookie,比如SESSIONID、token、JSESSIONID,失败时 cookie 不变或者只刷新了 CSRF。对比基线响应的 cookie 名集合,是个很硬的信号。
  • 响应体差异:成功页通常出现「退出登录」「控制台」「欢迎」这类词,失败页出现「密码错误」「验证码错误」「账号不存在」。
  • 响应长度差:最通用但最不稳的一招。长度差要配合归一化,把页面里的时间戳、随机 token、CSRF 值先替换掉再比,否则同一个失败页每次长度都不一样。

我的习惯是先打一发必然错误的口令,把这次响应做成基线指纹:状态码、跳转地址、cookie 名集合、归一化后的 body 长度和摘要。后面每一条字典命中,都跟基线指纹比,差异超过阈值才算候选。这套逻辑比硬编码关键字耐受得多,换一套系统只改配置不改代码。

2.3 万能密码为什么能过:本质不是猜中,是逻辑被绕过

万能密码这一项很多人不理解,觉得「怎么可能有个密码能登所有账号」。它不是密码,是注入。

后端如果这么写:

-- 反面示例:字符串拼接,这是万能密码能生效的根本原因 SELECT id, name FROM users WHERE name = 'admin' AND pwd = '123456';

当密码位置传入' or '1'='1,语句变成WHERE name='admin' AND pwd='' or '1'='1',后半段恒真,整个条件成立,直接返回 admin 那一行。传入admin'--则是把后面的密码校验整段注释掉。传入' or 1=1#在 MySQL 里效果类似,#是行注释。

所以万能密码命中意味着两件事:一是这个入口存在 SQL 注入面,二是这个入口的登录逻辑可以完全绕过。它的危害等级比「弱口令」高一个量级——弱口令至少还需要口令在字典里,万能密码是任何人拿到 payload 就能进。

对应的修复也很明确:所有 SQL 走参数化查询或预编译,绝不拼字符串;数据库账号给最小权限,登录查询只给SELECT且限定表;输入侧过滤是辅助手段,不能当主防线。我见过有系统加了addslashes就以为安全了,宽字节场景下照样穿透。

检测侧的做法是:把几条经典 payload 作为独立的「字典组」混进去跑,命中后单独打标签,报告里跟普通弱口令分开列,因为修复优先级完全不同。

2.4 字典决定上限:两千条足够覆盖大部分场景

字典不是越大越好。我常用的组合是三层:

第一层是默认口令。很多后台、中间件、运维面板出厂就带固定账号,比如admin/admin、admin/admin888、test/test、guest/guest。这一层命中率最高,成本最低,永远先跑。

第二层是通用弱口令 Top 1000。123456、password、qwerty、1qaz2wsx、admin123、abc123、P@ssw0rd这些。这一层覆盖的是「人懒」。

第三层是业务相关构造。企业简称加年份(abc2023、abc2024)、键盘序变形(qwer1234、1q2w3e4r)、姓名拼音加生日。这一层覆盖的是「人有习惯」,命中率不低,但需要一点对目标业务的了解。

用户名侧同理:admin、administrator、root、system、manager、operator、test、demo、guest。很多系统超管账号名是固定的,先把这几个跑一遍,比盲目扩字典有效得多。

规模上,2000 到 5000 条已经能覆盖绝大多数问题入口。再往上收益递减,而且请求量上去了,触发频率限制和账号锁定的概率直线上升,反而把有效信号淹掉。

3. 并发与限速:批量跑起来之后才会遇到的工程问题

单点能打通,不等于批量能跑完。批量阶段真正的敌人不是判定逻辑,是工程细节:并发开多少、超时怎么设、被拦了怎么收场、结果怎么落盘。这一章讲的是「跑得动」和「跑得准」之间的取舍。

3.1 并发不是越高越好:三个瓶颈卡在你前面

很多人第一次跑就把并发拉到 100,觉得机器扛得住。实际上有三个瓶颈:

本地瓶颈:线程数、文件句柄、DNS 解析。每条请求都重新解析域名的话,DNS 开销能占总耗时的三成。做法是进程内做 DNS 缓存,或者直接用 IP 加 Host 头。

目标瓶颈:小型后台可能就一台 2 核机器,20 并发就能把它打到响应时间飙到十几秒,甚至打挂。这不是危言耸听,我在内网测试环境上真把一台测试机打挂过,后来被同事念了很久。自查场景下把业务系统打挂,比漏检更严重。

中间设备瓶颈:网关、WAF、负载均衡都有频控阈值。常见的是单 IP 每分钟 60 到 300 次,超了就 429 或者直接丢连接。账号侧还有锁定策略,连续失败 5 次锁 30 分钟,这种时候你的字典再准也没用,因为账号已经被锁了。

我的起步值是并发 5 到 10,先跑 50 条观察响应时间的 P95。P95 超过 2 秒就降并发,超过 5 秒直接停。这个经验值不精确,但能避免九成的事故。

3.2 超时与重试:只对「可能成功」的失败重试

超时设置分两段:连接超时 3 到 5 秒,读超时 8 到 10 秒。连接超时短一点没关系,网络不通就是不通;读超时要留够,有些后台登录要查三次库、算一次验证码,慢的时候能到 3 秒以上。

重试策略要克制。只对这三类失败重试:连接被重置、读超时、5xx 服务端错误。重试次数最多 2 次,而且必须加退避,1 秒、3 秒这样递增。

403 和 429 绝对不能重试。这两个状态码的意思是「我知道你在干什么,我要拦你」,你重试一次,拦截阈值就往上加一档,重试十次的结果是整个 IP 段进黑名单,整批任务报废。这条是血泪经验,我在一次外部授权测试里因为脚本自动重试,把一个 /24 段全送进了对方的封禁列表,最后只能换时间窗重来。

3.3 触发风控之后怎么收场

风控触发的信号很明显:响应时间突然从 200ms 涨到 3s 以上、开始返回 429 或 403、返回的页面里出现验证码图片、连接被 RST、所有请求统一返回一个错误页。

看到这些信号,第一动作是停。停掉当前目标的所有请求,等 10 到 30 分钟,然后降并发、加请求间隔(每条之间 500ms 到 2s),再小批量试跑 20 条,看信号是否消失。

如果反复触发,说明对方的阈值比你的最低速率还低,这时候的正确做法是分批执行:把字典切成 5 段,不同时间段跑,避开业务高峰。凌晨跑听起来聪明,但如果对方有异常登录时间告警,凌晨反而更容易被标记。

还有一种情况是账号锁定。这时候继续跑只会把更多账号锁死,影响真实用户。所以自查前一定要跟运维确认锁定策略:连续失败几次锁、锁多久、能不能临时调宽。这一步不做,跑完的结果里会混进大量「本来是弱口令但被锁了没测出来」的漏报。

3.4 结果落盘:日志字段决定复核效率

跑完之后的复核工作量,取决于日志字段设计得好不好。我用的字段是这些:

字段说明用途
timestamp请求发起时间对齐风控日志
target协议 + 主机 + 路径定位入口
field_map用户名/密码字段名复现请求
username当前尝试的用户名复核
password当前尝试的口令人工确认登录
status_codeHTTP 状态码初筛
location跳转地址判定依据
body_len归一化后响应长度判定依据
elapsed耗时毫秒判断是否被限速
verdict命中 / 未命中 / 待定分级
confidence高 / 中 / 低复核优先级

两个细节值得强调。一是password字段必须记录,复核时要手工登一次确认,不记就得重跑。二是报告输出时口令要打码,只留首尾各两位,避免报告在内部流转时泄露完整凭据。

4. 复现一套自查流程:从目标清单到结果复核

前面讲的是原理,这一章给一套能直接抄的流程。前提写在最前面:只对自有系统或有书面授权的目标执行,没有授权的一律不做,这条没有例外。

4.1 第 0 步:把范围和边界写下来

动手前先产出一份清单,内容至少包含:域名或 IP 列表、每个入口的责任人、允许执行的时间窗、允许的最大并发、应急联系人和电话、明确排除的系统(生产核心库、第三方托管系统、有法律限制的系统)。

同时把自己的出口 IP 报备给运维和安全团队,说明什么时间段会有大量登录请求。这一步看着繁琐,但能避免你的自查被当成真实攻击,触发应急响应——我就见过自查跑到一半被 SOC 封号通报的乌龙。

4.2 第 1 步:单点探测,先拿基线

拿一个入口做单点探测,用必然错误的口令打一发,记录基线。下面这段用 curl 就够了:

# 单点登录探测:只发一次请求,输出状态码、跳转地址、响应长度、耗时 # --data-urlencode 会自动处理特殊字符,避免口令里的引号、空格破坏请求体 curl -s -o /tmp/baseline.html \ -w "code=%{http_code} redirect=%{redirect_url} size=%{size_download} time=%{time_total}\n" \ -X POST 'https://your-target.example/login' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -H 'X-Requested-With: XMLHttpRequest' \ --data-urlencode 'username=admin' \ --data-urlencode 'password=this-is-a-wrong-baseline-pass' \ --max-time 10

参数说明:-o把响应体落到文件,后面做指纹用;-w里的四个变量分别是状态码、跳转地址、响应体字节数、总耗时;--max-time 10是整体超时,防止卡死。跑完看一下redirect=是否为空、size=大概多少、time=是否正常。这一发就是后面所有判定的参照物。

4.3 第 2 步:归一化指纹,做差异判定

直接比长度会被随机 token 干扰,所以要先归一化。下面这段是判定核心:

import hashlib import re import requests def fingerprint(resp): """把一次响应压缩成可比对的指纹,去掉随机成分""" body = resp.text or "" # 去掉 32 位十六进制串(常见于 CSRF token、traceId) body = re.sub(r"[0-9a-fA-F]{32}", "<TOKEN>", body) # 去掉 UUID body = re.sub(r"[0-9a-fA-F-]{36}", "<UUID>", body) # 去掉时间戳,格式 2024-05-01 12:00:00 或 2024-05-01T12:00:00 body = re.sub(r"\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}:\d{2}", "<TIME>", body) # 去掉纯数字长度的随机串(部分系统会拼 6 位随机码) body = re.sub(r"\b\d{6}\b", "<RAND6>", body) return { "code": resp.status_code, "loc": resp.headers.get("Location", ""), "len": len(body), "digest": hashlib.md5(body.encode("utf-8", "ignore")).hexdigest(), "cookies": sorted({c.name for c in resp.cookies}), } def is_hit(base, cur, len_delta=30): """和基线比,判断这次是不是登录成功""" # 跳转地址变了,且不是回到登录页,是最强信号 if cur["loc"] and cur["loc"] != base["loc"] and "login" not in cur["loc"].lower(): return True, "redirect" # 出现了基线里没有的新 cookie,通常是会话凭证 if set(cur["cookies"]) - set(base["cookies"]): return True, "new_cookie" # 响应体摘要不同且长度差超过阈值,作为兜底 if cur["digest"] != base["digest"] and abs(cur["len"] - base["len"]) > len_delta: return True, "body_diff" return False, ""

逻辑说明:fingerprint负责把一次 HTTP 响应压成五个字段,关键是中间的四个正则替换,把每次都会变的随机串抹平,否则摘要永远不同。is_hit按信号强度从高到低判断,跳转优先,其次新 cookie,最后才是响应体差异。len_delta默认 30 字节,页面结构复杂的系统可以调到 50 到 100,误报会少一些但可能漏掉「成功页和失败页几乎一样」的情况。

这段代码的作用是给你一个可调的判定内核,工具里的判定无非是这套逻辑的工程化版本。自己写一遍的好处是:出问题时你知道该改哪一行。

4.4 第 3 步:批量调度,控速比控并发重要

批量阶段的核心是任务队列加限速器。思路是:字典拆成任务,工作线程从队列取,每个任务执行前先过限速器(比如每秒最多 5 个请求),执行完记录结果。伪代码层面就是线程池加令牌桶,工具里一般已经内置,你只需要把并发数和请求间隔调对。

实际配置时我会设三档:起步档并发 5、间隔 200ms;观察档并发 10、间隔 100ms;冲刺档并发 20、间隔 50ms。每一档跑 50 条看响应时间和状态码分布,稳得住才往上升,出现 429 立刻退回上一档。

4.5 第 4 步:结果复核,按置信度分级

跑完的结果不能直接进报告,要复核。我按置信度分三级:

高置信:跳转地址变化 + 出现新会话 cookie + 页面含「退出登录」字样。这一类直接手工登录一次确认,基本百分百命中。

中置信:只有跳转,或只有新 cookie。需要手工确认,有可能是「登录失败也跳转」的设计。

低置信:只有响应长度差异。这一类误报率能到一半以上,通常是页面里有广告位、随机推荐、A/B 测试导致的长度抖动。

复核的另一个作用是发现漏报:有些账号字典里没命中,但手工试admin/admin就进去了,说明字典或者字段映射有问题,要回头修配置。

5. 常见问题与排查:误报、跑空、被拦下的五个现场

这一章是我这些年攒下来的踩坑记录,按「现象 → 原因 → 解决」写。每一条都是真实遇到过的,新手对照着排查能省不少时间。

5.1 现场一:全站 100% 成功,报告没法看

现象:跑完发现所有用户名密码组合都判为命中,命中率 100%,报告没法用。

原因:判定逻辑把「登录失败页」当成了「成功页」。最常见的是登录失败也返回 302 跳回登录页,或者失败时 refresh 了一个新 cookie 名。也有一种情况是目标站点对所有未知路径都重定向到首页,跳转判定直接失效。

解决:回到基线对比。先打印基线指纹和一条命中记录的指纹,逐字段看差异在哪。如果是跳转误判,把「跳转地址包含 login」排除掉;如果是 cookie 误判,把每次都会刷新的 CSRF cookie 名加入白名单;如果目标对未知路径统一重定向,改用响应体关键字判定,别用跳转。

5.2 现场二:跑完 0 命中,但手工一试就进去了

现象:字典跑了几千条,一条没中,手工用admin/admin888登进去,秒进。

原因:字段名映射错了,或者提交方式不对。常见的是工具默认用username/password,实际系统用的是user_name/user_pwd;还有系统要求 JSON 提交,工具发的是表单,服务端直接忽略 body,每次都返回登录页,判定自然全不中。

解决:用浏览器发一次正常登录,把请求完整复制出来,逐项核对字段名、Content-Type、必填头、有没有 token 字段。核对完把配置改对,先单独跑 5 条验证,确认失败页和成功页的指纹确实不同,再上全量字典。

5.3 现场三:跑到一半全部超时,进度卡死

现象:前 200 条正常,第 201 条开始全部超时,日志里全是timeout,等十分钟也不恢复。

原因:触发了目标侧的频率限制或者连接数限制。表现是新建连接被拒、响应时间从 200ms 涨到 10s 以上、或者统一返回 429。也可能是本地出口带宽被占满,或者 DNS 解析被限流。

解决:立刻停任务,等 15 到 30 分钟。恢复后把并发降到 5、请求间隔加到 500ms,先跑 20 条试水。如果还是超时,说明 IP 已经被封,得换时间窗或者让运维临时加白。顺便检查一下本地 DNS 缓存有没有开,没开的话每次请求都解析域名,DNS 侧很容易成为瓶颈。

5.4 现场四:字典全废,因为前端把密码加密了

现象:抓包看到提交的密码字段是一串密文,长度固定,每次都不一样,字典里的明文原样发出去必然失败。

原因:前端做了 RSA 或 AES 加密,登录时用公钥加密密码再提交。这种情况下,明文字典对服务端毫无意义,服务端解密后跟库里的哈希比对,你的明文密文根本解不出来。

解决:这属于检测工具的能力边界,不是配置问题。可行的做法是分析前端加密逻辑,在请求发出前用同样的算法加密,或者直接在浏览器环境里执行登录动作。但这一步已经超出「批量自查」的范畴,需要单独评估。实操中我一般把这类入口标记为「需人工」,不纳入自动批量范围,避免浪费时间。

5.5 现场五:每条请求都失败,因为 CSRF token 每次都变

现象:单点探测成功,批量跑起来全失败,日志里状态码都是 200,但响应体是「非法请求」或「token 校验失败」。

原因:系统每次登录都要带一个和会话绑定的 CSRF token,而且 token 一次性使用。批量工具如果复用同一个 token 跑几百条,从第二条开始就全废。

解决:改成「每个会话一组请求」的模式:先 GET 登录页拿 token 和会话 cookie,POST 一次登录,然后丢弃会话,下一轮重新拿。这样请求量翻倍,速度慢一半,但正确率能到接近 100%。相应地,请求间隔要放宽,否则容易触发风控。

6. 从检测到修复:把一份结果变成排期表

跑出结果只是开始。我见过太多团队把报告往群里一发,然后没人动,半年后再扫还是那批账号。真正有价值的动作是把结果转成排期:哪些今天改,哪些本周改,哪些需要立项。

我用的分级是这样:万能密码命中排第一,这是逻辑绕过加注入面,属于当天必须处理;超管账号弱口令排第二,直接提权风险;普通账号弱口令排第三,可以按批次重置;账号枚举(不同错误提示暴露账号是否存在)排第四,属于加固项。修复动作也不复杂:强口令策略加首次登录强制改密、登录失败计数加锁定、加图形验证码或短信二次验证、所有 SQL 改参数化查询、错误提示统一成「用户名或密码错误」、登录日志接入告警对异常频率做监控。

回过头看,这件事最容易出错的地方不在技术,在心态。第一次跑出 9 个命中账号的时候,我差点直接把报告发出去,后来被导师拦下,让我逐条手工复核。结果 9 条里有 3 条是误报,都是「登录失败也跳转」造成的。如果那份报告流出去了,等于给运维团队递了一份假情报,后面所有的修复排期都是错的。

从那以后我每次跑完批量,都强制走一遍「基线对比 + 手工复核 + 置信度分级」这三步,哪怕只有十几条结果也不省略。慢一点没关系,错一次的成本高得多。希望帮到你。

本文还有配套的精品资源,点击获取