Dumping Lsass Process Memory In Different Ways

seventeen 2022-11-10 09:50:00

0x00 前言

Dumping Credentials from Lsass Process Memory在内网渗透流程中起到不可忽视的作用。本文将从源码以及对抗杀软的角度对几种仍然有效的方法进行分析。

前阵子在github发了一个Dump Lsass进程的工具seventeenman/CallBackDump,该项目内利用了回调的方式Dump Lsass进程,暂时满足了过国内杀软的需求(核晶还需要加以小修改),在本文中也会提到如何对该项目加以改造。

0x01 Dump Hash痛点

Dump Hash主要分为两步,第一步是把目标机器上的Lsass进程转储下来,第二步便是解密Lsass进程的转储文件。因为第二步可以在本地机器上处理,所以我们将重点放在第一个步骤上。

  1. 首先的是如何获取lsass进程句柄,当你写完代码在国内环境下完美地dump hash,放到国外或者核晶的环境下就会发现连lsass进程句柄都拿不到,根本到不了后面dump的操作的免杀。

  2. 其次,便是如何将lsass进程转储下来,这涉及到两个动作,一个是dump,另外一个是保存到文件。比如defender对保存出来的文件检测比较严格,只要检测到你dump出来的是lsass进程,一般都会直接处置。

在实际测试中发现,不同杀软对这些操作的敏感度不同,所以我目前最佳的方案是针对不同杀软制定不同的dump方案。

当然,本文并不涉及到开启PPL以及国外EDR挂钩加密Lsass进程这两种情况。

0x02 获取Lsass进程句柄方案

dump lsass进程最常见的一种方法是先通过OpenProcessPROCESS_QUERY_LIMITED_INFORMATION|PROCESS_VM_READ权限来获取lsass进程句柄。但这种方法与直接调用MiniDumpWriteDump的行为一组合就变得很敏感。

那么先来看一下OpenProcess这个api,这里需要关注第一个参数为对进程访问的权限,第三个参数为进程PID。

2022-11-04-03-16-08-image.png

这里重点来回顾一下第一个参数Process Security and Access Rights

  1. PROCESS_ALL_ACCESS: 进程的所有可能访问权限。
  2. PROCESS_CREATE_PROCESS: 需要创建一个进程。
  3. PROCESS_CREATE_THREAD: 需要创建一个线程。
  4. PROCESS_DUP_HANDLE: 需要使用 DuplicateHandle 复制句柄。
  5. PROCESS_QUERY_INFORMATION: 需要检索有关进程的一般信息,例如其令牌、退出代码和优先级。
  6. PROCESS_QUERY_LIMITED_INFORMATION: 需要检索有关进程的某些有限信息。
  7. PROCESS_SET_INFORMATION: 需要设置有关进程的某些信息,例如其优先级。
  8. PROCESS_SET_QUOTA: 需要使用 SetProcessWorkingSetSize 设置内存限制。
  9. PROCESS_SUSPEND_RESUME: 需要暂停或恢复进程。
  10. PROCESS_TERMINATE: 需要使用 TerminateProcess 终止进程。
  11. PROCESS_VM_OPERATION: 需要对进程的地址空间执行操作(VirtualProtectEx、WriteProcessMemory)。
  12. PROCESS_VM_READ: 需要在使用 ReadProcessMemory 的进程中读取内存。
  13. PROCESS_VM_WRITE: 需要在使用 WriteProcessMemory 的进程中写入内存。

在下面的方法中将会对上述权限利用方式以及效果进行分析。

NtDuplicateObject间接获取句柄

大概的一个过程如下:

  1. 先通过NtQuerySystemInformation来获取所有进程打开的句柄。

  2. 使用具有PROCESS_DUP_HANDLE权限的OpenProcess打开进程。

  3. 使用NtDuplicateObject来获取远程进程副本。

  4. 再通过NtQueryObject来获取句柄的信息。

  5. 最后通过QueryFullProcessImageName显示进程完整路径加以判断。

首先是通过NtQuerySystemInformation检索SystemHandleInformation(16)获取所有进程的句柄。

2022-11-05-00-57-35-image.png

这里用PSYSTEM_HANDLE_INFORMATION结构体存放所有进程句柄的信息,所以我们遍历这个结构体即可。

2022-11-06-12-00-06-image.png

接着,遍历刚刚获取到的进程句柄,通过NtDuplicateObject来Duplicate句柄。

2022-11-05-01-13-55-image.png

最后通过NtQueryObject获取进程的信息,使用QueryFullProcessImageName获取进程的完整路径判断是否为lsass进程的句柄,如果是则返回,否则继续遍历句柄。

2022-11-05-01-16-13-image.png

这个思路很不错,间接性地获取lsass的进程,避免了直接获取lsass进程的句柄,让我们来测试一下实际免杀效果怎么样。

普通的defender处理好还是随便过的,defender主要的问题还是转储出来的文件一定要经过加密,不能就单纯的dump出来。(具体处理方法见0x03)

2022-11-04-20-23-40-image.png

