☰
弱口令审计实战:SNETCracker端口字典配置与误报复核整改
2026/10/26 10:34:00 网站建设 项目流程

简介:SNETCracker 是一套面向 Windows 平台的安全审计工具配套源码,适用于弱口令自查、授权渗透测试学习和批量口令审计,适合具备 C# 与网络协议基础的安全从业者、运维人员及学生参考。工具基于 C# 开发,需 .NET Framework 4.0,覆盖 SSH、RDP、SMB、MySQL、Oracle、FTP、MongoDB、Redis、VNC 等常见服务,支持批量 IP 或 IP 段、多线程检查、端口扫描、自定义端口与字典,并可将用户名与密码组合尝试以提高命中率。压缩包共 129 个文件,约 11.47MB,以 62 个 cs 源码和 54 个 dll 依赖为主,另含 resx 资源、sln/csproj 工程及配置文件,便于梳理项目结构、界面逻辑与扫描模块。已有 1163 人学习,可作为理解多线程任务调度、服务探测、弱口令检测流程和字典配置的实践样本,也便于做功能裁剪与二次开发。

1. 四十条命中只有十一条作数:弱口令审计先把判断标准立起来

某次授权范围内的内网自查,我用 SNETCracker 跑完一整轮,结果窗口弹出四十多条命中,逐条人工复核之后能落地的只有十一条,剩下的是服务识别错位、已经停用的账号,还有把“账号存在但登录被拒”当成弱口令的空欢喜。那天我把结果清单打印出来一条条对,才想明白这类 Windows 平台上的批量弱口令审计工具真正的作用是放大判断,而不是替代判断——它把候选面从几万台主机收敛到几十条记录,剩下的事还得靠人。

这篇讲的就是怎么把这件事做扎实:审计目标面怎么划、自定义端口和字典怎么配、用户名和密码结合的检查方式什么时候开、多线程和超时怎么取值才不会把业务登崩,最后命中清单怎么复核成一张能交给责任人签字的整改单。做资产自查的运维、拿到书面授权的渗透新手,照着流程走能少跑几轮空转;带过队的老手,可以重点看第 3 章的配置取舍和第 5 章的误报来源。

2. 先划战场再开工具:审计目标面和授权窗口怎么定

2.1 从暴露面反推该审哪几类服务

我一般不会打开工具就先填个网段全量跑。那种做法的结果通常是两种:要么命中一堆你已经知道的老问题,要么一个都不命中,然后你怀疑工具坏了。正确的顺序是先拿到活主机和开放端口,再拿“工具支持的服务类型”去和暴露面做交集,交集之外的一个都别碰。下面这张表是我自己审计时会先填的,左边是服务,右边是这类服务在弱口令审计里最容易出的问题,按这个顺序排优先级基本不会错。

服务类型默认端口审计关注点处置优先级
远程桌面3389本地管理员组账号、空口令、用户名同名口令高
文件共享445共享账号、域内陈旧账号高
远程终端22root 直连、默认运维口令高
数据库类3306 / 1433 / 5432 / 1521sa、root 空口令,测试库沿用生产口令高
文件传输21匿名登录、弱口令、权限越界中
明文远程登录23明文传输,口令一旦弱等于全暴露高
图形远程5900无认证直连、口令位数过低中

这张表的作用不是让你照抄,而是让你在开跑之前就能回答一个问题:这台机器上跑着哪类服务,我准备用哪套字典去打它。同一个网段里 3389 和 21 的字典策略完全是两回事,运维类账号和业务类账号的命名习惯也完全不同。把这层想清楚,后面配端口、配字典才有依据,否则就是拿一把钥匙去捅一整栋楼的门。

顺带说一句,暴露面清单的时效性很重要。三个月前的端口扫描结果直接拿来用,翻车的概率不低——服务下线、端口改了、机器重装系统换了用途,这些都会让你的命中率莫名其妙地掉下去。我现在的习惯是审计当天重新拉一遍存活和端口,宁可多花两小时,也别拿旧清单跑一晚上。

2.2 授权边界和审计窗口:先问清三件事再动手

弱口令审计和普通漏洞扫描最大的区别是它会真的尝试登录,一旦账号被锁、服务被挤占,业务那边立刻就能感觉到。所以在跑之前,有三件事必须先书面确认,口头答应不算。

第一件是授权范围,精确到网段和端口,超出范围的主机哪怕扫出来也不要去试,这不是谨慎,这是底线。第二件是账号锁定策略,域账号和企业统一认证一般都有失败次数阈值,触发了就是一片人登不上系统,这个阈值必须提前问到具体数字。第三件是业务高峰时段,数据库和远程桌面类的审计尽量安排在低峰,尤其是并发调高的时候。

