这是什么?

一款 Windows 下单文件离线 HashDump 工具(代号 bypassEDR_hashdump_V1)的静态加固版。在 SYSTEM 上下文一次运行,直接打印本机 SAM 本地账号 NTLM/LM 与 缓存域凭据 DCC2。

这不是原版的重发,而是做了一层专门针对 360 QVM 静态 / 火绒的混淆加固之后的新版本——在「行为面靠 reg.exe 白利用」的基础上,把 静态指纹也清零了:

核心能力:

  • 单 EXE(约 94KB、静态去符号、带自洽 PE 资源),目标机跑一次即出可 diff 的 hash

  • 敏感读取全由微软签名白程序 reg.exe export 完成,本进程零直读 SAM

  • 静态面:敏感字面量 hex 低熵编码 + volatile 解码,360 QVM 无高熵字节块可抓;一次发布一换 XORKEY

  • 不导出 SYSTEM hive——bootkey 从 Lsa 子键 class 活体读取

  • 附带 --keep 保留 .reg 取证、--selftest 加密自检(13/13)

为什么取 Hash 这么麻烦?

红队横向 / 取证常要一台机器的本地口令哈希,目标就两类:

  1. SAM 本地账号 的 NTLM/LM

  2. 缓存域凭据(最近登录过的域账号,存 DCC2)

经典打法 secretsdump.py 有三座大山:

  • 得把 SAM / SYSTEM / SECURITY 三个 hive 原样取回来——Vista 后 SAM/SECURITY 只放 SYSTEM 读,取 hive 本身就是高危动作

  • 攻击机得是 Windows + Python + impacket,Linux 攻击机还得 .reg → 还原 hive 绕一大圈

  • 目标机有杀软/EDR 时,reg save、卷影拷贝、直读 SAM 句柄——全是必拦项

一句话:工具链越重,暴露面越大。 而常见 HashDump 工具本身往往还有一个死穴——字符串明文裸奔:reg.exe、HKLMSAM、NTPASSWORD、qwerty 常量全躺在 .rdata 里,360 QVM 的「高熵字节块 + 特征串」一拍一个准。本版把这层也补上了。

核心思路一:脏活全交给微软签名的 reg.exe(行为面)

V1 不是跟 EDR 拼特征,而是不碰 EDR 想拦的东西:

所有敏感读取,全部交给微软签名的 reg.exe export HKLMSAM / HKLMSECURITY。 我们进程拿到的只是 reg.exe 自己导出的 纯文本 .reg, 之后全程本地纯 CPU 解密——没有第二次敏感系统调用。

EDR 天天拦 reg save、拦直开 RegistryMachineSAM、拦 VSS,却很少拦管理员常用的 reg.exe export HKLMSAM——签名白程序、普通读键语义,规则引擎眼里就是日常操作。这是原版项目 AabyssZG/HashDump-BypassEDR 的核心盲点,V1 完整继承。

核心思路二:静态指纹清零,专打 360 QVM / 火绒(本版重点)

行为面再干净,产物本身若被静态一眼看穿,投递层就白做了。这一版针对 360 QVM 的静态判定习惯(高熵字节块 → "Malware.gen"、加壳 → "壳即恶意"、字符串特征 → "Trojan/PSW")做了四件事:

层面

手法

为什么过 QVM

字符串存储

敏感字面量 XOR → 存成 0-9A-F 的 hex 串(~4bit/字符)

.rdata

 里没有高熵字节块;strings 扫不到 SAM/SYSTEM/NTPASSWORD/reg.exe/PolEKList/NL$KM 任何明文

常量折叠

解码用 const volatile 指针逐字节还原

防 -O2 把异或常量直接折叠进立即数/.rodata,明文不落地

密钥轮换

单个全局 XORKEY,发布前换一次 key

,gen_xstr.py 一键重新生成整张串表

每次发布产物字节全变,云查样本指纹失效,无法按旧样本哈希命中

进程观感

VERSIONINFO

 + app.manifest(asInvoker+supportedOS) + 图标 + -static -s 去符号

带自洽的版本/清单/图标,不像临时拼的裸 hack;不加壳不加花指令,不触发"加壳即恶意"

连错误提示、$DCC2$ 输出格式、hh8_ 临时目录前缀这些"看着无害"的串,也全部走同一张 xstr 表——二进制里没有任何一条可 grep 的提示链。整份产物约 94KB,是给杀软"一个普通小工具的观感",而不是"一个 HashDump 的观感"。

一句话:行为面交给微软白程序,静态面把指纹抹到 strings 都扫不出来。

相比原版,更近一步

原版也是"reg.exe 导出"思路,却卡在攻击机侧太重:导出 3 个 .reg 后还要在(同样得是 Windows 的)攻击机上跑 PowerShell 把文本 reg 导回注册表、再 reg save 还原成 hive,才轮到 secretsdump。V1 把解密整体搬进了目标机:

原版

V1

还原 hive

要(RegReduction + Windows 攻击机)

砍掉

,.reg 直接当 hive 使

SYSTEM hive

导出后攻击机解 bootkey

不导出

,活体读 Lsa class

导出次数

3 次

2 次

攻击机

必须 Windows

任意(解密不占攻击机资源)

字符串

明文裸奔

hex 低熵 + volatile 解码,全部清零