360企业安全云也是没啥问题。

2022-11-04-20-30-36-image.png

来试一下黑化了的核晶360,可以看到核晶是懂拦截的。(这个已允许不是我点的哈,自己可以测一下,它自己允许的不关我事情。从这里看得出来360也是考虑了一下要不要拦截)

2022-11-04-20-42-09-image.png

可惜PROCESS_DUP_HANDLE权限有点招摇。像核晶这种杀软在OpenProcess检测比较严格,会阻止我们对lsass进程做一些操作,在api中请求的权限会直接影响到能否获取到lsass进程句柄,但是否OpenProcess我们就彻底无法利用来获取句柄呢?

CreateProcessEx获取进程句柄

相比于PROCESS_VM_READPROCESS_DUP_HANDLE这类权限,实际上PROCESS_CREATE_PROCESS权限并不是很敏感。

PROCESS_CREATE_PROCESS权限有什么用呢?实际上我们拿到这个权限的句柄就可以代表此进程创建子进程,PPID Spoofing也是应用了此技术来创建子进程。

ForkPlayground/ForkLib.cpp的项目中利用该技术,首先是调用OpenProcessPROCESS_CREATE_PROCESS权限打开lsass进程句柄。

2022-11-04-20-57-44-image.png

接着在NtCreateProcessEx中的参数SectionHandle中传递NULL(内存区对象直接继承自父系统进程),在ParentProcess参数中传递目标进程的PROCESS_CREATE_PROCESS句柄,就会得到远程进程lsass的handle。

2022-11-04-21-03-45-image.png

让我们来测试一下这种获取句柄的效果如何?我们可以看到这位懂哥又不懂了,lsass进程直接被dump下来了。

2022-11-04-20-53-11-image.png

这里顺带说一下,PssCaptureSnapshot这个API实际上也是在做这个操作。

2022-11-05-00-42-00-image.png

再者,RtlCloneUserProcess这个API同样也能实现相同的功能。

2022-11-04-21-50-23-image.png

因为考虑到免杀性的原因,所以在我的CallBackDump/main.cpp项目里并没有对获取进程句柄做过处理的代码,只放了第一版朴实无华的PROCESS_ALL_ACCESS代码给师傅们改造。但是在国内的对抗环境下,其实除了核晶也没有什么问题。

0x03 检测lsass转储文件

CallBack加密转储文件

这里利用的是MiniDumpWriteDump的回调API,该API主要用来扩展MiniDumpWriteDump的功能。

2022-11-04-21-06-07-image.png

CallbackType用来判断何时回调执行我们自定义的代码段,以下是微软文档的说明。

2022-11-04-21-19-15-image.png

IoStartCallback中将Status成员设置为S_FALSE,其目的是让每次涉及IO操作都经过回调。在IoWriteAllCallback中,我们将每一部分的lsass进程都写入到申请的堆内存中,以便我们加密后再存储到文件中。

2022-11-04-21-00-28-image.png

相比于重写MiniDumpWriteDump,这种方法会简单很多,但也因为回调的方式一些利用无法扩展。

到此为止,国内的已经处理差不多了,接下来看看稳重的卡巴斯基。

0x04 直接利用系统白操作

在我的测试中,对于卡巴斯基这种分配受限组的杀软,dump lsass进程最好是用白操作。

SilentProcessExit机制dump内存

通过RtlReportSilentProcessExit与Windows错误报告服务(WerSvcGroup下的WerSvc)通信,由WerFault.exe来转储lsass进程。

值得注意的是该方法获取lsass进程句柄方法不能随意更改,更改完权限后会发现dump不出lsass进程。

代码可以参考此处RedTeamTools/windows/LsassSilentProcessExit,具体代码其实就只有改注册表以及调用RtlReportSilentProcessExit,其余的就还是一些细节的处理,这里就没什么好讲的了。

需要动注册表,实际测试中对卡巴斯基个人版有效,360会直接报毒镜像劫持。

2022-11-05-02-50-47-image.png

有一个非常有意思的点,如果你在defender环境下利用该方法去dump lsass进程。你会发现第一次成功了,并且defender反复的来回报毒和加白,这种方法会让defender犹豫不决,过个几秒就正常了,并且也没有被杀。但在第二次dump的时候,defender就会直接杀。

2022-11-05-16-59-26-image.png

此外还有一种通过ALPC模拟正常lsass异常信息的方法,但需要模拟system权限并且也是需要动注册表。不过相比之下优点就是windows日志记录不会记录模拟的exe,只会记录lsass自己的操作。(但这种方法在卡巴环境测试之中dump不出lsass,在defender环境下直接转储原文件落地反而没什么问题,个人觉得实战效果不会很理想)

这种方法具体的原理利用可以看这个DEF CON 30 presentations/Asaf Gilboa - LSASS Shtinkering Abusing Windows Error Reporting to Dump LSASS.pdf

Lsass SSP自加载

这种方法的好处主要是让lsass自己加载一个dll,让我们dump lsass进程的操作变成lsass自己的操作。

