SNETCracker弱口令审计实战:组合字典、多线程与整改清单

发布时间:2026/10/26 18:28:38
SNETCracker弱口令审计实战:组合字典、多线程与整改清单

简介:SNETCracker 是面向 Windows 平台的弱口令审计与安全检查工具,适合安全测试人员、运维管理员及学习者批量排查 SSH、RDP、SMB、MySQL、SQL Server、Oracle、FTP、MongoDB、Memcached、PostgreSQL、Telnet、SMTP、VNC、Redis 等常见服务中的弱密码账号。压缩包共 129 个文件,以 62 个 C# 源码和 54 个 DLL 依赖库为主,另含 resx 资源、ico 图标、sln 解决方案及 csproj 工程配置,整体约 11.47MB,可在 .NET Framework 4.0 环境下编译和二次开发。已有 1163 人学习下载。工具支持多线程批量检查、IP 地址或网段导入、自带端口扫描、用户名与密码组合尝试、自定义字典和端口,源码中可进一步查看线程池调度、任务队列、主界面交互与配置读取等实现。读者可据此理解弱口令检测流程,搭建授权范围内的口令审计环境,并学习 Windows 桌面安全工具的工程组织方式;对学习 C# 桌面应用、协议口令尝试和线程池并发模型也有参考意义,完整项目结构便于对照调试、替换字典及扩展新的服务检查模块。

1. 弱口令审计:别在登录框里一个个手敲了

季度自查的时候,某公司内网拉出来一份账号表,六百多个账号,分散在文件共享、数据库、中间件、网络设备后台。手工拿「123456」「admin」「用户名+123」在登录框里一个个试,一天下来眼睛花,还漏掉一半。SNETCracker 就是冲这个场景来的:Windows 平台上的弱口令审计工具,批量、多线程,能把用户名和密码拼成组合去跑,支持自定义服务端口和字典。它不负责入侵,负责在你自己的授权范围内,把「谁还在用弱密码」这件事变成一份能复核的清单。适合做内网自查的运维、做整改验收的安全工程师,以及需要交报告的人。

2. 拆开看它的工作模型:协议、线程、字典三条线

弱口令检查工具看着像「填个 IP、挂个字典、点开始」,但真正决定跑得准不准的,是三件互相关联的事:撞哪个服务、并发怎么排、字典长什么样。这三条线任何一条出问题,结果都会呈现为「全部失败」或「一个都没撞出来」,而这两种现象背后往往是完全不同的原因。

2.1 协议与端口:先决定撞哪扇门

工具的第一层是服务识别。同一个 IP 上可能开着文件共享、数据库、远程终端,不同服务的认证报文格式、失败返回、锁定策略都不一样。检查器必须按服务类型分别发包,否则就是把 MySQL 的握手包甩给 SMB,只会收获一堆超时。

常见做法是覆盖这几类高频入口,端口只是默认值,实际环境里被改过的非常多:

服务类型默认端口常见改端口场景检查时的注意点
Windows 文件共享445很少改,但常被策略限制需要管理员权限运行;域账号要带域前缀
SQL Server1433多实例部署时会改实例名和端口要对上,否则连不上
MySQL3306几乎都会改注意是否限制来源 IP
FTP21常改到 2000 以上匿名登录和普通账号要分开看
Telnet23老设备常见明文协议,失败返回不标准
SSH22常改有失败次数限制,容易触发封禁

这张表的价值不在端口号本身,而在于提醒一件事:目标清单里必须写清楚「IP + 端口 + 服务类型」三要素。只给 IP,工具只能按默认端口猜;给了端口但服务类型选错,握手阶段就失败,后面的字典再精准也没用。我一般会先做一轮端口探测,把活着的端口和服务版本确认一遍,再决定这批目标分给哪个协议去跑。探测和爆破要分开做,混在一起跑,日志会乱得没法看。

还有一个容易忽略的点是「同一 IP 多服务」。一台机器上同时开 445 和 1433 很常见,如果目标清单里只写了一行,就只会检查其中一个。清单格式上,我倾向于一行一个「IP,端口,协议」,宁可多写几行,也不要在工具里靠猜。

2.2 多线程:并发上去之后,麻烦也跟着上去

多线程是这类工具的核心卖点,也是翻车最集中的地方。它的基本模型是:所有「目标 × 账号 × 密码」的组合进一个任务队列,若干个工作线程从队列里取任务、发包、写结果,主线程负责汇总和落盘。线程数决定单位时间发出多少请求,也决定你对目标系统造成的压力。

