
1. 项目概述与背景最近在整理一些历史项目时翻到了一个名为RedSnarf的工具它一度在渗透测试和红队评估中被用于针对Windows环境的凭证提取。虽然这类工具现在可能已经过时或者其利用的漏洞已被修补但研究其源码对于理解Windows安全机制、凭证存储原理以及攻击者或防御者的思维方式依然具有极高的学习价值。今天我就从一个开发者和安全研究者的角度带大家深入拆解一个用Python实现的、模拟RedSnarf核心功能的凭证窃取模块。我们不会讨论任何攻击性用途而是聚焦于技术原理本身看看Windows的凭证是如何被系统管理的以及从编程层面我们如何通过合法的系统接口去访问这些信息——这对于开发安全软件、进行数字取证或加固系统防御都至关重要。简单来说我们要解析的是一个Python脚本它的核心目标是在已经获得相应权限例如管理员权限的Windows系统上通过模拟登录会话、访问安全进程内存或解析本地存储文件等方式提取出当前系统或用户保存的各种凭证如明文密码、哈希值NTLM/LM Hash、访问令牌等。理解这个过程能让我们更清楚地知道凭证在系统中“藏”在哪里以及如何通过配置策略和监控来保护它们。本文适合对Windows内部安全机制感兴趣的安全研究人员、系统管理员以及对Python系统编程有进阶需求的开发者。2. 核心原理Windows凭证存储与访问机制要解析这样一个工具首先必须理解Windows操作系统是如何管理用户凭证的。这并非一个简单的“密码文件”而是一套复杂的安全子系统Security Subsystem核心组件是本地安全机构子系统服务LSASS。2.1 LSASS进程的核心角色LSASSLocal Security Authority Subsystem Service是Windows安全体系的心脏。它负责处理本地安全策略、用户认证以及凭证管理。当你登录一台Windows计算机时输入的密码经过计算后其哈希值通常是NTLM Hash会被加载到LSASS进程的内存空间中。此外为了支持单点登录SSO等功能LSASS还可能缓存用户的明文密码尽管默认情况下高版本Windows已极力避免、Kerberos票据Tickets以及各种访问令牌Tokens。因此从攻击者视角同样从防御和取证视角LSASS进程的内存就成了一个“凭证宝库”。RedSnarf这类工具的传统思路之一便是注入代码到高权限进程或直接以调试权限附加到LSASS进程然后从其内存空间中“挖”出这些凭证数据。我们的Python实现将重点放在通过合法的Windows API来模拟这一过程的关键环节。2.2 凭证的多种形态与存储位置凭证在Windows中并非只有一种形式理解其形态是解析代码的前提哈希值Hash这是最常见的形态。Windows不使用明文密码进行验证而是使用其哈希值。主要有两种LM Hash古老且脆弱基于DES在Windows Vista/7及之后版本默认禁用但在某些旧系统或特定配置下可能仍存在。NTLM Hash目前主用的挑战-响应认证协议使用的哈希。它是MD4哈希算法的结果。即使密码策略要求强密码NTLM Hash在内存中也是存在的。明文密码在某些场景下例如为了兼容旧式应用或特定的凭据管理器Credential Manager条目系统可能会在内存中缓存明文密码。但这需要特定的条件触发并非默认行为。访问令牌Access Token代表一个用户或一组权限的安全对象包含用户SID、所属组、特权列表等。窃取高权限令牌如SYSTEM令牌并复制它可以瞬间提升进程权限。Kerberos票据在域环境中用于域认证的票据TGT, Service Ticket也由LSASS管理。存储位置除了LSASS内存还包括SAM文件存储本地用户账户的哈希值位于%SystemRoot%\system32\config\SAM被系统锁死无法直接读取。注册表一些系统配置和缓存的凭据可能存在于注册表中如HKLM\SECURITY。凭据管理器Credential Manager用户手动保存的网站、网络位置凭据通常被加密后存储在用户配置文件下。我们的Python源码解析将围绕如何通过编程手段与LSASS交互或解析这些存储位置来展开。2.3 Python在Windows系统编程中的优势与挑战为什么用Python来实现Python拥有强大的ctypes库和第三方包如pywin32可以方便地调用Windows原生APIAdvapi32.dll,Kernel32.dll等。这使得Python脚本能够执行许多底层的系统操作而无需编写复杂的C/C代码。然而挑战在于需要精确理解API的参数、结构体和调用约定任何错误都可能导致访问违规Access Violation或无法获取数据。接下来的解析我们会看到大量ctypes与Windows API交互的代码。3. 关键技术点一进程权限提升与令牌操作在尝试访问LSASS或其他受保护资源之前我们的进程必须具备足够的权限。这就是“特权提升”Privilege Escalation在代码层面的体现。3.1 启用调试特权SeDebugPrivilege这是最关键的一步。SeDebugPrivilege特权允许一个进程调试另一个进程这包括了读取/写入目标进程内存的能力。默认情况下只有管理员组成员且以管理员身份运行的进程才能启用此特权。代码解析示例import ctypes from ctypes.wintypes import * # 定义必要的Windows常量和结构体 PROCESS_QUERY_INFORMATION 0x0400 PROCESS_VM_READ 0x0010 TOKEN_ADJUST_PRIVILEGES 0x0020 TOKEN_QUERY 0x0008 SE_PRIVILEGE_ENABLED 0x2 # 获取当前进程令牌 hProcess ctypes.windll.kernel32.GetCurrentProcess() hToken HANDLE() ctypes.windll.advapi32.OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ctypes.byref(hToken)) # 查找SeDebugPrivilege的LUID luid LUID() ctypes.windll.advapi32.LookupPrivilegeValueW(None, “SeDebugPrivilege”, ctypes.byref(luid)) # 准备特权调整结构 class TOKEN_PRIVILEGES(ctypes.Structure): _fields_ [(“PrivilegeCount”, DWORD), (“Privileges”, LUID_AND_ATTRIBUTES * 1)] tp TOKEN_PRIVILEGES() tp.PrivilegeCount 1 tp.Privileges[0].Luid luid tp.Privileges[0].Attributes SE_PRIVILEGE_ENABLED # 调整令牌特权 ctypes.windll.advapi32.AdjustTokenPrivileges(hToken, False, ctypes.byref(tp), 0, None, None)关键点与避坑OpenProcessToken必须包含TOKEN_ADJUST_PRIVILEGES权限才能修改特权。LookupPrivilegeValue用于将特权名转换为本地唯一标识符LUID。AdjustTokenPrivileges是实际启用特权的函数。调用后务必检查GetLastError()即使函数返回非零也可能只是ERROR_NOT_ALL_ASSIGNED需要进一步确认。重要安全提示在脚本结束时应考虑将特权恢复原状这是一个良好的编程习惯。3.2 令牌窃取与模拟Token Impersonation获得SeDebugPrivilege后我们可以打开高权限进程如lsass.exe的令牌然后复制它并用它来模拟该用户。核心步骤打开目标进程使用OpenProcess并指定PROCESS_QUERY_INFORMATION权限。打开进程令牌使用OpenProcessToken获取目标进程的令牌句柄。复制令牌使用DuplicateTokenEx复制该令牌可以创建一个主令牌Primary Token或模拟令牌Impersonation Token。模拟令牌常用于线程临时“扮演”另一个用户。模拟令牌使用ImpersonateLoggedOnUser让当前线程使用复制的令牌。之后该线程执行的操作就具有了目标令牌的权限。实操心得复制令牌时SecurityImpersonation级别通常就足够了。如果需要创建一个全新的进程则需要Primary令牌。模拟完成后务必使用RevertToSelf()函数来恢复线程的原始身份避免权限混乱。在防御侧监控OpenProcess和DuplicateToken等API的调用特别是针对lsass.exe的是检测此类攻击的有效手段。4. 关键技术点二访问LSASS进程内存与MiniDumpWriteDump直接读取LSASS内存来解析凭证结构是复杂且容易因系统版本更新而失效的。更稳定、更经典的方法是让LSASS自己把内存“倒”出来即生成一个内存转储文件Dump File然后离线分析这个文件。这通过MiniDumpWriteDumpAPI实现。4.1 使用MiniDumpWriteDump生成转储这个函数是DbgHelp.dll的一部分。它可以将指定进程的完整或部分内存空间写入一个文件。代码流程解析# 加载DbgHelp.dll dbghelp ctypes.WinDLL(‘DbgHelp.dll’) # 定义MiniDumpWriteDump函数原型 MiniDumpWriteDump dbghelp.MiniDumpWriteDump MiniDumpWriteDump.argtypes [HANDLE, DWORD, HANDLE, ctypes.c_int, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p] MiniDumpWriteDump.restype BOOL # 打开LSASS进程需要PROCESS_QUERY_INFORMATION | PROCESS_VM_READ lsass_pid 找到lsass的PID hProcess ctypes.windll.kernel32.OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_DUP_HANDLE, False, lsass_pid) # 创建转储文件 dump_file open(‘lsass.dmp’, ‘wb’) # 需要一个可写的文件句柄这里简化处理实际需要使用CreateFile API获取文件句柄 hFile ctypes.windll.kernel32.CreateFileW(‘lsass.dmp’, GENERIC_WRITE, 0, None, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, None) # 调用MiniDumpWriteDump # MiniDumpWithFullMemory 标志表示转储完整内存 MINIDUMP_TYPE 2 success MiniDumpWriteDump(hProcess, lsass_pid, hFile, MINIDUMP_TYPE, None, None, None)参数详解与注意事项PROCESS_DUP_HANDLE权限在某些情况下对于MiniDumpWriteDump是必须的。MINIDUMP_TYPE是一个标志位MiniDumpWithFullMemory (0x00000002)确保我们获取完整内存这对于后续提取哈希是必要的。最大的坑从Windows 10 1803RS4开始微软引入了“LSASS保护”LSA Protection。当该功能启用时默认在Windows 10 1809和Windows Server 2019上对某些SKU启用LSASS将以受保护进程Protected Process Light, PPL运行。这意味着即使有SeDebugPrivilege非签名的调试器或进程也无法打开LSASS进行内存读取或转储。这是防御此类攻击的关键机制。绕过PPL需要更高的权限或利用漏洞这已超出普通API调用的范畴。在真实环境中此方法可能因系统加固而失败。4.2 离线解析转储文件获取NTLM哈希获得lsass.dmp文件后我们可以在一个安全的环境下离线分析。常用的工具是mimikatz但我们的Python解析旨在理解原理。本质上我们需要在内存转储中寻找特定的数据结构。简化版解析思路概念性定位LSASS进程的地址空间在Dump文件中解析进程环境块PEB、线程环境块TEB等信息。寻找堆Heap和特定内存区域凭证哈希通常存储在LSASS进程的堆内存中。识别哈希结构在内存中NTLM哈希可能以固定的模式存储例如连续的16字节二进制数据MD4结果。可能需要寻找与之关联的_MSV1_0_PRIMARY_CREDENTIAL等内部结构。关联用户名哈希附近通常会有对应的用户名ASCII或Unicode字符串。需要正确的解析算法来将内存偏移与用户名关联起来。注意直接解析LSASS Dump内存结构极其复杂且高度依赖于Windows版本和补丁级别。在实际的RedSnarf或Mimikatz中这部分是通过逆向工程LSASS模块找到存储凭证的全局变量或函数指针来完成的。Python实现这部分通常不是从头造轮子而是集成现有的解析库如pypykatz来读取Dump文件。一个更实用的Python方法调用本地Mimikatz或使用pypykatzimport subprocess # 方法1调用mimikatz需本地存在 # 这实际上是将核心工作交给了成熟的工具 result subprocess.run([‘mimikatz.exe’, ‘privilege::debug’, ‘sekurlsa::logonpasswords’, ‘exit’], capture_outputTrue, textTrue) # 然后解析result.stdout中的输出 # 方法2使用pypykatz库纯Python import pypykatz import minidump # 读取minidump文件 minidump_file ‘lsass.dmp’ dump minidump.MinidumpFile(minidump_file) # 解析凭据 creds pypykatz.parse_minidump_external(dump) for cred in creds.logon_sessions: for lsa_cred in cred.msv_creds: print(f”User: {lsa_cred.username}”) print(f”Domain: {lsa_cred.domainname}”) print(f”NTLM Hash: {lsa_cred.NThash.hex()}”)实操心得对于安全研究使用像pypykatz这样维护良好的库是更可靠的选择它封装了不同Windows版本的内存结构解析逻辑。直接内存解析代码脆弱且易失效不适合用于需要稳定性的生产或测试环境。生成Dump文件的行为本身会被高级别的EDR端点检测与响应或AV杀毒软件记录和告警。MiniDumpWriteDump调用是一个关键的攻击指标IoC。5. 关键技术点三注册表与SAM文件哈希提取除了内存中的哈希本地用户账户的密码哈希也存储在SAMSecurity Accounts Manager数据库中。SAM文件被系统锁定但我们可以通过访问注册表或卷影复制Volume Shadow Copy等技术来读取它。5.1 通过注册表读取SAM哈希SAM数据库的内容在系统运行时被映射到注册表的HKLM\SAM和HKLM\SECURITY键下。需要SYSTEM权限才能完全访问。操作步骤提升至SYSTEM权限可以通过psexec -s启动一个SYSTEM权限的shell或者利用命名管道模拟等技巧。在代码中有时会用到CreateNamedPipe和ImpersonateNamedPipeClient的组合来获取SYSTEM令牌。读取注册表键使用winregPython标准库或ctypes调用RegOpenKeyEx,RegQueryValueEx等API。解析SAM哈希结构从注册表中读取的是二进制数据需要按照SAM数据库的格式进行解析。这涉及到对V值F值的解析其中包含了用户的RID相对标识符、LM/NTLM哈希如果存在等。简化代码概念import winreg # 需要以SYSTEM权限运行此脚本 try: # 尝试直接打开SAM子键需要极高权限 sam_key winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r”SAM\SAM\Domains\Account\Users”, 0, winreg.KEY_READ) # 枚举用户以RID命名如000001F4对应Administrator for i in range(winreg.QueryInfoKey(sam_key)[0]): rid winreg.EnumKey(sam_key, i) user_key winreg.OpenKey(sam_key, rid) # 读取F值二进制数据 f_value, _ winreg.QueryValueEx(user_key, “F”) # 这里需要复杂的解析逻辑来提取哈希 # … 解析f_value … except PermissionError as e: print(“需要SYSTEM权限:”, e)注意事项直接读取HKLM\SAM在现代Windows上即使有SYSTEM权限也可能受到限制因为该键被LSASS进程以独占方式锁定。更常见的方法是使用卷影复制服务VSS创建一个SAM文件的副本然后离线解析这个副本文件。这涉及到vssadmin或WMIC命令或者调用VSS API。5.2 使用卷影复制VSS获取SAM文件这是一种更高级但更隐蔽的方法因为它不直接接触被锁定的活动文件。基本思路使用VSS API创建一个系统卷通常是C:的卷影副本快照。将卷影副本中的\Windows\System32\config\SAM和\Windows\System32\config\SYSTEM文件复制出来。使用工具如secretsdump.pyfrom Impacket解析这些离线文件获取NTLM哈希。SYSTEM文件包含解密SAM数据库所需的启动密钥Boot Key。Python实现要点概念性调用vssadmin create shadow命令或使用win32com操作VSS COM接口 (IVssBackupComponents)。获取卷影副本的设备路径如\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1。使用shutil.copy或直接文件读取API从该路径复制文件。防御视角监控vssadmin命令的异常使用特别是由非管理应用程序发起的创建和删除操作。监控对\Device\HarddiskVolumeShadowCopy目录下敏感系统文件的读取访问。6. 常见问题、检测与防御实录在复现和研究这些技术的过程中会遇到各种各样的问题同时从防御角度理解如何检测和缓解也至关重要。6.1 实操中常见错误与排查OpenProcess失败错误代码5拒绝访问原因即使以管理员身份运行如果没有成功启用SeDebugPrivilege或者目标进程是受保护进程PPL也会失败。排查检查AdjustTokenPrivileges的返回值并调用GetLastError()。成功启用后错误码应为ERROR_SUCCESS(0)。使用Process Explorer或pslist查看目标进程如lsass的“保护”标志。如果显示“Protected”则说明PPL已启用。尝试使用-v或–debug参数运行你的脚本打印每一步的权限和错误信息。MiniDumpWriteDump成功但生成的Dump文件大小为0或无法解析原因传递给函数的参数不正确特别是文件句柄或者进程权限不足缺少PROCESS_DUP_HANDLE亦或是Dump类型标志不正确。排查确保使用CreateFileAPI获得有效的文件句柄并检查其有效性。确认OpenProcess时包含了PROCESS_DUP_HANDLE权限。尝试使用MiniDumpWithFullMemory之外的更小的Dump类型如MiniDumpWithHandleData进行测试看是否能生成有效文件。解析LSASS Dump时找不到哈希或用户名原因内存解析逻辑与当前系统版本不匹配或者目标系统启用了Credential Guard等高级安全功能将哈希移到了受保护的、基于虚拟化的安全环境如LSAIso进程中传统的LSASS内存里只有占位符。排查检查Windows版本和补丁。考虑使用更新版本的解析库如pypykatz。在系统上运行msinfo32查看“基于虚拟化的安全性”是否处于“正在运行”状态。如果启用了Credential Guard传统的内存读取方法将失效。6.2 攻击指标IoCs与防御策略理解攻击技术是为了更好地防御。以下是从这些技术中提炼出的关键攻击指标和对应的防御建议攻击阶段可能的行为IoCs防御与检测建议权限准备进程尝试启用SeDebugPrivilege、SeTcbPrivilege等敏感特权。启用Windows审计策略如“审核特权使用”使用SIEM或EDR监控特权启用事件事件ID 4672。限制非必要账户的管理员权限。进程访问非系统、非安全软件进程如未知的python.exe尝试以PROCESS_VM_READ等权限打开lsass.exe进程。启用攻击面减少ASR规则“阻止从Windows本地安全机构子系统窃取凭据”。使用EDR监控对LSASS进程的OpenProcess调用。内存转储进程调用MiniDumpWriteDump特别是目标进程是lsass.exe。启用ASR规则“阻止Windows本地安全机构子系统lsass.exe的内存中凭据盗窃”。监控DbgHelp.dll中MiniDumpWriteDump的调用。注册表访问非SYSTEM进程尝试读取HKLM\SAM或HKLM\SECURITY注册表键。强化注册表权限监控对这些敏感注册表路径的访问尝试。文件访问进程访问卷影副本设备路径\Device\HarddiskVolumeShadowCopy下的SAM/SYSTEM文件。监控对卷影副本管理命令vssadmin的异常使用。限制对VSS服务的管理。横向移动使用提取的哈希进行传递哈希Pass-the-Hash, PtH攻击。启用NTLM审计、限制NTLM使用、部署Credential Guard、使用LAPS管理本地管理员密码。个人加固建议启用Credential Guard这是防御内存中凭证窃取最有效的手段之一。它利用虚拟化安全技术将密钥和哈希隔离在安全的容器中。应用最小权限原则日常使用标准用户账户而非管理员账户。仅为特定任务分配临时管理员权限。保持系统更新及时安装安全补丁修复可能被利用的本地提权漏洞。部署高级端点安全产品使用具备行为检测能力的EDR能够识别上述可疑的API调用链和进程行为。研究RedSnarf这类工具的源码本质上是一次对Windows安全底层的深度探索。通过Python去实现其中的关键环节让我对ctypes调用、Windows安全API、进程和内存管理有了更扎实的理解。更重要的是从攻击者视角看清了凭证可能“泄露”的每一个环节这反过来极大地提升了我设计防御策略和进行安全审计时的精准度。技术本身无分善恶关键在于掌握它的人将其用于何处。希望这篇深度解析能帮助你在理解Windows安全机制的道路上更进一步。记住最好的防御始于深刻的理解。