关键代码如下,通过AddSecurityPackage加载一个dll,需要注意的是dll的路径要是绝对路径。之前xpn通过rpc调用AddSecurityPackage的项目师傅们感兴趣可以改改。

#define SECURITY_WIN32

#include <stdio.h>
#include <Windows.h>
#include <Security.h>
#pragma comment(lib,"Secur32.lib")

bool loadersth(IN LPSTR ssp_path) {

    wchar_t ssp_path_w[MAX_PATH];

    mbstowcs(ssp_path_w, ssp_path, MAX_PATH);

    SECURITY_PACKAGE_OPTIONS spo = { 0 };

    NTSTATUS status = AddSecurityPackageW(ssp_path_w, &spo);

    if (status == SEC_E_SECPKG_NOT_FOUND)
    {
        return true;
    }
    else
    {
        return false;
    }
}

可以看到这里调用了AddSecurityPackage,那么能不能通过EnumerateSecurityPackages枚举呢?在我本地测试中,发现不管在dllmain中return true还是false,通过EnumerateSecurityPackages都没有查询到我们的ssp。

#define SECURITY_WIN32

#include <stdio.h>
#include <Windows.h>
#include <Security.h>
#pragma comment(lib,"Secur32.lib")

int main(int argc, char** argv) {
    ULONG packageCount = 0;
    PSecPkgInfoA packages;

    if (EnumerateSecurityPackagesA(&packageCount, &packages) == SEC_E_OK) {
        for (int i = 0; i < packageCount; i++) {
            printf("Name: %s\nComment: %s\n\n", packages[i].Name, packages[i].Comment);
        }
    }
}

2022-11-05-16-42-54-image.png

这里也可以监控一下dump dll是怎么被lsass加载的。

2022-11-05-16-28-15-image.png

在我的测试中发现这里并没有RegSetValue的行为,所以xpn文章里指的应该是注册mimilib.dll为ssp的过程才会去注册表注册。

2022-11-05-16-40-13-image.png

这里其实做一些免杀上的处理也是可以直接过卡巴斯基的。

2022-11-05-03-31-42-image.png

在上面提到CallBackDump一些操作会有问题,其实就是我在利用ssp自加载CallBackDump的时候出现了问题。我不确定问题是否由回调引起的,但目前我可以十分肯定的是电脑重启是因为ssp加载了CallBackDump,就算使用了debugview也没有收到异常是什么。

0x05 Protected Prcoess Light

这里顺带提一下这个PPL,上述的免杀方案针对于没有开启PPL的情况下,那么我们如何检测目标机器是否开启PPL呢?

开启PPL

首先将HKLM\SYSTEM\CurrentControlSet\Control\Lsa\RunAsPPL的值改为1,重新启动。

开启与关闭的方法参考Configuring Additional LSA Protection | Microsoft Learn

(谨慎开启,我的卡巴环境开了不知道为什么关不了)

2022-11-05-02-07-58-image.png

检测开启PPL

检测开启PPL过程主要是:

  1. 使用OpenProcess以PROCESS_QUERY_LIMITED_INFORMATION权限打开lsass进程。

  2. 再通过GetProcessInformation获取ProcessProtectionLevelInfo信息。

具体的代码可以参考PPLdump/utils.cpp

2022-11-05-03-22-52-image.png

检测开启PPL这个操作一般杀软不会拦截,如果要保险一点也可以做一些细节处理。

2022-11-05-02-00-33-image.png

0x06 Reference

GitHub - skelsec/pypykatz: Mimikatz implementation in pure Python

GitHub - D4stiny/ForkPlayground: An implementation and proof-of-concept of Process Forking.

Abusing Windows’ Implementation of Fork() for Stealthy Memory Operations

DEF CON 30 presentations/Asaf Gilboa - LSASS Shtinkering Abusing Windows Error Reporting to Dump LSASS.pdf

Dumping Lsass without Mimikatz with MiniDumpWriteDump - Red Teaming Experiments

RedTeamTools/windows/LsassSilentProcessExit at master · lengjibo/RedTeamTools · GitHub

Exploring Mimikatz - Part 2 - SSP - XPN InfoSec Blog

GitHub - itm4n/PPLdump: Dump the memory of a PPL with a userland exploit

评论

seventeen

不参加各类攻防演练以及境内外渗透项目,如溯源到本人id,纯属巧合。

随机分类

软件安全 文章:17 篇
二进制安全 文章:77 篇
Exploit 文章:40 篇
企业安全 文章:40 篇
其他 文章:95 篇

扫码关注公众号

WeChat Offical Account QRCode

最新评论

Article_kelp

因为这里的静态目录访功能应该理解为绑定在static路径下的内置路由,你需要用s

N

Nas

师傅您好!_static_url_path那 flag在当前目录下 通过原型链污

Z

zhangy

你好,为什么我也是用windows2016和win10,但是流量是smb3,加密

K

k0uaz

foniw师傅提到的setfge当在类的字段名成是age时不会自动调用。因为获取

Yukong

🐮皮

目录