提示:把授权范围、时间窗口、锁定阈值写进一份简单的记录里,跑之前发给对接人确认一遍,跑完连同结果一起归档。真出事的时候,这份记录比任何解释都有用。

审计窗口的另一个考虑是重试。网络抖动在跨网段审计里非常常见,一次超时并不代表口令错,如果工具侧配了重试,那么同一个账号实际会发出两次以上的认证请求。锁定阈值如果是 5 次,你的有效尝试次数其实只有 2 次,这个账要提前算,不能等锁了才反应过来。

2.3 目标清单的清洗和分组

拿到原始清单之后我会先做一次清洗,重复 IP、非法格式、带注释的行全部剔掉,然后按端口分组存成不同文件。这一步看着琐碎,但它直接决定了后面能不能一批一批地跑、能不能出问题只回滚一批。下面这段是我常用的处理脚本,输入是一个每行一条记录的文本文件,支持IP和IP:端口两种写法,输出按端口分文件。

# 目标清单清洗:去重、校验、按端口分组 import ipaddress from collections import defaultdict raw = open('targets_raw.txt', encoding='utf-8').read().splitlines() groups = defaultdict(set) for line in raw: line = line.strip() if not line or line.startswith('#'): # 跳过空行和注释行 continue if ':' in line: host, port = line.rsplit(':', 1) # 从右侧切,兼容 IPv6 写法 else: host, port = line, '0' try: ipaddress.ip_address(host) # 非法 IP 直接丢弃,避免污染后续批次 except ValueError: continue groups[int(port)].add(host) for port, hosts in groups.items(): name = f'targets_{port if port else "default"}.txt' with open(name, 'w', encoding='utf-8') as f: f.write('\n'.join(sorted(hosts))) print(name, len(hosts))

这段代码里最关键的两行是rsplit(':', 1)和ipaddress.ip_address(host)。前者决定了带端口的目标能不能被正确拆开,后者决定了那些从表格里复制粘贴带出来的多余字符会不会混进目标文件——这类脏数据混进去之后,工具的表现往往是“卡在某个目标上不动”,排查起来非常费时间。分组输出的意义在于,你可以先跑targets_3389.txt这一批,跑完看结果没问题再跑下一批,出问题也只影响一个服务类型的审计。

分组之后还有一个动作容易被省掉:给每一组标上业务归属。同一个 IP 段里可能既有生产库也有测试机,处置优先级完全不一样。我一般会在文件名里带上批次号和日期,比如targets_3389_batch01_0412.txt,这样后面结果对账的时候能直接定位到是哪一批跑出来的。

3. 端口、字典、用户名密码组合:SNETCracker 三项配置的取舍

3.1 自定义端口怎么填才不出错

默认端口这件事在真实内网里几乎从来不成立。远程桌面改成 13389、数据库挪到 3xxxx、文件传输挪到 2xxxx,这些都是常规操作。SNETCracker 支持自定义服务端口,这个能力要用起来,但填的时候有两个细节容易出问题。

第一个细节是“一个服务对多个端口”。同一类服务在不同主机上可能监听不同端口,这时候不要在一行里堆一串端口,而是拆成多个任务分别跑,每个任务只对应一个端口。原因很实际:多线程审计的结果是按任务归集的,端口混在一起,出结果之后你分不清是哪台机器的哪个端口命中的,复核阶段要一台台回去验证,工作量翻倍。

第二个细节是端口和协议的对应关系。工具判断服务类型靠的是端口和协议特征,你把一个非标准端口填到某个服务类型下面,工具会按那套协议去握手,握手失败自然就没结果。所以填之前先用端口清单确认一遍这个端口上跑的是什么,别猜。我见过最典型的翻车是有人把 Web 管理后台的端口填到数据库类型下面,跑了一晚上零命中,然后开始怀疑字典有问题。

注意:非标准端口审计时,先对单个目标手工验证一次能否正常建连,确认协议对得上,再开批量。这一步花三分钟,能省掉一整晚。

3.2 字典怎么配:通用、业务、变形三层

字典质量直接决定命中率,这件事有点玄学,但可控的部分比想象中多。我的做法是分三层准备,按顺序拼进最终字典文件。

