
简介面向网络安全从业者与渗透测试学习者的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 typehidden namecsrf_token value...里POST 时必须带上而且 token 和会话绑定。这种情况工具如果只发一次 POST服务端直接拒绝返回的还是登录页判定逻辑一不留神就当成「成功」。第二种是 Content-Type 不是表单。有些前后端分离的后台走application/jsonbody 是{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 11语句变成WHERE nameadmin AND pwd or 11后半段恒真整个条件成立直接返回 admin 那一行。传入admin--则是把后面的密码校验整段注释掉。传入 or 11#在 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、Pssw0rd这些。这一层覆盖的是「人懒」。第三层是业务相关构造。企业简称加年份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 usernameadmin \ --data-urlencode passwordthis-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_delta30): 和基线比判断这次是不是登录成功 # 跳转地址变了且不是回到登录页是最强信号 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 和会话 cookiePOST 一次登录然后丢弃会话下一轮重新拿。这样请求量翻倍速度慢一半但正确率能到接近 100%。相应地请求间隔要放宽否则容易触发风控。6. 从检测到修复把一份结果变成排期表跑出结果只是开始。我见过太多团队把报告往群里一发然后没人动半年后再扫还是那批账号。真正有价值的动作是把结果转成排期哪些今天改哪些本周改哪些需要立项。我用的分级是这样万能密码命中排第一这是逻辑绕过加注入面属于当天必须处理超管账号弱口令排第二直接提权风险普通账号弱口令排第三可以按批次重置账号枚举不同错误提示暴露账号是否存在排第四属于加固项。修复动作也不复杂强口令策略加首次登录强制改密、登录失败计数加锁定、加图形验证码或短信二次验证、所有 SQL 改参数化查询、错误提示统一成「用户名或密码错误」、登录日志接入告警对异常频率做监控。回过头看这件事最容易出错的地方不在技术在心态。第一次跑出 9 个命中账号的时候我差点直接把报告发出去后来被导师拦下让我逐条手工复核。结果 9 条里有 3 条是误报都是「登录失败也跳转」造成的。如果那份报告流出去了等于给运维团队递了一份假情报后面所有的修复排期都是错的。从那以后我每次跑完批量都强制走一遍「基线对比 手工复核 置信度分级」这三步哪怕只有十几条结果也不省略。慢一点没关系错一次的成本高得多。希望帮到你。本文还有配套的精品资源点击获取