这里有个反直觉的结论:线程数不是性能指标,而是风险指标。调高线程,撞出率不会线性提升,但触发账号锁定、被边界设备限速、把目标服务打挂的概率是实打实上升的。尤其是 Windows 域账号,默认策略下连续几次失败就会锁定,锁了之后要等管理员解锁,自查阶段把业务账号锁掉,性质就不一样了。

实际配置时我会按目标规模分区:几十个目标,5 到 10 个线程足够;几百个目标,10 到 20 个线程;上千个目标才考虑 30 以上,而且要分批跑。更重要的是,同一批任务里不要把「对锁定敏感的服务」和「不敏感的服务」混在一起,前者的线程数要单独压低。

另外,线程数还要和超时配合。线程开得大、超时设得长,队列里会堆一堆卡住的连接,进度条走得慢,你以为是字典太大,其实是线程全被慢目标占着。后面第 4 章会单独说这个组合怎么调。

2.3 「用户名 + 密码」组合字典:成功率真正的来源

纯密码字典的逻辑是「拿一份常见密码挨个试所有账号」,命中率取决于目标群体的密码习惯。组合字典的逻辑不一样:它把用户名本身当成密码素材,生成用户名、用户名+123、用户名@2024、用户名+年份、首字母大写变体这一类候选。企业环境里「用户名就是密码」「用户名加个数字」的占比高得离谱,这一层往往比几万条通用字典更有效。

工具本身支持这种结合方式,但具体生成什么变体,得自己按目标环境的命名规则来。我一般用脚本先生成,再人工扫一眼,去掉明显不合规的组合:

# 根据用户名清单生成组合密码字典 # 输入:users.txt,一行一个用户名 # 输出:combo_dict.txt,一行一个候选密码,已去重 $users = Get-Content -Path .\users.txt -Encoding UTF8 | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne "" } | Select-Object -Unique $years = @("2023","2024","2025") $suffix = @("123","1234","12345","123456","@123","@1234","@2024","!@#","888","666") $out = New-Object System.Collections.Generic.HashSet[string] foreach ($u in $users) { $lower = $u.ToLower() $cap = (Get-Culture).TextInfo.ToTitleCase($lower) [void]$out.Add($lower) [void]$out.Add($cap) [void]$out.Add("$lower$lower") [void]$out.Add("$lower@123") foreach ($s in $suffix) { [void]$out.Add("$lower$s") [void]$out.Add("$cap$s") } foreach ($y in $years) { [void]$out.Add("$lower$y") [void]$out.Add("$lower@$y") } } $out | Set-Content -Path .\combo_dict.txt -Encoding UTF8 Write-Host ("生成候选密码 {0} 条" -f $out.Count)

这段脚本做四件事:读用户名、按大小写和常见后缀做笛卡尔积、用 HashSet 去重、按 UTF-8 落盘。$suffix和$years是主要调参点,年份要按目标环境的实际时间窗调整,加太多会明显撑大字典体积。Get-Culture).TextInfo.ToTitleCase处理首字母大写,对中文用户名基本无影响,可以忽略。

字典文件本身有三个硬要求:一行一个、UTF-8 不带 BOM、没有空行。带 BOM 的文件在部分工具里会把第一条密码读成乱码,表现为「第一条永远失败」,很容易被误判成目标问题。生成完我习惯用Get-Content .\combo_dict.txt | Measure-Object -Line对一下行数,再Select-Object -First 5抽头几条看格式。

3. 从解压到第一份结果:完整跑一遍

工具是 Windows 桌面程序,没有复杂的安装过程,但「能打开」和「能跑出正确结果」之间隔着配置这一层。这一章按实际顺序走一遍,每一步都对应一个可能出问题的地方。

3.1 运行环境与目录结构

系统层面,Windows 10、Windows 11 或者 Windows Server 都能跑,常见依赖是 .NET Framework 4.x,缺了会直接弹框报错,按提示装即可。必须用管理员权限启动,原因是检查 SMB 共享和部分系统服务时要用到管理员令牌,普通权限下连接会被直接拒绝,表现出来就是「目标全部不可达」。