第一层是通用弱口令,123456、password、admin、qwerty这类,数量不大但覆盖面广,放在最前面。第二层是业务字典,从资产信息里推导出来——系统简称、项目代号、部署年份,组合成xxx2024、xxx@2024、Xxx2024!这样的形态。第三层是变形层,针对已知用户名做大小写、后缀、符号的变形。第三层配合 3.3 节讲的用户名密码结合检查一起用,效果最好。

# 业务字典生成:占位符替换成你自己的资产信息 SITE = ['org', 'lab', 'demo'] # 系统简称/项目代号,自行替换 YEARS = ['2022', '2023', '2024', '2025', '2026'] BASE = ['admin', 'test', 'guest', 'operator'] SUFFIX = ['', '123', '123!', '@123', '!@#', '888'] out = set() for s in SITE: for y in YEARS: out.add(f'{s}{y}') # org2024 out.add(f'{s}@{y}') # org@2024 out.add(f'{s.capitalize()}{y}!') # Org2024! for b in BASE: for suf in SUFFIX: out.add(f'{b}{suf}') # admin123! with open('dict_biz.txt', 'w', encoding='utf-8') as f: f.write('\n'.join(sorted(out))) print(len(out))

这里用set是为了自动去重,用sorted是为了让输出稳定可复现——同一份输入每次生成完全相同的字典,出了问题才能复现。真正决定命中率的是SITE和YEARS这两个列表填得准不准,这两个值必须从你自己的资产信息里来,填得越贴近实际命名习惯,命中率越高。

字典顺序这件事值得单独说一句。多数工具是按下发顺序逐条尝试的,把高概率口令放在前面,能显著缩短单账号的平均尝试次数,也就降低了触发锁定的风险。我一般按“通用弱口令 → 业务组合 → 变形扩展”的顺序拼接,最后再整体去重一遍。

3.3 用户名和密码结合的检查方式怎么开

“密码支持和用户名结合进行检查”是这个工具最值得用的一个能力,原理不复杂:很多系统的弱口令不是随机弱,而是和账号本身强相关,zhangsan的密码是zhangsan123,ops01的密码是Ops01@2024。纯通用字典打这类账号命中率很低,一旦把用户名作为变量带进密码,命中率会明显抬起来。

前提是你手里得先有一份用户名清单。来源一般有三个:资产系统导出、共享目录里的账号表、以及按命名规则做的有限枚举。前两个最可靠,第三个要谨慎,枚举范围太大会直接把锁定阈值打穿。拿到清单之后,用下面这段生成用户名:密码的组合文件。

# 用户名 → 候选密码 的变形组合 users = [u.strip() for u in open('users.txt', encoding='utf-8') if u.strip()] SUFFIX = ['', '123', '123456', '@123', '!', '2024', '2025'] with open('pairs_userpass.txt', 'w', encoding='utf-8') as f: for u in users: for suf in SUFFIX: f.write(f'{u}:{u}{suf}\n') # zhangsan:zhangsan123 f.write(f'{u}:{u.capitalize()}@2024\n') # zhangsan:Zhangsan@2024 f.write(f'{u}:{u.upper()}!2024\n') # zhangsan:ZHANGSAN!2024

SUFFIX列表控制在 5 到 8 个就够,每多一个后缀,单账号的尝试次数就多一次,锁定风险同步上升。首字母大写加年份这条单独写出来,是因为它在实际审计里命中率往往比全小写更高——不少系统的初始口令规则就是这么定的。生成的组合文件再和通用字典拼接使用,顺序上把用户名组合放前面。

3.4 线程、超时、重试的取值逻辑

多线程是这类工具的性能来源,也是最容易出事的参数。线程数不是越大越好,我一般的起步值是 10 到 20,小样本试跑确认目标扛得住之后,再按批次往上加,单批次不超过 50。判断标准有两个:目标侧的业务监控有没有明显波动,以及本机的网络连接数有没有打满。

超时我给 3 到 5 秒,跨网段或者目标负载高的时候给到 8 秒。超时设太短会把“响应慢”误判成“口令错”,白白漏掉真实命中;设太长会让慢主机占住线程,整体进度被拖死。重试次数默认给 1 次,如果目标侧锁定阈值很低,就把重试关掉,用降低并发来换稳定性。

这三个参数之间是联动的:并发高、重试多、锁定阈值低,三者凑一起就是事故。血泪经验是先把并发压到最低跑通一轮,确认整条链路没问题,再逐步放开,每次只调一个参数,出问题能立刻定位是哪个参数引起的。

4. 一轮可复现的审计:小样本试跑到命中结果对账

4.1 先拿三到五台机器做试跑

