欢迎回到“威胁检测与搜寻建模”博客系列。前面我们讨论了操作和操作序列,我称之为操作路径。本文将探讨这样一种想法,即任何给定技术或子技术都必须有一个操作路径,但可以有很多。当存在许多操作路径时,可以将这些路径组合成一个操作图,表示攻击者可用于执行技术或子技术的不同操作序列。本文将探讨这一概念,并研究攻击者实施替代操作路径以逃避某些检测或预防控制的真实示例。如果我们了解操作选项以及攻击者可能更喜欢一条路径而不是另一条路径的原因,我们就可以更好地预测我们可以预期看到的变化类型。我希望您喜欢这篇文章,并且一如既往,我希望得到反馈和/或有关我在本系列中提出的想法的任何讨论。
本系列的第三篇文章介绍了操作的概念。操作作为目的论等效函数的容器。换句话说,如果两个函数提供相同的输出(例如,生成进程的句柄),则它们将完成相同的操作(在本例中为进程访问)。
本文比较了 Mimikatz 和 Dumpert 这两个工具,以演示操作的有用性。Mimikatz(特别是命令)进行三个函数调用以完成 OS 凭据转储技术。这些函数调用是 、 和 。下面的图 1 显示了此函数调用序列:sekurlsa::logonPasswords
ntdll!NtQuerySystemInformation
kernel32!OpenProcess
kernel32!ReadProcessMemory
相关视频教程
恶意软件开发(更新到了144节)
另一方面,Dumpert 实现了相同的结果,从 LSASS 内存中读取凭据,同时使用与 Mimikatz 不同的三个函数调用。Dumpert 使用的函数调用是 、 和 。Dumpert 进行的函数调用顺序如下图 2 所示:syscall!NtQuerySystemInformation
syscall!NtOpenProcess
dbghelp!MiniDumpWriteDump
本文继续演示如何使用抽象操作代替特定函数来描述完成操作系统凭据转储:LSASS 内存的可能功能组合。图 3 中所示的操作顺序适用于 Mimikatz 和 Dumpert 的实现。
每个操作都充当函数调用图的抽象层。因此,Process Enumerate 操作存在函数调用图,Process Access 操作存在函数调用图,Process Read 操作存在函数调用图,我们尚未考虑的操作存在更多函数调用图。
在介绍操作的原始文章中,我们利用当前对相关函数调用图(进程枚举、进程访问和进程读取)的理解来计算此操作序列可能的功能组合数。我们计算出,这单个操作序列包含 192 个可能的函数变体(4 个进程枚举函数、6 个进程访问函数和 8 个进程读取函数),这些函数可以组合起来完成此任务。
然而,在第三篇文章中,我们讨论了将新信息集成到现有函数调用图中,以扩展我们对每个操作区域的理解。此集成将 Process Enumerate 函数调用图中表示的函数数从 4 个增加到 21 个函数。使用下面图 4(进程枚举)、图 5(进程访问)和图 6(进程读取)中所示的函数调用图,我们可以将此单个操作序列中包含的功能变体数重新计算为 1,008(21 个进程枚举、6 个进程访问和 8 个进程读取)。
通过抽象的力量,我们可以将 1,008 个独特的变体(在功能级别)表示为更高抽象级别(操作级别)的一个变体。
当我在这篇文章中处理不同的例子时,我将解释我对作者为什么做出某些“交易”决定的解释——以 Dumpert 为例。
值得重申的是,Outflank 团队决定使用不同函数调用的主要原因之一,特别是替换 是因为他们注意到许多 EDR 供应商专注于检测此攻击的进程访问操作。然而,他们也发现,许多传感器只监控更肤浅的层,即 Win32 API 函数,而不是系统调用,即使这些函数在功能上是等效的。
kernel32!OpenProcess
syscall!NtOpenProcess
OpenProcess
NtOpenProcess
在本系列的最新文章中,我们介绍了复合函数的概念。与简单函数(如 或)不同,它只执行一个且一个操作;复合函数是执行多个操作的单个函数。在我们探索的 的情况下,执行进程访问和进程读取。由于它执行执行 OS Credential Dumping 所需的三个操作中的两个:LSASS Memory,因此我们可以想象一个新的伪操作路径,如下图 7 所示。kernel32!OpenProcess
kernel32!ReadProcessMemory
kernel32!Toolhelp32ReadProcessMemory
Process Enumerate -> Toolhelp32ReadProcessMemory
由于伪操作路径,可能有 21 个额外的变体。添加此伪操作路径后,计数达到 1,029 个可能的功能变体。Process Enumerate (21) -> [Process Access + Process Read] (1)
必须认识到,由于包含两个操作,因此它仅是包含此子序列 () 的任何操作序列的有效选项。因此,尽管进程访问和进程读取操作的函数调用图都包含该函数,但对于使用另一个操作路径之一的所有操作路径,它并不是一个有效的选择。例如,我们可能发现未来的操作路径具有子序列(可能类似于过程注入)。因此,对于此操作路径,这不是一个有效的选项。Toolhelp32ReadProcessMemory
Process Access -> Process Read
Toolhelp32ReadProcessMemory
Process Access -> Process Write
Toolhelp32ReadProcessMemory
如果技术或子技术(如 OS 凭据转储:LSASS 内存)包含的不是一个,而是可能包含多个有效的操作路径,该怎么办?我们已经在 Dumpert 中看到,攻击者可以在功能分辨率级别上改变他们的选择以逃避检测,但是如果他们也可以在操作层面上改变他们的交易技巧呢?这篇博文探讨了操作变化的一些示例。它解释了我们如何将其整合到我们的地图中,以及当我们努力增加对这种特定技术和工艺的理解时,这对我们意味着什么。
在第 2 部分中,我们讨论了攻击者(如 Outflank 的团队)如何进行功能更改以逃避检测,但我从未考虑过在操作层可以进行更改。考虑到当时还不存在业务层的概念,谁能怪我呢?但是,我确实知道一篇博客文章,其中提出了一种略微修改的方法来转储凭据,我知道我必须将其集成到模型中。尽管如此,我还是遇到了困难,直到我意识到这种方法可能提供了另一种操作路径。下一节将介绍 James Forshaw 的这种方法,以及我在弄清楚这一点后发现的其他几个替代方案。
如上一段所述,识别 OS 凭据转储的 1,029 个功能变体感觉很棒:LSASS 内存。不过,我知道James Forshaw写了一篇博客文章(在下面分享),介绍了一种方法,该方法允许他绕过Microsoft在Windows 10中添加到LSASS的SACL。
www.tiraniddo.dev
SACL 专门针对进程访问操作。尽管如此,James发现它只报告了请求中包含访问权限的情况(他在帖子中有更多细节,所以请查看)。结果,James 发现他可以打开 LSASS(进程访问)的句柄。但是,他可以仅使用访问权限打开它,这不会触发 SACL。然后,James 可以对 NtDuplicateObject 使用一个技巧,其中应用程序可以派生对进程的完全访问句柄,在本例中为 LSASS。然后,他可以在不触发 SACL 的情况下读取这个新的“重复”句柄。James 在他的 POC 中使用的一系列函数如下图 8:PROCESS_VM_READ
PROCESS_DUP_HANDLE
注意:James 在博客文章中的示例没有指定他使用哪个函数来枚举 LSASS 的进程标识符或从生成的句柄中读取,因此我在创建图 8 时采取了一些自由。它们应该代表他可能或可能使用的功能。
这一系列的函数调用带来了两难境地。四个函数和三个操作的序列之间怎么会有一致性?起初,我选择忽略它,但它一直困扰着我。最终,我意识到这是另一种操作路径。James的方法遵循了操作路径。请注意,这与操作路径非常相似,但插入了句柄复制操作以绕过 SACL。这个小小的变化可能是可见性和不可见性的区别。此方法可避免任何依赖于 LSASS SACL 的检测策略。图 9 显示了 James 方法引入的操作路径。Process Enumerate -> Process Access -> Handle Copy -> Process Read
Process Enumerate -> Process Access -> Process Read
在意识到多个操作路径是可能的之后,我们应该尝试找到尽可能多的替代操作路径。一篇有趣的文章
比尔·德米尔卡皮
描述了一种不同的规避方法。在这种情况下,Bill 担心防病毒产品子集会过滤进程句柄请求的访问权限,尤其是对 LSASS 等敏感进程的访问权限。他提到,在13种可能的访问权限中,有9种经常被过滤,因此在面对这些产品时是不可靠的。这种限制使他开始了探索剩余四个访问权限中的任何一个是否有助于实现某些目的的旅程,例如从 LSASS 进程中转储凭据。下面是他描述其方法的博客文章的链接:
billdemirkapi.me
Bill 发现,大多数产品都会保留未经过滤的访问权限。此访问权限允许调用方使用生成的句柄创建进程。对于任何使用过父进程欺骗的开发人员来说,这种访问权限可能听起来很熟悉,正如 Bill 在他的帖子中详述的那样。Bill 最终发现,这种访问权限允许创建一个称为分叉的子进程。具体而言,当一个进程被分叉时,分叉会继承分叉(父)进程 LSASS 的专用内存区域的句柄。PROCESS_CREATE_PROCESS
分叉 LSASS 进程允许 Bill 从 LSASS 转储凭据,以及其他令人兴奋的技术。很酷的是,即使目标具有防病毒或 EDR 产品,如果它们允许句柄具有访问权限,则可以过滤对敏感进程的句柄请求,攻击者也可以创建 LSASS 的分支,并最终通过分支进程读取凭据,而无需获得对 LSASS 的“读取句柄”。PROCESS_CREATE_PROCESS
此方法提供了第三种操作路径。此路径包括 的以下操作序列,如图 11 所示:Process Enumerate -> Process Access -> Process Create -> Process Read
本文中要探索的第三个也是最后一个替代操作路径是由 Matteo Malvica 和
b4rtik 发现并撰写的。在下面分享的博客文章中,Matteo 提到他和一位同事在从 LSASS 转储凭据时遇到了问题,而没有触发内置的 Windows Defender 高级威胁防护警报。即使是像 Dumpert 这样的工具似乎也没有绕过警报,因此他们不得不寻找解决方案。有趣的是,此警报似乎针对的是进程读取操作,而不是许多其他产品所针对的进程访问操作(我们将保留一些很好的讨论,以备将来的文章中讨论应将哪个操作用作检测或预防控制的基础)。那么他们做了什么呢?
www.matteomalvica.com
他们最终发现了一个名为 的函数,该函数允许他们创建进程的快照,然后从快照而不是进程本身读取。这样,专注于 LSASS 的进程读取的检测控件会错过该行为,因为此方法对 LSASS 的快照执行进程读取操作,而不是对 LSASS 进程执行。为此,示例代码项目 ATPMiniDump 调用 、 、 和 。下面的图 12 显示了此函数调用序列:PssCaptureSnapShot
ntdll!ZwQuerySystemInformation
ntdll!ZwOpenProcess
kernel32!PssCaptureSnapshot
dbghelp!MiniDumpWriteDump
然后,我们可以将这些函数抽象为新操作路径为 的操作。ATPMiniDump 演示了我们将在本博客文章中介绍的第四个也是最后一个操作路径,如图 13 所示。Process Enumerate -> Process Access -> Snapshot Create -> Process Read
现在,开发人员可以使用四种已知的操作路径来执行 OS 凭据转储:LSASS 内存子技术。至关重要的是,我们不要假设这四条路径代表了所有可能的操作路径。相反,它表示已知的操作路径。也就是说,我们现在可以将四个单独的操作路径组合成一个操作图,如图 14 所示:
如果一个操作路径 有 1,029 个功能变体,则想象一下此图中的四个操作路径表示了多少个功能变体。请记住,每个操作都是函数调用图的抽象。为了执行此计算,我们首先需要为新操作(Handle Copy、Process Create 和 Snapshot Create)创建函数调用图,分别如图 15、图 16 和图 17 所示。Process Enumerate -> Process Access -> Process Read
注意:请注意,向 Snapshot Create 函数调用图添加了“部分”标签。这是因为这是一个需要进一步分析的复合函数;但是,这种分析可能会分散本文重点的注意力(存在多个操作路径,这些路径可以组合成一个操作图),因此本文将其视为一个简单的函数。
PssCreateSnapshot
使用这些新的函数调用图,我们现在可以计算操作图中每个操作路径的功能变化数。为了证明这一点,我们将使用操作名称的简写缩写。以下是图表中包含的操作列表及其相关缩写:
句柄复制(图 15):有六 (6) 个功能变体HC
Process Access(图 5):有六 (6) 种功能变体PA
Process Create(图 16):有二十八 (28) 个功能变体PC
Process Enumerate(图 4):有二十一 (21) 个函数变体PE
Process Read(图 6):有八 (8) 个功能变体PR
Snapshot Create(图 17):有四 (4) 种功能变体SsC
然后,我们可以计算每个操作路径所代表的功能变化的数量。
直接内存访问PE (21) x PA (6) x PR (8) = 1,008
工具帮助32ReadProcessMemoryPE (21) x [PA -> PR] (1) = 21
重复令牌PE (21) x PA (6) x HC (6) x PR (8) = 6,048
进程分叉PE (21) x PA (6) x PC (28) x PR (8) = 28,224
进程快照PE (21) x PA (6) x SsC (4) x PR (8) = 4,032
通过将每个操作路径的变体总数相加,我们可以找到 OS 凭据转储:LSASS 内存子技术的四个操作变体中总共有 39,333 个功能变体。
注意:当然,这是基于本文中共享的操作图和函数调用图中显示的当前知识。我们应该假设这只是总数的一个子集,但至少我们有一些切实的东西可以开始。
本文扩展了第 2 部分中提出的“操作”概念。最初的假设是,每种技术或子技术都有一个且只有一个操作序列。在第 2 部分中,我们看到 Mimikatz 使用的序列是 。尽管如此,在这篇文章中,我们探讨了三种替代方法/工具,它们不符合我们先入为主的观念,即只有一条有效的操作路径。具体来说,我们发现James Forshaw能够在序列中插入一个额外的操作,以逃避特定的检测方法。我们在另外两个示例中重复了该过程。sekurlsa::logonPasswords
PE -> PA -> PR
关键的一点是,在分析的操作层面上,单个技术或子技术可以有许多有效的操作路径,我们可以将其绘制成操作图。然后我们知道,每个操作都是底层函数调用图的抽象,该图充当完成操作的不同功能选择的映射。我们假设,我们可以通过乘以每个函数调用图中的入口点数量来计算给定操作路径的功能变化数量。例如,Mimikatz 和 Dumpert 遵循直接内存访问操作路径,该路径使用进程枚举、进程访问和进程读取操作。如果 Process Enumerate 有 21 个函数入口点,Process Access 有 6 个,Process Read 有 8 个,则此操作路径的功能变体总数为 1,008。我们可以将每个操作路径的功能变化数相加,以计算子技术的功能变化总数。令人兴奋的是,根据此计算,在功能级别上,OS Credential Dumping:LSASS Memory 子技术总共有 39,333 种变体。但是,这些变体在操作级别上只能抽象地表示为四个唯一的变体。21x6x8
我们将以这一观察结束。当我们谈论攻击实例时,也许我们正在分析违规报告;我们可以在战术层面考虑它,“我们观察到攻击者使用了凭据访问策略。我们可以在技术层面上考虑它,“我们观察到攻击者使用了操作系统凭据转储技术。我们可以在子技术级别考虑它,“我们观察到攻击者使用了 LSASS 内存子技术。我们可以在操作层面考虑它,“我们观察到攻击者使用了直接内存访问()”。或者,我们可以在功能层面考虑它,“我们观察到攻击者调用了 、 和 ”。问题是,“对于手头的任务来说,哪个级别的分析是最合适的?PE -> PA -> PR
syscall!NtQuerySystemInformation
syscall!NtOpenProcess
dbghelp!MiniDumpWriteDump
二进制漏洞课程(更新中)
windows网络安全防火墙与虚拟网卡(更新完成)
windows文件过滤(更新完成)
USB过滤(更新完成)
游戏安全(更新中)
ios逆向
windbg
还有很多免费教程(限学员)
更多详细内容添加作者微信