目录上,这类工具通常会把主程序、配置、字典、结果分开存放。拿到包之后先别急着点主程序,花两分钟看清目录:

  • 主程序目录:可执行文件和依赖库,不要单独把 exe 拷到别处
  • 配置目录:保存上一次的任务参数、目标列表、协议选项,排查问题时最先看这里
  • 字典目录:内置的常见密码字典,可以替换但不要删
  • 结果目录:导出的成功记录和日志

第一件事是确认程序能正常启动,第二件事是把配置目录清一遍或备份,避免上一份任务的残留参数影响这次结果。这个动作听着琐碎,但「跑完发现扫的是上周的 IP 段」这种事,我见过不止一次。

3.2 目标清单:先探测再导入

不建议直接把一整个 B 段扔进去。先做端口探测,把活着的服务和端口确认下来,再生成导入清单。PowerShell 自带的Test-NetConnection够用:

# 批量探测目标端口存活,输出可导入的清单 # targets.txt:一行一个 IP 或 CIDR,如 192.168.10.0/24 # 输出:alive_targets.csv(IP,Port,Service) $ports = @{ 445 = "SMB" 1433 = "MSSQL" 3306 = "MySQL" 21 = "FTP" 22 = "SSH" 23 = "Telnet" } $result = foreach ($line in Get-Content .\targets.txt) { $line = $line.Trim() if ($line -eq "") { continue } foreach ($p in $ports.Keys) { $t = Test-NetConnection -ComputerName $line -Port $p ` -InformationLevel Quiet -WarningAction SilentlyContinue if ($t) { [pscustomobject]@{ IP = $line Port = $p Service = $ports[$p] } } } } $result | Sort-Object IP, Port | Export-Csv .\alive_targets.csv -NoTypeInformation -Encoding UTF8

逻辑很直白:对每个目标逐个端口试连,通不过就跳过,通过的写进 CSV。-InformationLevel Quiet让它只返回布尔值,避免刷屏。-WarningAction SilentlyContinue压掉超时告警。这几个参数里,唯一需要按环境调的是端口集合:如果目标环境把 MySQL 改到了 33060,$ports里就要补上,否则探测阶段就把真实目标漏掉了,后面检查再准也没用。

探测阶段建议串行或低并发,它对目标的影响比检查阶段小得多,但也不能开着几十个并发去打。

3.3 字典挂载与任务参数

目标清单导入后,进入参数配置。这一屏的选项通常包括协议类型、线程数、超时、重试次数、是否启用「用户名+密码」组合模式、字典文件路径。

字典挂载顺序有讲究:先挂小的高频字典(比如 top 100 常见密码),再挂组合字典,最后挂大体积的全量字典。原因在于结果落盘和人工复核——先命中的往往是管理最松的账号,早点看到结果,能及时判断这批目标值不值得继续跑。

如果工具支持多字典串联,直接把三个文件按顺序加进去,不要手动合并成一个几十万行的巨无霸。合并之后一旦结果异常,你没法判断是哪一层字典出的问题,排查成本会高很多。

参数上还有两个细节:一是「密码为空」这个选项,很多环境里确实存在空密码账号,但这个选项一旦打开,误报也会增加,建议单独跑一轮,不要和正常字典混在一起;二是「是否尝试锁定前停止」,如果工具有这个能力就打开,没有的话就靠线程数控制。

3.4 启动、观察与结果导出

点开始之后不要走开。前三十秒最关键,观察三件事:有没有命中、失败原因分布、进度是否在推进。

如果有命中,先抽一条手工验证,确认真实可登录,再继续跑。这一步是为了排除误报——某些服务的失败返回不标准,工具可能把超时或协议错误判成成功。如果全部失败,先看失败原因:是连接超时、认证拒绝,还是协议不匹配。这三种对应完全不同的处理方式,混在一起看只会越看越乱。

结果导出建议保留原始格式,同时另存一份 CSV。原始格式里通常带有时间戳、协议、端口,这些字段在做整改清单时都要用到,丢了就得重跑。

4. 三个旋钮怎么拧:线程、超时、重试

参数配置这一屏看着简单,实际上三个数字的组合决定了这次检查是「半小时出结果」还是「跑一整晚还锁了一批账号」。这三个旋钮之间是有耦合的,单独调其中一个,经常按下葫芦浮起瓢。

4.1 线程数:按目标规模和锁定策略分区

线程数的经验值可以参考这张表,但一定要结合目标系统的锁定策略做二次修正:

目标规模建议线程数适用场景风险提示
1 到 503 到 5单台设备、少量数据库基本无风险
50 到 3008 到 15部门级内网自查注意域账号锁定
300 到 100015 到 25全公司网段需分批,避免持续高压
1000 以上25 到 40大型环境,需授权建议先抽样验证策略

判断依据不是目标数量本身,而是「目标是否共享同一套认证体系」。如果几百个目标都是同一个域里的机器,失败次数是累计的,线程数要按域策略往下压,而不是按 IP 数量往上加。反过来,如果目标分散在不同系统、各自独立认证,并发可以放开一些。

另一个判断点是网络链路。跨网段、跨机房的目标,单次连接耗时会长很多,这时候线程开大反而会让队列积压,因为线程都在等超时。我一般的做法是先在 5 个目标上试跑,观察单任务平均耗时,再决定全量的线程数。

4.2 超时与重试:宁可少撞,不要假失败

超时太短,正常的慢链路会被判成失败,结果是「明明密码对却没撞出来」;超时太长,一个失联目标就能占住线程几分钟,整体进度被拖死。内网环境一般 3 到 5 秒够用,跨网段可以放到 8 到 10 秒。

重试次数建议设成 1 次,不要更多。重试的目的是区分「偶发网络抖动」和「真的失败」,但每多一次重试,对目标系统的认证请求就多一次,锁定风险同步放大。如果某个目标的重试失败率特别高,那不是重试次数的问题,是链路或者端口的问题,应该单独拎出来排查。

还有一种情况是「连接建立成功但认证阶段超时」,这类目标往往是服务负载已经很高,再压上去只会更糟。看到这种目标,正确的动作是把它从这批任务里摘出来,换个时间段单独跑。

4.3 字典顺序:分层跑比一次性跑完更划算

字典体积和运行时间基本成正比,但命中分布是高度不均的。常见密码的前几百条能覆盖大部分弱口令,剩下的几十万条边际收益递减得很快。

我的习惯是分三轮:第一轮只跑 top 200 常见密码加空密码,几分钟出结果,先摸清目标的整体水位;第二轮跑组合字典,针对第一轮没命中的账号;第三轮才是全量字典,而且只在前两轮结果不理想、且时间允许的情况下跑。

这样做的另一个好处是日志清晰。三轮分开跑,每一轮的命中都能对应到具体的字典策略,写报告时可以直接说「组合字典贡献了 X 个命中」,比笼统地说「跑了一遍字典」有说服力得多。

组合字典的体积要控制。按第 2 章那个脚本,每个用户名生成几十条候选,一千个账号就是几万条,配合目标数量乘起来是百万级的任务量。跑之前先算一下「目标数 × 账号数 × 字典条数」,超过千万级就要考虑分批,不然线程队列会堆到你看不出进度。

5. 避坑与排查:五条踩过的坑

5.1 现象:跑完显示全部失败,但手工登录是通的

现象:检查结果显示所有目标认证失败,随便抽一台手工用同样的账号密码能登录。

原因:最常见的是协议或端口选错。目标把 1433 改成了 14330,工具还按默认端口发,连握手都完成不了,自然全是失败。第二常见的是权限问题,SMB 检查没用管理员权限启动,连接在协商阶段就被拒。第三是域账号写法不对,工具里填了administrator,实际环境需要DOMAIN\administrator或者administrator@domain这种格式。

解决:先看失败原因是「连接超时」还是「认证拒绝」。超时基本都是端口或网络问题,用Test-NetConnection逐个确认端口;认证拒绝才是账号密码问题。域账号统一加域前缀再跑,别指望工具自动补。权限问题最简单,右键以管理员身份运行。

5.2 现象:跑完之后有一批账号登不上了

现象:检查结束,业务方反馈某几个账号被锁,需要管理员解锁。

原因:线程数开太大,或者对同一批账号连续试了太多密码,触发了域策略的失败次数阈值。很多环境的阈值是 5 次,线程一开大,几秒钟内就能把阈值打满。还有一种情况是和多轮字典叠加有关,三轮字典连跑,每轮都从第一条开始试,失败次数是累加的。

解决:跑之前先问清楚锁定策略,把线程数压到安全区。对同一批账号,字典条数也要控制,宁可分多次跑,不要一次把几十万条全压上去。已经锁了的账号走正常解锁流程,别用工具反复试,越试锁得越久。

5.3 现象:进度条卡在某个数字不动

现象:任务跑到一半,进度长时间不推进,但程序没崩。

