资讯动态

SNETCracker弱口令审计:范围、批量检测与复核避坑

发布时间:2026/10/26 18:28:48 来源:尧图企业网站定制
简介SNETCracker 是面向 Windows 平台的弱口令审计工具源码包适合安全运维、渗透测试学习者及需批量核查账号风险的技术人员。工具基于 C# 与 .NET Framework 4.0 开发覆盖 SSH、RDP、SMB、MySQL、FTP、Redis 等常见服务支持用户名与密码组合尝试、自定义端口和字典并可按 IP 段多线程检查自带端口扫描以减少第三方工具依赖。压缩包共 129 个文件、约 11.47MB以 62 个 cs 源码、54 个 dll 依赖及 resx、config、csproj、sln 等工程文件为主便于在 Visual Studio 中梳理项目结构与线程池实现。已有 1163 人学习下载读者可了解弱口令审计流程、多线程调度、批量目标管理和字典策略也可作为内部安全自查工具二次开发的参考样本。1. 弱口令审计不是跑一遍就完事三个前置条件决定结果有没有价值第一次拿 SNETCracker 这类弱口令检查工具做审计多数人的开局是这样的把手上所有 IP 一股脑贴进去字典挑最大的那个线程拉到顶点开始然后盯着进度条刷。跑完导出一张表几十条弱口令账号看着挺有成就感。结果这份表交上去复核环节一条都过不了——要么目标根本不在授权范围内要么同一个账号在三次尝试后就被锁定策略锁死剩下的记录全是认证失败的假阳性。工具本身没问题问题出在开局那三件事没定测谁、测什么、测到什么程度停。这篇就把这三件事拆开顺带把批量多线程、用户名密码结合检查、自定义端口和自定义字典这几个能力讲透让第一次上手的人能照着配也让已经跑过几轮的人回头看看自己的流程漏在哪。2. 划清可测范围从资产台账到端口清单的三层筛选弱口令审计最容易翻车的地方不在工具里在打开工具之前。我见过太多人拿到一份 IP 段就直接开跑跑到一半发现里面混着业务核心库、混着第三方托管的主机、混着早就下线的测试机最后报告没法交还得回头解释为什么碰了不该碰的机器。范围这件事必须做成可追溯的清单而不是脑子里的一团印象。下面两层筛选第一层决定哪些能测第二层决定测哪个门。2.1 从台账到可测目标把授权落到每一行记录上资产台账和可测目标是两回事。台账是我有什么可测目标是我这次被允许测什么、测到什么时间、用什么强度。这两者之间的差集就是审计事故的高发区。我一般会要求清单里至少有这么几列缺一列就不进队列字段作用缺失后果资产编号和目标 IP 一一绑定报告里回指报告无法定位复核时对不上号归属系统/负责人出问题第一时间能找到人触发告警后无人认领授权单据号证明本次测试有据可依被当成异常扫描处理测试窗口允许执行的时间段撞上业务高峰影响生产账号锁定策略连续失败几次锁定、锁多久批量跑直接把运维账号锁死是否允许口令尝试有些主机只允许做配置核查越界操作最后两列最容易被忽略也最容易出事。账号锁定策略这一项决定了你后面线程数和字典规模的上限——一个5 次失败锁定 30 分钟的域环境你拿 500 条字典去打第一条记录之后全是无效数据还会把正常运维挡在门外。所以台账整理阶段就要把这类信息问清楚问不到的就默认按最严格处理单账号尝试次数不超过 2 次跑完立刻停。清单整理完还要做一次反向确认把最终队列发给资产负责人过一遍让他确认这里面每一台都在授权范围内。这一步看着繁琐但它是唯一能在事后拿出来的证据。我吃过一次亏清单里有一台是某同事临时挂上来的测试机没人认领跑完第二天就被安全团队找上门。从那以后这份反向确认我一次都没省过。2.2 端口不是越多越好默认端口与自定义端口的取舍弱口令审计本质上是对认证入口做尝试所以第一步是把认证入口找出来。很多服务跑在默认端口上但生产环境里改端口的比例远比想象中高——改端口不算什么高深的安全措施但它确实能把一大批只会扫默认端口的脚本挡在门外。SNETCracker 支持自定义服务端口这个能力在真实环境里的价值比多跑几本字典还大。常见的默认端口先列一份底表作为扫描阶段的起点服务类型默认端口审计时的注意点RDP3389失败次数阈值低容易触发锁定SSH22多数有 fail2ban 类防护注意封禁SMB445域环境下可能触发账号锁定FTP21匿名登录要单独确认不算弱口令Telnet23明文传输测试前确认不影响业务MySQL3306注意 host 限制可能只允许内网SQL Server1433与 Windows 认证模式相关Oracle1521监听器与实例名分开确认PostgreSQL5432默认账号权限范围要提前确认Redis6379无认证和有密码是两种状态MongoDB27017老版本默认无认证VNC5900部分实现无锁定机制风险高这张表只是起点不是终点。真正要做的是端口开放和服务指纹两步走先做端口扫描拿到开放列表再对每个开放端口做一次协议识别确认它跑的到底是不是你以为的那个服务。跳过指纹识别直接按端口号去尝试是误报的主要来源之一——把 3389 上的一个自定义 Web 服务当成 RDP 去打跑出来的失败毫无意义跑出来的成功更可怕说明工具把返回内容误判成了认证通过。自定义端口的写法上有个细节如果目标服务改到了非常规端口要在清单里显式标注服务类型 端口而不是只给一个端口号。因为同一个端口号在不同主机上可能对应完全不同的服务只给端口号后续配置阶段就得靠猜。我习惯在清单里多加一列协议假设跑之前先小样本验证 3 到 5 台确认假设成立再全量铺开。还有一类目标要单独拎出来只对内网开放、但通过某种方式暴露到更大范围的管理后台。这类入口往往是最该审计的也是最容易在台账里被漏掉的因为它们不在标准的资产扫描结果里。整理阶段多问一句有没有对外映射的管理端口往往能捞出几条真正有价值的目标。3. 工具能力拆开看多线程、用户名密码组合、自定义字典各自解决什么问题SNETCracker 在 Windows 平台上跑核心能力是三块批量多线程、密码与用户名结合检查、自定义端口和字典。很多人把这三块当成参数越多越强实际上它们各自对应一个具体问题配错了不但不提效还会制造假数据。这一章把三块拆开讲清楚每块在什么场景下该开到什么程度。3.1 多线程批量的收益曲线不是线程越高越快多线程的价值在于把等待网络往返的时间重叠起来。单线程跑一个目标绝大部分时间花在建立连接、等待服务响应上CPU 几乎是闲的开到 20 线程吞吐量能上去一个数量级。但这条曲线很快就平了再往上加收益递减得非常快原因有三个第一是目标侧的限制。很多服务对并发连接数有上限超过之后新连接直接被拒工具记录下来就是连接失败你得回头看日志才知道是被限流了不是口令错了。第二是中间设备的限制。防火墙、入侵防御这类设备对短时间内的大量认证请求有阈值超了就把源 IP 拉黑后面的目标全部连不上一轮跑下来有效数据可能不到三成。第三是账号锁定。并发高意味着同一账号在极短时间内收到多次尝试锁定策略一触发这个账号在锁定窗口内就彻底没法测了。我的经验值是面向单台主机、账号数量少的场景10 到 20 线程足够面向大量主机、每台只做少量尝试的横向批量场景可以开到 50 左右但要配合每台主机串行、主机之间并行的策略。这个策略的意思是同一台机器上的多次尝试排队走不同机器之间并行跑这样既压住了单机的失败频率又吃到了批量的吞吐。很多人反过来配——单机高并发、多机串行——正好踩在锁定策略和限流的两条红线上。判断线程数合不合适有个很土但管用的办法先拿 5 台目标试跑看三个指标——连接失败率、平均响应时间、有效认证响应占比。连接失败率超过 10%说明线程或频率超了平均响应时间随线程数上升明显变长说明对端在排队有效认证响应占比低于 80%说明大量请求根本没到认证环节。这三个指标任意一个不对先把线程砍一半再跑。3.2 用户名和密码结合检查命中率差距就出在这一步只跑密码字典的做法命中率低得可怜。原因很简单绝大多数弱口令不是随机撞上的而是有规律的——用户名和密码相同、用户名加年份、用户名加 123、用户名首字母加生日。这类规律用纯密码字典覆盖不到但用用户名 密码组合的方式生成候选命中率会高出一个量级。这也是 SNETCracker 强调密码支持和用户名结合进行检查的实际意义。组合规则我一般分四层来做从代价低到代价高第一层是恒等变形用户名本身当密码加上常见的后缀 123、1234、123456、!、、#。第二层是大小写和分隔符变体首字母大写、全大写、加下划线或点。第三层是组织特征词拼接把系统名、部门缩写、年份拼进去这一层需要提前收集信息命中率最高但也最费工夫。第四层才是通用弱口令字典兜底。用 Python 生成第一、二层候选的脚本大概长这样参数都能改# 根据用户名列表生成组合候选输出一行一个 用户名:密码 # 用法python gen_combo.py users.txt combo.txt import sys SUFFIXES [, 123, 1234, 123456, 12345678, !, , #, 2023, 2024] PREFIXES [, !] def variants(name): 生成单个用户名的常见弱口令变形 base name.strip() if not base: return seeds {base, base.lower(), base.upper(), base.capitalize()} for s in list(seeds): # 首字母大写 常见后缀 for suf in SUFFIXES: yield s suf yield s suf[::-1] if suf.isdigit() else s suf # 组织特征词拼接 for s in seeds: for org in (admin, test, dev, ops): yield org s yield s org def main(): with open(sys.argv[1], encodingutf-8) as f: for line in f: for cand in variants(line): if 6 len(cand) 20: # 过滤掉过短过长的 print(f{line.strip()}:{cand}) if __name__ __main__: main()这段脚本的作用是把用户名扩展成用户名 候选密码的成对列表。SUFFIXES和PREFIXES是要按目标环境改的比如目标系统有明确的上线年份就把年份加进去如果是内部系统组织缩写那一组权重最高。6 len(cand) 20这个长度过滤是按常见密码策略设的实际环境里如果最短长度要求是 8就改成 8能省掉一大半无效尝试。脚本输出的格式是用户名:密码正好对应工具里用户名密码成对输入的场景省去手工拼接。这里要提醒一句组合生成出来的候选数量会膨胀得很快。100 个用户名按上面的规则大概能生成几千条再叠加字典就是几万条。所以生成完必须先算一遍总量再和单账号允许尝试次数 × 账号数对一下超了就砍规则别硬上。3.3 字典裁剪体积和时间之间要算一笔账通用弱口令字典动辄几十万条直接丢进去跑一台机器可能要跑几个小时。真实审计里时间是有限的所以字典要分层配第一层Top 200 到 500 的高频弱口令覆盖绝大多数admin/adminroot/123456这类第二层按服务类型定制的字典数据库类和 Web 后台类的常见弱口令分布不一样第三层组织特征词来自第二步收集的信息第四层才是通用大字典兜底。前两层跑完命中率通常能到七成以上后面两层是补漏。字典文件本身还有两个工程细节。一是编码Windows 平台上工具读字典时如果编码对不上中文或者特殊字符会变成乱码表现为明明字典里有这条就是不命中。统一存成 UTF-8 无 BOM 是稳妥做法改完拿记事本另存一次确认。二是空行和行尾空格字典里混进空行会导致工具尝试空密码这在某些服务上会被记成一次异常登录日志里很难看。跑之前用一条命令清一遍# 去掉空行、行尾空格和 Windows 换行符输出干净字典 sed -e s/\r$// -e s/[[:space:]]*$// raw.txt | grep -v ^$ | sort -u clean.txtsed的两条替换分别处理回车符和行尾空格grep -v ^$去掉空行sort -u去重。去重这一步能砍掉不少体积尤其是多个来源合并的字典。4. 一次可复现的审计流程从配置到可交付报告前面讲的是想清楚这一章讲跑明白。一次能复现的审计从头到尾要留下四样东西目标清单、配置快照、原始结果、复核记录。缺任何一样这份结果都没法被别人重跑也就没法被信任。4.1 配置阶段先小样本试跑再全量铺开配置阶段的顺序我固定成这样导入目标清单 → 按服务类型分组 → 每组挑 3 到 5 台做试跑 → 核对试跑结果 → 调整参数 → 全量执行。分组这一步很关键。不同服务的认证行为差异很大同一个线程数在 SSH 上没问题在 RDP 上可能直接触发锁定。所以线程数、超时时间、重试次数这些参数要按服务类型分开设不能一套参数打天下。试跑阶段要看的东西比结果本身更重要认证失败的返回内容对不对、连接超时比例高不高、有没有出现连接成功但认证阶段无响应的情况。最后这种最麻烦说明协议握手有问题可能是端口对了但服务类型判断错了也可能是目标有前置的认证网关。试跑不通过就调参数别急着全量。配置快照我一般存成一个文本文件内容包括工具版本、目标清单文件名、字典文件名和条数、线程数、超时时间、重试次数、执行时间窗口。这份东西在报告附录里放一页后面谁来复核都能照着复现。4.2 执行与监控中断了怎么接上全量执行阶段最怕两件事跑一半断了和跑完了发现结果被覆盖。断的情况很常见网络抖动、目标重启、工具卡死都有可能。所以执行前要把目标按批次切开比如每 50 台一批一批跑完立刻导出结果再跑下一批。这样即使中途断了已完成的部分不会丢。SNETCracker 支持批量导入分批导入就行不需要什么特殊操作。结果被覆盖的问题出在导出命名上。默认文件名重复第二次导出就把第一次的覆盖了而很多人是跑完才想起来检查。我的做法是导出文件名里带上批次号和时间戳比如result_batch03_20240612_1430.csv跑完统一放一个目录最后合并。合并和去重用一个短脚本就能搞定比手工粘贴可靠得多# 合并多批结果并去重保留每条记录首次出现的时间 import csv, glob, sys seen {} for path in sorted(glob.glob(result_batch*.csv)): with open(path, encodingutf-8-sig) as f: for row in csv.DictReader(f): # 以 目标账号端口 作为唯一键防止同一账号被重复统计 key (row[host], row[username], row[port]) if key not in seen: seen[key] row with open(merged.csv, w, newline, encodingutf-8-sig) as f: w csv.DictWriter(f, fieldnamesnext(iter(seen.values())).keys()) w.writeheader() w.writerows(seen.values())utf-8-sig是为了兼容 Excel 打开时的中文显示DictReader按表头读列唯一键取主机、账号、端口三元组。如果结果表里没有端口列就把 key 改成主机加账号。去重之后的数量才是真实命中数合并前的总数没有意义因为同一账号可能在多批里都被记录。监控方面执行期间要盯的是失败率曲线不是进度条。失败率突然抬升通常是触发了目标侧的限流或封禁这时候应该暂停而不是加速。我一般会把批次间隔设成 5 到 10 分钟让对端的防护机制有时间复位。4.3 结果复核与报告产出每条记录都要能验证跑出来的结果不能直接进报告必须抽检复核。抽检比例我按命中数定少于 20 条全查20 到 100 条抽 20%超过 100 条抽 10% 且不少于 20 条。复核的方式是拿单条记录去独立验证一次确认这个账号确实能用这个密码登录而不是工具的误判。复核用一段小脚本比手工点更靠谱比如验证一个 SSH 账号# 单账号复核确认该账号确实能用该密码登录仅限授权范围内使用 import paramiko def verify(host, port, user, pwd, timeout8): client paramiko.SSHClient() client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) try: client.connect(host, portport, usernameuser, passwordpwd, timeouttimeout, allow_agentFalse, look_for_keysFalse) return True except paramiko.AuthenticationException: return False # 认证失败说明是误报或密码已变更 except Exception as e: return fERROR: {e} # 连接问题不代表账号无效 finally: client.close()这段的关键是把认证失败和连接异常分开处理。复核时最怕把连接超时当成账号无效直接划掉结果把真实命中漏了。allow_agentFalse, look_for_keysFalse是为了强制走密码认证避免脚本误用本机的密钥登录成功产生假阳性。报告结构我固定四段范围与授权、方法与参数、命中清单含账号、服务、端口、验证状态、修复建议。命中清单里每一条都要标已验证/待验证没验证的不能写成结论。修复建议按账号类型分开写——运维账号、应用账号、设备默认账号的处理方式不一样混在一起写接收方没法执行。5. 排查与避坑五类让结果失真的常见问题跑出来的数据不对劲八成不是工具的问题是配置或者环境的问题。下面这几条是我这些年反复遇到的按现象 → 原因 → 解决整理遇到对不上的先对照排查。5.1 认证侧的三类坑账号被锁结果全废。现象是第一批跑完后后续批次的失败率突然变成 100%日志里全是认证拒绝。原因通常是没确认目标的锁定策略或者确认了但线程开太高同一账号在几秒内被尝试了十几次。解决办法是执行前把单账号尝试次数压到锁定阈值的三分之一以下并把同账号的尝试串行化已经锁了的等锁定窗口过去再跑或者联系管理员临时解锁别硬试。服务指纹不匹配跑了个寂寞。现象是某个端口的失败率异常高且失败返回内容和其它同类型目标不一样。原因是端口上跑的不是你以为的服务比如 3389 上挂了个远程管理网关协议握手能过但认证流程完全不同。解决办法是先做一次协议识别确认服务类型再配置识别不出来的目标单独拎出来用单条手工验证别混在批量里。字典编码不对明明有就是不中。现象是字典里肉眼可见的弱口令没被命中而同类目标在别的机器上命中了。原因是字典文件编码不是 UTF-8或者混了 BOM 头、Windows 换行符。解决办法是统一转成 UTF-8 无 BOM、LF 换行跑之前用前面那条sed命令清一遍并且抽样确认头几行内容正常。5.2 网络与并发侧的两类坑网络抖动被记成认证失败。现象是同一批目标里某几台的失败率明显偏高重跑一次结果又不一样。原因是超时时间设得太短跨机房或者链路上有丢包时认证请求还没返回就被判超时。解决办法是把超时时间按链路质量分档跨区域的目标单独设一档更长的超时并且开启重试——但重试次数要计入单账号尝试次数别把锁定阈值算漏了。并发冲高把源 IP 拉黑。现象是跑到中途所有目标都连不上本地网络正常但目标侧完全无响应。原因是短时间内的认证请求触发了中间设备的阈值。解决办法是降并发、加批次间隔跑之前和网络团队打个招呼把测试源加进观察名单别让防护设备把审计流量当成攻击处理。这条也是最容易在事后解释不清的所以执行前的沟通记录一定要留。6. 收在一个习惯上把一次审计变成可重复的回归项弱口令这件事有个特点修完不等于修完。今天改掉的密码三个月后可能因为人员变动、系统上线、临时账号又退回去了。所以单次审计的价值有限真正有价值的是把它变成一个有固定节奏的回归动作。我现在的做法是把整条链路固化成四份东西缺一份就不算完成一份稳定的目标台账带授权和锁定策略、一套按服务分层的字典和组合规则、一份固定的参数模板按服务类型分组、一份可复现的结果合并与复核脚本。这四份东西版本化管理每次跑只改台账和批次号其余不动。这样两次审计之间的结果才有可比性——你能说清楚这次比上次少了 12 个弱口令而不是这次扫出来 30 个上次好像 40 多个吧。这里有个小技巧值得单独说把每次的命中清单和上一次做差集只看新增和未修复两类。新增的往往对应最近上线的系统或新开的账号未修复的则是推动不力的部分这两类的处理优先级完全不同。差集用脚本做几行代码的事比人工比对可靠。# 对比两次审计结果输出新增命中与未修复命中 import csv def load(path): with open(path, encodingutf-8-sig) as f: return {(r[host], r[username]) for r in csv.DictReader(f)} old load(audit_202403.csv) new load(audit_202406.csv) print(新增上次没有、这次有) for item in sorted(new - old): print( , item) print(未修复两次都有) for item in sorted(new old): print( , item)唯一键取主机加账号输出的两类正好对应推动新增系统整改和追旧账两个动作。这段脚本每次审计后跑一遍五分钟的事但能让整改推动有据可依。回过头看我踩过最贵的一个坑不是参数配错是没留配置快照。有一次跑完发现命中数比预期高很多想复现却怎么也想不起当时的线程数和字典版本最后只能全部重跑浪费了一整天。从那以后我每次开跑前都强制走一遍台账双签 配置快照 小样本试跑这三步一步不省。工具再强也强不过流程上的那点笨功夫。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