全量跑之前必须试跑,这一步没有例外。挑三到五台有代表性的目标,覆盖不同服务类型、不同网段、不同系统版本,用最终要用的那套配置完整跑一遍。试跑要看的不是命中多少,而是四件事:能不能正常建连、认证失败和网络失败能不能区分、日志里有没有异常报错、本机和目标的资源占用是否正常。

试跑阶段建议把结果输出打开到最详细,每一条尝试都留痕。这时候结果多不多不重要,重要的是链路是通的。我见过太多人跳过试跑直接全量,跑到一半发现协议版本不对,前面几小时全白费,还得从头再来一遍。

试跑通过之后,把配置原样保存一份,后面全量跑用的是同一份配置。这一步是为了可复现——结果有问题的时候,你可以拿着同样的配置再跑一次,看现象是否能重现。

4.2 全量跑的时候盯什么

全量跑不是点完开始就去干别的。前十分钟要盯三个指标:进度增速、命中率、错误类型分布。命中率在开跑初期应该是缓慢上升的,如果一直是零,大概率是配置问题而不是目标真的都安全,这时候停下来查比硬跑强。错误类型里如果“连接超时”占比特别高,说明网络或者并发有问题,继续跑只会浪费时间。

中途发现某一批目标命中率异常低,可以先跳过这批,跑完其他批次再回头单独查。工具一般支持任务中断和续跑,用这个能力把大批次拆成小批次,是降低风险最直接的办法。

跑完之后不要立刻导出结果就完事,先看一遍总览:总尝试数、成功数、失败数、超时数,四个数加起来应该等于总数。加不上就说明有记录丢了,可能是写入冲突,也可能是磁盘满了。这个对账动作看起来多余,但它能在你拿着结果去汇报之前,先把数据不可信的问题拦下来。

4.3 结果导出后的三方对账

导出的原始结果不能直接用,里面通常有三类噪声:同一账号因为多端口、多批次重复出现;空口令和认证失败混在一起;账号名大小写不一致导致同一个人算成两条。下面这段做完去重和优先级排序,输出一份可以直接进复核的清单。

# 命中结果清洗:去重 + 按风险分级排序 import csv rows = [] with open('result.csv', encoding='utf-8-sig') as f: rows = list(csv.DictReader(f)) seen, merged = set(), [] for r in rows: key = (r['ip'].strip(), r['port'].strip(), r['user'].strip().lower()) if key in seen: # 同一 IP+端口+账号只保留第一条 continue seen.add(key) merged.append(r) def level(r): pwd, user = r.get('pass', ''), r['user'].strip().lower() if pwd == '': # 空口令,最高优先级 return 0 if pwd.lower() == user: # 口令与用户名相同 return 1 if user in ('admin', 'root', 'sa', 'administrator'): return 2 # 运维类账号 return 3 merged.sort(key=level) with open('result_clean.csv', 'w', encoding='utf-8-sig', newline='') as f: w = csv.DictWriter(f, fieldnames=merged[0].keys()) w.writeheader() w.writerows(merged)

key里带上端口是必要的,同一台机器的不同服务必须分开算,否则会把远程桌面和数据库的命中合并成一条,整改的时候就漏了。level这个分级函数是我自己加的,排序之后空口令和同名口令排在最前面,复核的时候按顺序往下走,效率高很多。

清洗完之后还有一步是人工对账:拿清洗后的清单,和资产台账逐个核对账号归属,确认这个账号是不是还在用、属于哪个责任人、对应什么业务。这一步没有捷径,但它是把“工具输出”变成“可整改问题”的唯一通道。

5. 避坑与排查:五个把弱口令审计跑成事故的现场

5.1 现象:跑了一整晚,命中数为零

原因通常不在工具,而在三个地方。端口填错是最常见的,尤其是非标准端口,把服务类型和端口对错了,握手根本建立不起来。第二是协议版本不匹配,老系统的认证握手方式和新版本不一样,工具按新协议去谈,对方直接断开。第三是字典压根没覆盖到,通用字典打完一轮,目标用的全是业务组合口令,自然一条不中。

解决办法是分层排查:先拿单台目标手工验证能否正常建连,确认网络和端口没问题;再用一个已知存在的弱口令账号做正向验证,确认工具这条链路能出结果;最后才去怀疑字典,看字典条数、编码格式、有没有多余的空行和 BOM 头。这三步走完,九成以上的零命中都能定位到具体环节。

5.2 现象:第二天业务反馈账号被锁