原因:线程被少数几个慢目标占满了。这些目标要么已经失联但连接没断,要么响应极慢,每个线程都在等超时。工具本身没有动态剔除机制的话,队列就会卡住。另一种可能是 DNS 反解,某些工具在记录结果时会尝试反查主机名,反查超时也会拖慢整体。

解决:把超时调短,先让卡住的任务失败退出;如果工具支持,关掉 DNS 反解选项;实在不行就暂停、导出已完成部分的结果、把剩余目标重新分批跑。卡住的目标单独拉出来手工确认,大概率是网络策略或者服务异常,不是密码问题。

5.4 现象:同一批目标两次跑出来的结果不一样

现象:今天跑命中 20 个,明天同样的配置跑命中 17 个。

原因:三次差异来源。一是网络抖动导致的偶发失败,被当成了「不命中」;二是目标侧有登录频率限制或者临时封禁,第二次跑的时候部分 IP 还在限制期;三是字典顺序或线程数变了,导致试到某个账号时正好触发了锁定。

解决:复核阶段不要只看命中数,要看失败原因分布。差异大的目标单独重跑一次,用最低线程、最长超时,确认是稳定不命中还是偶发失败。报告里对不确定的条目要标注,别把偶发失败写成「安全」。

5.5 现象:导出的结果没法直接用来整改

现象:结果里只有 IP 和命中的密码,没有协议、端口、时间,整改时对不上号。

原因:导出时只选了精简字段,或者工具默认导出格式就不带这些信息。多协议环境里,同一个 IP 上 445 和 1433 是两个不同的风险点,合并成一行就丢了信息。

解决:导出时选全字段,至少保留 IP、端口、协议、账号、密码、发现时间。如果工具不支持,就在跑之前把目标按协议分组,分组导出,文件名里带协议标识。这一步多做十分钟,后面写整改单能省几个小时。

6. 进阶:把命中结果做成能交付的整改清单

跑到命中只是第一步,接下来的活是把散落的记录变成业务方能看懂、能执行的清单。原始导出通常是一行一条命中,同一个 IP 可能出现多次,同一个账号在不同服务上重复出现,直接甩给运维只会被退回来。

我习惯用脚本先做一轮归并,按「IP + 端口 + 协议」聚合,统计每个资产上的命中账号数,再按风险等级打标:管理员账号命中记为高,普通业务账号记为中,测试或临时账号记为低。下面这段是常用的整理逻辑:

# 把原始命中结果聚合成整改清单 # raw_hits.csv 需包含:IP,Port,Service,User,Password,Time $hits = Import-Csv .\raw_hits.csv -Encoding UTF8 $grouped = $hits | Group-Object IP, Port, Service | ForEach-Object { $first = $_.Group[0] [pscustomobject]@{ IP = $first.IP Port = $first.Port Service = $first.Service HitCount = $_.Count Accounts = ($_.Group.User | Sort-Object -Unique) -join ";" FirstSeen = ($_.Group.Time | Sort-Object)[0] Level = if ($_.Group.User -match "admin|root|sa|operator") { "高" } elseif ($_.Group.User -match "test|temp|guest") { "低" } else { "中" } } } $grouped | Sort-Object Level, IP | Export-Csv .\remediation_list.csv -NoTypeInformation -Encoding UTF8 Write-Host ("聚合后资产数 {0},原始命中 {1}" -f $grouped.Count, $hits.Count)

这段的关键在Level那一列的三段判断,规则可以按自己环境的账号命名习惯改,比如把svc_开头的服务账号单独归一类。Group-Object的聚合键是三个字段的组合,如果目标环境存在同一服务多实例的情况,可以把实例名也加进去。

清单出来后,还有一步是复测。整改完成后不要用全量字典重跑,按原来的命中记录逐条验证就行:账号还在不在、密码改没改、服务端口有没有变。复测结果单独记一列,和初测清单放在一起,整改闭环才算完整。

血泪经验是:第一次跑完别急着导报告,先把命中记录抽十条手工验证一遍。我吃过一次亏,工具把超时判成了成功,报告交上去被业务方当场打回,后面整批结果都得重跑。从那以后我每次跑完都强制走一遍抽样复核,哪怕只抽五条,也比返工强。资源包里除了主程序,还带了配置模板、示例字典和配套脚本的附件源码,脚本部分可以直接改成自己环境的规则。希望帮到你。

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