如何使用?

只需一个 EXE,以 SYSTEM 运行一次。

1. 把 hashdump.exe 投到目标机
2. 以 SYSTEM 权限运行(非 SYSTEM 会明确报错退出,不给空结果)
3. stdout 直接打印 hash 行
4. 临时 .reg 用后自删(--keep 可保留取证)
40465904-af7f-4b55-990b-5ad82d2c3ce2.png

目标机拉起 SYSTEM 的常用姿势:


# PsExec
PsExec64.exe -s -accepteula hashdump.exe
 
# 计划任务(stdout 重定向落盘再取回)
schtasks /create /tn hd /tr "cmd /c hashdump.exe > C:windowstemph.txt" /ru SYSTEM /sc once /st 00:00
schtasks /run /tn hd && schtasks /delete /tn hd /f

为什么必须 SYSTEM? Vista 后 SAM/SECURITY 对普通管理员都不放读,管理员跑 export 拿到的是空文件。工具检测到导出过小就明确退出——不会给你一份"看起来成功其实空"的结果。

输出长什么样?

# SAM 本地账号(pwdump 格式)
Administrator:500:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
 
# 缓存域凭据(secretsdump 的 $DCC2$ 格式)
corp.local/alice:$DCC2$10240
  • DCC2 行对应 hashcat -m 2100,离线即可爆破,无需回连域控

  • stdout 直接可与 impacket secretsdump.py 对 diff,验证零成本

技术原理

整条解密链与 impacket/secretsdump.py 常量、偏移逐行对齐,可看作"把 secretsdump 从 hive 读取换成 .reg 文本读取,再内联进一个 exe"。

Bootkey —— 不用导 SYSTEM 了

bootkey 就躺在 HKLMSYSTEM...Lsa{JD,Skew1,GBG,Data} 四个键的 class 字符串里(32 个半字节按固定置乱表排布)。class 字段进不了 .reg,所以这步活体读注册表——但只读 class,无敏感内容出进程,也就没有"读 SAM"那种特征。按表反置乱即得 16 字节 bootkey。

SAM 账号 —— F 解锁、V 记账、DES 落地

  • DomainsAccount 的 F 值存着加密的 hashed boot key:老式 rev1 走 MD5(salt+qwerty+boot+digits) 派生 RC4 流(带校验和防 bootkey 读错);Vista+ rev2 走 AES-128-CBC

  • 每个账号在 Users<RID>V(固定 0xCC 头)里记自己的加密 NT/LM:新式 AES、旧式 RC4(MD5(hboot+RID+"NTPASSWORD�"))

  • 最后 16 字节再按 RID 派生两个 DES 密钥拆两半解一遍——RID 参与密钥派生,防互相套用

缓存域凭据 —— 三级派生一把 AES

  • PolEKList:按 SHA256(bootkey + salt×1000) 派 AES-256 key,解出 LSAKey

  • 再按 SHA256(LSAKey + salt×1000) 解出 NL$KM,取后 16 字节当缓存记录 AES key

  • 每条 CacheNL$N 用自己的 IV 做 AES-128-CBC 链式解密,明文前 16 字节即 hash,后按"用户名长度+pad → 短域 pad → 域名字节"布局抠出 domain/user

  • 迭代数看 NL$IterationCount(>10240 对齐 4096 倍数,否则 ×1024,缺省 10240)——就是 $DCC2$ 里那个数

一个吃细节的坑:impacket 对 LSA 类密钥的 AES 语义是分裂的——全零 IV 逐 16 字节块独立解密,非零 IV 才跨块链式。V1 的 AES-CBC 两套语义都复刻了,分别伺候 PolEKList/NL$KM(逐块)和缓存记录(链式),少一边解出来都是花屏。

加密原语零依赖:MD5/SHA-256/RC4/DES/AES-128-CBC 全纯 C 自实现、静态链接,不引 libcrypto——省体积,也少 crypto.dll 导入特征。

免杀/查杀效果实测

设计上收敛的检测面

层面

分析

敏感读取

全程 reg.exe export(签名白程序),无自有代码直开 SAM 句柄

行为链

导出后仅"读自己 .reg + CPU 计算",无第二次敏感动作,时间线干净

静态字符串

全部 hex 低熵编码 + volatile 解码,strings 无明文;.rdata 无高熵字节块

进程观感

约 94KB 静态去符号、带自洽版本资源,不加壳不花指令,无 QVM 高熵命中

样本指纹

XORKEY 一次一换,产物逐版字节不同,云查旧样本哈希失效

进程形态

reg.exe 以 CREATE_NO_WINDOW 静默拉起,观感像后台维护任务

反面要承认的:字节层/行为层设计 ≠ 落地实锤。进程名、导出文件名、hh8_ 目录名是稳定可查的行为痕迹(--keep 还会把 .reg 留在 %TEMP%),杀软完全能对"reg export + XOR + 起 reg.exe"这类组合做规则化;360 主防盯上"SYSTEM 下读 bootkey + 导 SAM + 本地解密"这套真实行为时,任何做同样事的工具都难独善其身。所以正确用法是:产物当单次使用消耗品,投递层再做一次变异(loader 基建管这层)——不是能长期驻留的东西。本工具侧能做的,是把"看起来像一个 HashDump"这件事做到最小。