这是最典型的事故形态,原因几乎都是并发和重试叠加,把失败次数打到了锁定阈值以上。还有个容易被忽略的点:同一批目标里可能有多台机器共用同一套统一认证,你在不同任务里分别试同一个账号,锁定计数是共享的,账面上看每台机器都没超,实际上早就超了。

解决办法是审计前把锁定阈值问到具体数字,按“并发数 × 重试次数 < 阈值”倒推参数。统一认证环境下,把共用同一认证源的目标合并成一个任务串行跑,别拆成并行批次。真锁了要立刻停止审计,联系管理员解锁,把受影响的账号清单和锁定时间点记录下来,一起写进审计报告里,别藏着。

5.3 现象:命中很多,复核大半是误报

误报的来源有三类。一是服务识别错位,端口上跑的其实是别的东西,工具按错误的协议解析返回值,把一段正常响应当成了认证成功。二是版本差异,某些老版本的认证协议在特定条件下会返回容易误判的状态码。三是空口令判定过宽,账号存在但被禁用,返回的提示和口令正确很接近,被算成了命中。

解决办法是复核阶段强制作二次验证:对每一条命中,用独立的方式重新登录一次,确认口令真的可用。这一步不能省,把误报写进整改单比漏报更伤信誉。另外在配置阶段就把“仅报告确认成功”这类选项打开,宁可少报也不要乱报。

5.4 现象:进度卡在某个 IP 上长时间不动

原因是超时设置和线程池的交互问题。某台主机响应极慢或者半开连接,线程会一直等在那儿,线程池被占满之后整个任务就停住了。表现是进度条长时间不动,日志也不再刷新,看着像死机。

解决办法是把超时压到一个合理范围,3 到 5 秒,配合一次重试。同时把目标清单拆小,单批次控制在几百台以内,卡住的时候能快速定位到具体是哪台。跑之前先用连通性测试筛一遍,把完全不通的目标剔除掉,别让它们占着线程干等。

5.5 现象:同一个账号在结果里出现七八次

原因是多线程写入没有去重,或者字典本身有重复条目。多批次跑的时候更明显,同一台机器在不同批次里都被扫到,结果文件里就重复了。这种噪声看起来无害,实际上会让复核工作量翻好几倍,还容易让人误判问题规模。

解决办法分两头:字典侧在生成时就用集合去重,输出前再整体排一次;结果侧在清洗脚本里按 IP、端口、账号三元组去重,就是 4.3 节那段代码干的事。批次之间最好加个标记列,写清楚这条结果是哪一批、什么时间跑出来的,后期追溯能省很多事。

提示:踩过的坑里,最难查的永远是“看起来一切正常但结果不对”的那一类。所以每一个环节都留一份可复现的输入和配置,出问题的时候能原样重跑一遍,比任何猜测都管用。

6. 从命中清单到整改单:我最后只留这一张表

审计报告写得多漂亮都不如一张能签字的整改单。清洗后的命中清单还只是“问题候选”,要变成能推进的事,得补上三列:账号归属、整改方式、复测时间。我现在的做法是直接把清洗结果导进下面这张表,一个账号一行,谁的责任谁签字。

序号目标服务/端口账号风险等级整改方式责任人复测日期
110.x.x.21远程桌面/3389本地管理员高(空口令)立即停用并重置运维组次日
210.x.x.35数据库/3306测试账号高(同名口令)改密 + 限制来源应用组三日内
310.x.x.77文件传输/21共享账号中(弱口令)改密 + 启用双因素业务组一周内
410.x.x.90远程终端/22运维账号高(口令可猜)改密 + 禁用直连运维组次日

整改方式这一列不要写“加强口令复杂度”这种没法验收的话,要写成能验证的动作:停用、改密、限制访问来源、启用双因素、改为密钥认证。风险等级和复测日期挂钩,高风险的次日复测,中风险一周内复测,复测不复用原字典,重新生成一批新口令变体再打一遍,确认旧口令确实失效。

我还会在表里加一列“是否首次发现”,用来区分老问题和新增问题。老问题反复出现,说明上一次的整改根本没落地,这时候要找的是流程,不是技术。

回过头看,那四十多条命中最后只剩十一条这件事,对我的改变不在于工具用得多熟,而在于我从此再也不把工具输出当成结果。从那以后我每次开跑之前都强制走一遍:先划目标面,再小样本试跑,跑完先做数据对账,最后逐条人工复核,一步都不跳。这套流程看着慢,但它是唯一能让你拿着结果去开会而不心虚的做法。希望帮到你。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询