Hashcat的掩码攻击是我做授权渗透测试和密码安全评估时最常用的密码恢复手法之一。很多朋友刚接触Hashcat时,习惯一上来就跑纯暴力破解,遇到8位数字还好,一旦碰上大写字母、小写字母、数字混合的密码,纯暴力破解的候选空间能到几千亿甚至万亿级,哪怕挂着高端显卡,等上几天都是常事。掩码攻击的思路完全不同:它假定密码存在某种可推断的结构,用?d、?l、?u这类占位符把每一位的字符类型直接画出来,让破解器只在一个大幅缩小的空间里枚举。想要快速搞定8位混合密码,掩码攻击往往就是最优解。
这篇文章围绕一个很具体的实战需求展开:用Hashcat掩码攻击破解8位混合密码。我会把掩码语法、掩码设计思路、性能估算、常用掩码模板和踩坑排查整个讲透。适合刚接触Hashcat的渗透测试新手,也适合已经会跑字典、想正式把掩码攻击纳入武器库的进阶用户。全文只讨论授权范围之内的安全测试与密码恢复场景,所有操作请务必先确认自己拥有对应的合法授权。
1. 掩码攻击是什么,为什么它比纯暴力破解高效
1.1 从暴力破解的算力账说起
先看一组数字。一个8位密码,假设字符集是95个可打印ASCII字符,候选空间是95^8,约6.63万亿亿……准确说是6.63×10^15,也就是6630万亿种组合。显卡对当前哈希算法的破解速度如果是10 GH/s(每秒百亿次),全部跑完需要大约6.63×10^5秒,差不多7.7天。而实际渗透测试和时间窗口往往只有几小时,加密文档恢复的场景里用户更没有耐心等一周。
暴力破解天然有个浪费算力的问题:它把每个位置的字符都当成完全随机、独立分布的变量。但人设出来的密码根本不是随机的,绝大多数密码都遵循高度可预测的结构,比如首字母大写、后接小写、再跟两三位数字。暴力破解这种"无差别枚举"把大量算力消耗在永远不可能出现的组合上,比如Zzzzzzzz或者@@@@@@@@这类没人会用的密码。
1.2 掩码攻击的核心理念
掩码攻击(mask attack)是Hashcat三种核心攻击模式之一,另外两种是字典攻击和规则攻击。它通过占位符描述密码每一位的字符类型,把一个巨大的搜索空间切分成若干个小得多的子空间。以8位混合密码为例,你不需要知道具体是哪几个字符,只需要判断出它是"大写+小写+数字"还是"小写+小写+数字+特殊符号"这类结构,GPU就只需要在一个受限的范围内枚举。
打个比方:暴力破解像在全城挨家挨户敲门找一个人,掩码攻击则是根据"这个人住东区、三层、带阳台"的线索先划出一个片区,再在片区内找。搜索范围小了五六个数量级,速度自然不可同日而语。实际算一下:完全暴力的8位全字符空间是6.63×10^15,而一个?u?l?l?l?l?l?d?d掩码(首字母大写、5位小写、2位数字)的候选空间只有大约3.09×10^10,两者差了五个数量级。同一个设备,时间从"几天"直接压到"几十秒"。
1.3 掩码攻击的典型适用场景
掩码攻击适合以下场景,我在实际项目中用得比较多:
- 授权渗透测试的离线哈希破解:拿到哈希后快速验证弱密码、检验密码策略是否形同虚设。
- CTF比赛的安全题目:题目里往往直接暗示了密码格式,掩码几乎是最优解。
- 企业内部密码策略审计:考察员工密码是否落在常见结构里,掩码模板可以量化检测覆盖率。
- 取证与个人数据恢复:自己忘记密码的加密压缩包、文档、磁盘镜像,如果能回忆起部分格式,掩码可以直接缩小范围。
无论哪种场景,前提都是合法授权。对不归你管的哈希做破解尝试,不只是合规问题,也是安全从业者的基本职业底线。
2. 掩码语法完全拆解:?d、?l、?u、?s 与自定义字符集
2.1 内置字符集
Hashcat定义了一套非常简洁的占位符体系,核心就这几个:
| 占位符 | 含义 | 字符范围 | 单个位置候选数 |
|---|---|---|---|
| ?d | 数字 | 0-9 | 10 |
| ?l | 小写字母 | a-z | 26 |
| ?u | 大写字母 | A-Z | 26 |
| ?s | 特殊符号 | 空格和!@#等可打印符号 | 33 |
| ?a | 以上全部混合 | ?l?u?d?s | 95 |
| ?b | 全部可打印二进制字节 | 0x00-0xff | 256 |
另外还有?h(小写十六进制0-9a-f)和?H(大写十六进制0-9A-F),日常破解常规密码用得不多,但处理某些设备默认密钥、或者特定系统生成的恢复码时会用到,了解即可。?a是最常用的通配,等价于把大小写字母、数字、符号全部混在一起,这也是暴力破解在Hashcat里的本质写法。
2.2 自定义字符集的高阶玩法
内置占位符不够用的时候,可以用自定义字符集。Hashcat的命令行参数里,-1、-2、-3、-4分别对应四组自定义字符集,掩码中用?1、?2、?3、?4来引用。写法非常灵活:
# 自定义字符集只包含 abc123 hashcat -m 1000 -a 3 -1 "abc123" hashes.txt "?1?1?1?1?1?1" # 第一组字符集是"小写字母+数字",掩码组合使用 hashcat -m 1000 -a 3 -1 "?l?d" hashes.txt "?1?1?u?u?d?d?d?d"第二个例子表达的结构是:前2位是小写字母或数字、中间2位必须是大写字母、后4位必须是数字。自定义字符集里的内容可以叠加内置占位符,这个能力非常实用。比如目标系统的密码策略明确"只允许字母和数字、不允许特殊符号",直接用-1 "?l?u?d"就能把符号过滤掉,避免在无效空间里浪费算力。
2.3 从攻击模式到完整命令行
掩码攻击对应的攻击模式是-a 3。一个典型的完整命令长这样:
hashcat -m 1000 -a 3 -w 3 hashes.txt "?u?l?l?l?l?l?d?d" -o cracked.txt逐参数拆开看:
-m:哈希类型。1000是NTLM,0是MD5,100是SHA1,1400是SHA256,22000是WPA-PBKDF2-PMKID。拿不准时先识别哈希类型再跑。-a 3:指定掩码攻击模式。-a 0是字典,-a 1是字典组合,-a 6和-a 7是字典与掩码的混合模式。-w 3:工作负载。默认是1或2,3会占满显卡。如果机器上还跑着其他任务,建议留在2。-o:指定结果输出到文件,避免终端滚动刷屏看不清。- 掩码本身用双引号包起来。这一步有讲究,后面马上说。
这里有个新手必踩的坑:在bash或zsh里,?是glob通配符。如果不加引号,shell会把?u?l?l?l?l?l?d?d当成文件匹配模式展开,轻则掩码变成一堆文件名,重则直接报错找不到掩码。所以掩码永远要放在双引号里,这是掩码攻击的第一条纪律。
3. 实战:用掩码攻击破解8位混合密码
3.1 拿到哈希后的第一步:确认哈希类型
哈希类型搞错,一切都白搭。我见过太多人拿个MD5哈希用-m 1000(NTLM)去跑,跑了半天当然一无所获。常见的判断方法:32位十六进制字符串大概率是MD5或NTLM,64位十六进制大概率是SHA256,带$分隔符的多段式字符串基本是各类Unix shadow哈希格式。新版本的Hashcat有--identify命令可以辅助识别,老版本或者拿不准时,直接查看Hashcat自带的示例哈希列表:
hashcat --example-hashes -m 0把输出里的示例和手头的哈希放一起逐段比对,格式特征确认了再选-m。这一步错不得,选错哈希类型,掩码跑得再快也没有意义。
3.2 根据密码策略设计掩码结构
确认哈希类型之后,下一步不是急着敲命令,而是推断密码结构。假设目标系统的密码策略是"8位以上、必须包含大写字母、小写字母和数字",那么用户最可能按什么习惯设置?我做了那么多密码审计,观察到的规律高度一致:首字母大写、后面跟几个小写字母、最后加一到两位数字,比如Abcdef12、Happyday9这类。基于这个判断,优先跑的掩码应该是:
?u?l?l?l?l?l?d?d:首字母大写、5位小写、2位数字?u?l?l?l?l?l?l?d:首字母大写、6位小写、1位数字?u?u?l?l?l?l?d?d:前两位大写、4位小写、2位数字
对比一下候选空间和耗时,就能直观感受到掩码的价值。为了计算统一,我按NTLM哈希在主流显卡上常见的10 GH/s下限来估算:
| 掩码 | 候选空间 | 10 GH/s耗时 |
|---|---|---|
?a?a?a?a?a?a?a?a(纯暴力) | 6.63×10^15 | 约7.7天 |
?u?l?l?l?l?l?d?d | 3.09×10^10 | 约3秒 |
?u?l?l?l?l?l?l?d | 8.03×10^10 | 约8秒 |
?l?l?l?l?d?d?d?d | 1.76×10^10 | 约2秒 |
同一台机器,同一个哈希类型,纯暴力破解要跑一周,掩码攻击只需要几秒。如果哈希算法更慢(比如某些SHA变体速度只有1 GH/s甚至更低),掩码结构的优势会进一步放大。结构信息的价值就在这里。
3.3 先跑基准测试再估算时间
正式开跑之前,强烈建议先给当前设备跑一次基准测试,搞清楚自己的硬件在不同哈希算法上的真实速度:
hashcat -b -m 1000输出里会给出每秒尝试数(H/s)。拿到这个数之后,时间估算就很直白了:时间 = 掩码候选空间 ÷ 每秒速度。比如基准测试显示当前设备对NTLM是12 GH/s,那么?u?l?l?l?l?l?d?d(3.09×10^10)大约2.6秒就跑完。如果是一个慢哈希,设备试验速度只有500 KH/s,那同样掩码要跑8小时,这时候就得重新评估掩码是否还有收窄空间。
另外有个小技巧,可以用--stdout参数把Hashcat当成候选生成器,直接数一数某个掩码到底会产生多少组合:
hashcat -a 3 --stdout "?u?l?l?l?d?d?d?d" | wc -l这个输出数量就是精确的候选空间,比手算更靠谱,也方便你验证掩码有没有拼错结构。
3.4 完整执行与结果读取
确认结构和速度都合理之后,正式执行:
hashcat -m 1000 -a 3 -w 3 -o cracked.txt hashes.txt "?u?l?l?l?l?l?d?d"跑完以后,用--show直接从potfile里读取已破解结果:
hashcat -m 1000 --show hashes.txt--show不会重新破解,而是读取Hashcat的potfile缓存,秒出结果。这里有一个实际项目里很实用的习惯:哈希文件最好按用户名:哈希的格式组织,破解后输出才能对应回具体账号。如果只丢一串哈希进去,破出来就是孤零零的明文,没法落地到"哪个账号用了弱密码"。
4. 常用掩码模板整理
4.1 模板速查表
Hashcat自带的masks目录里就有一批.hcmask格式的模板文件,可以直接用-a 3 masks/passwords_8.hcmask调用。我自己整理了一份按场景分类的常用8位掩码表,比官方那个更贴合实际审计需求:
| 模板名称 | 掩码 | 候选空间(约) | 典型场景 |
|---|---|---|---|
| 8位纯数字 | ?d?d?d?d?d?d?d?d | 1.00×10^8 | 初始密码、设备默认密码 |
| 8位纯小写 | ?l?l?l?l?l?l?l?l | 2.09×10^11 | 无策略约束的老系统 |
| 首大写+7小写 | ?u?l?l?l?l?l?l?l | 2.09×10^11 | 大写开头但后面不带数字 |
| 首大写+5小写+2数字 | ?u?l?l?l?l?l?d?d | 3.09×10^10 | 强密码策略下的典型习惯 |
| 首大写+6小写+1数字 | ?u?l?l?l?l?l?l?d | 8.03×10^10 | 数字只加一位的保守用户 |
| 2大写+4小写+2数字 | ?u?u?l?l?l?l?d?d | 3.09×10^10 | 前两位固定大写的习惯 |
| 3小写+5数字 | ?l?l?l?d?d?d?d?d | 1.76×10^10 | 单词前缀+数字后缀 |
| 经典弱密码变体 | ?u?s?l?l?l?d?l?d | 3.92×10^10 | 匹配P@ssw0rd这类结构 |
| 8位全字符暴力 | ?a?a?a?a?a?a?a?a | 6.63×10^15 | 完全未知结构的兜底方案 |
4.2 模板背后的概率逻辑
这些模板不是拍脑袋写出来的,背后是对大量真实密码样本的统计。人设密码有一个非常强的规律:当系统强制要求"大小写+数字"组合时,绝大多数人会选择把大写放在开头、数字放在结尾,中间的字母尽量用小写,因为这样最好记、最好打。?u?l?l?l?l?l?d?d这类模板赌的正是这个习惯。而完全没有策略约束的系统里,纯数字、纯小写的比例极其高,?d和?l模板跑第一轮往往就有收获。
另一个值得注意的模板是?u?s?l?l?l?d?l?d,它对应的是P@ssw0rd这棵"经典弱密码树"上的所有变体:首字母大写、紧跟一个特殊符号、几个小写、一个数字替换字母o、再补一个小写和数字。这类密码看起来复杂,实际上命中率极高,在不少企业的密码审计里,这一类能捞出一大把。
4.3 字典、规则与掩码的叠加组合
掩码攻击不是孤立的,它可以和字典、规则灵活组合,覆盖更多场景:
-a 6:字典在前、掩码在后,比如hashcat -m 1000 -a 6 words.txt "?d?d?d?d",适用于"已知基础词+4位数字尾巴"的结构。-a 7:掩码在前、字典在后,比如hashcat -m 1000 -a 7 "?u?u" words.txt,适用于"两位大写前缀+常见单词"。- 规则叠加:先用
-r best64.rule对字典做常见变形(大小写替换、尾号追加),再配合掩码,把同一份字典吃干榨净。 - 多个掩码文件:把模板写进
.hcmask文件放进masks目录,之后一句命令直接调用,不用每次手敲长串掩码。
经验之谈:先跑字典加规则,再跑掩码模板,最后才上全字符暴力。这个优先级能帮你省下大量时间,因为实际目标里能被字典命中的比例远高于你的直觉,掩码是字典失效后的第二梯队。
5. 常见问题与排查技巧实录
5.1 速度远低于预期
掩码空间明明很小,却跑得很慢,问题通常出在几个地方。
首先看工作负载参数。-w 3和默认的-w 1跑出来的速度差几倍都很正常,如果显卡没有跑满,先试着把-w拉高。其次确认是不是真的在用GPU跑,老机器或者驱动配置不当的时候,Hashcat可能会退化到CPU上。用-d参数指定设备编号,配合--benchmark对比理论速度,一目了然。
还有一个很现实的原因:哈希算法本身就很慢。MD5、NTLM这类"快哈希"在GPU上每秒几十亿次,但bcrypt、scrypt、Argon2这类慢哈希每秒只有几百到几千次,是刻意设计成抗GPU破解的。遇到这种哈希,再大的显卡也救不回来,唯一能做的就是让掩码更精确,把候选空间压到尽量小。
5.2 常见报错信息怎么处理
我自己整理了一份高频报错速查,新手对照着看能少走很多弯路:
| 报错现象 | 常见原因 | 处理方式 |
|---|---|---|
| No devices found | OpenCL驱动缺失或版本过旧 | 更新显卡驱动,检查平台枚举,用-I查看可用设备 |
| Separator unmatched | 哈希文件格式错误,一行塞了多个哈希 | 检查哈希文件,确保每行一个条目,且格式和-m匹配 |
| Token length exception | 哈希长度和所选-m类型对不上 | 重新识别哈希类型,换-m |
| Out of memory | 自定义字符集过大、掩码过长导致生成器内存溢出 | 拆分掩码分段跑,或改用-O优化内核 |
| 候选空间数了半天卡住 | 生成启动时构造掩码内部结构耗时 | 掩码别写得太复杂,过多的固定字符和自定义集会影响初始化 |
5.3 掩码写对了却不输出结果
排除Hashcat本身报错之后,最常见的"没结果"其实是这三个原因。
第一个是potfile缓存干扰。同一份哈希如果之前跑过且命中了,potfile里已经有记录,再跑一次Hashcat不会重复输出,因为默认认为已经"完成"了。想干净重跑,用--potfile-disable,或者指定一个新的--potfile-path。
第二个是掩码结构与实际密码差距太大。你赌?u?l?l?l?l?l?d?d,但目标密码实际是?l?l?l?l?d?d?d?d,跑完整个空间也不会有结果。这时候别死磕一个模板,换一份结构假设多试几个。
第三个是长度问题。说是8位密码,实际上可能有些账号是7位、有些是9位。这时候用增量模式:
hashcat -m 1000 -a 3 --increment --increment-min 8 --increment-max 8 hashes.txt "?u?l?l?l?l?l?d?d"--increment会按min到max的区间自动扩展掩码位数,把长度变化也覆盖进去。实战里我一般直接--increment-min 6 --increment-max 10配合一个通用模板,先把长度区间扫一遍。
5.4 合理排序与进度监控
完全不知道结构的8位混合密码,我的默认处理顺序是这样的:
- 先跑
?d?d?d?d?d?d?d?d,纯数字,几秒十几秒,先捞一波初始密码。 - 再跑纯小写
?l?l?l?l?l?l?l?l,覆盖无策略约束的老系统。 - 然后跑
?u?l?l?l?l?l?l?l和?u?l?l?l?l?l?d?d这类大写开头结构。 - 接着是
?u?s?l?l?l?d?l?d这类经典弱密码变体。 - 最后才考虑
?a?a?a?a?a?a?a?a全字符暴力,这通常已经是最后手段。
长任务执行期间,可以用--status-timer 60让Hashcat每60秒打印一次当前进度和剩余时间,配合--restore断点续跑,宕机或中断都不至于白跑。这个习惯在跑几十亿候选空间的模板时尤其重要。
6. 实操心得与几个容易忽略的细节
整个掩码攻击流程走下来,我个人体会最深的是:掩码攻击的瓶颈从来不是显卡,而是你对密码结构的判断。自定义字符集和内置占位符都是死工具,真正拉开差距的是猜结构的能力——这需要对目标系统密码策略、用户习惯和常见弱密码模式有足够的敏感度。
几个容易被忽略的细节,最后统一提醒一下:
每次破解前先跑benchmark,把自己设备在不同哈希上的实测速度记成一张表。以后遇到任何估算,直接查表代入,不用每次重新摸索性能。这个习惯帮我省了很多时间。
potfile里存的是明文密码,属于高度敏感的数据。不要在共享目录、临时目录里留potfile,审计项目结束后该清理的清理,该加密保存的加密保存。很多安全测试项目出问题,往往不是破解不了,而是结果文件管理不当。
我自己习惯把所有常用掩码写成.hcmask文件,按"8位数字""8位混合""日期后缀""弱密码变体"分类归档,配合-a 3 masks/xxx.hcmask直接调用。积累一段时间之后,这套模板库就是针对不同客户、不同系统策略最快出活的基础设施。掩码攻击从入门到熟练,区别就在于你有没有自己的模板积累,以及遇到跑不出结果时能不能冷静地换一个结构假设。希望这篇文章能让你少踩几个坑,尽快把这一套流程用顺。