DLL劫持与代理攻击:Shellcode注入新技巧
概述
本次网络研讨会最初于2024年10月4日发布。视频中专家深入探讨了DLL劫持的复杂性及恶意代码代理的新技术,全面讨论了方法论和武器化过程。演讲探讨了这些技术在实际参与中的应用,并解答了近期研究发布中的常见问题。演讲者强调了理解DLL功能及微软安全措施影响的重要性,同时突出了持续渗透测试在发现漏洞方面的有效性。
重点内容
- DLL劫持:利用应用程序信任执行恶意代码,实现权限提升和持久性网络攻击。
- DLL代理攻击:通过操纵文件夹权限加载恶意代码,同时保持原始DLL功能,避免进程崩溃。
- Process Monitor工具:使用微软的Process Monitor识别和操纵DLL,用于低权限用户场景测试。
- Face Dancer工具:自动化DLL劫持,功能包括创建可劫持DLL和针对性攻击,促进持续研究。
- 实际攻击模拟:持续渗透测试团队使用自定义工具和技术(包括虚假Outlook更新)设计真实攻击活动。
- 缓解措施:防御Windows漏洞需重视意识和检测,强调电子邮件过滤器、强控制和钓鱼教育。
完整视频
文字记录
Matthew Eidelberg
是的,本次演讲将讨论DLL劫持以及我研究出的一些新技术。我喜欢称之为通过代理Shellcode的新方法。我们将讨论我发现这些向量的方法、如何将其武器化,并与Michael Allen讨论如何在实战中使用这些技术。因为显然,只谈论研究是不够的,如何实际应用才是关键。上周我们发布了相关文章和研究后,我收到了很多问题,因此本次演讲也旨在解答这些疑问。
这一切都源于我们的持续渗透测试(CPT)团队。正如左上角所示,您可以看到我们的小蝾螈标志。在深入讨论之前,我想先做一些基础铺垫,因为我们会提到许多听起来熟悉但可能令人困惑的术语。不幸的是,DLL和DLL劫持、侧加载等所有术语虽然听起来不同,但功能相似。因此,我将尽力在简短的方法论回顾中突出这些内容,以便大家有一个共同的信息基线,帮助理解无限的可能性以及我开始研究这些技术的初衷。
显然,当我们 tasked 寻找任何新型攻击或面对长期活动时,最重要的是查看野外已知的内容、哪些被捕获、哪些具有高风险,然后逆向工作,寻找未被讨论的新内容或可能 obscure 的内容。我们团队讨论的一个重点是持久性,即创建能够长时间存在的东西。如果您收听了预演闲聊,我们提到在这些CPT操作中,任务可能持续数月、数周甚至半年。因此,我们希望建立非常持久且不会暴露的东西。基于此,这是我 approach 的方法和研究发现。
既然我们来讨论DLL劫持,那么什么是DLL劫持?当我们谈论DLL时,首先必须理解DLL是进程可以加载的函数库,然后可以调用这些函数。最著名的大概是NTDLL,这是Win32的核心。通过它,您可以分配内存、创建文件、保存文件、更改内存段权限等。例如,Excel加载时,会将NTDLL加载到进程中,设置一切,并在其自身进程中为其分配空间。但这并不意味着DLL一旦加载就立即运行,这是它与传统exe文件或可执行文件的不同之处。一旦加载,DLL支持的函数都可以被进程调用,一旦调用,它们就可以执行操作。
当我们谈论DLL劫持时,我们实际上关注的是利用应用程序和DLL之间的信任。我们试图在中间插入自己,以便在尝试调用有效DLL时加载恶意代码。如前所述,持久性。但这些类型的攻击不仅用于持久性。过去几年中有很好的技术,您可以通过某种DLL劫持获得提升的上下文,例如从标准低权限用户提升到高权限甚至NT系统,或者作为部署在特定时间或触发器后运行的东西的绝佳方式,以初始建立代码执行,我们将在用例之一中讨论如何将其武器化用于大型鱼叉钓鱼活动。
但由于其影响巨大,微软已采取许多措施来缩小传统方法的差距,使DLL劫持易于检测且更难利用。这些常见的、越来越难利用的方法列在这里。您有前三名。我想暂停一下,以防有人说,嘿,等等,我习惯做这些攻击。这些是最常见的。至少还有四种其他方法。但我真的想专注于这些知识的基线。第一种是侧加载,即您将DLL放入与进程相同的文件夹中。例如,假设是Excel,您可以将文件放入其中,它会在其m文件夹中寻找这个不存在的奇怪DLL。也许它使用之前的相同示例寻找NTDLL,而不是在系统文件夹中寻找,它在其自身文件夹中寻找,显然失败,然后转到下一个。哦,现在我有正确的路径。通过放入相同文件夹,它会盲目信任并加载我们的DLL。现在有一些注意事项,显然当您这样接管DLL时,必须确保仍然提供那些函数和东西。因为像NTDLL这样的核心DLL不可访问。进程将崩溃,我们不希望进程崩溃,尤其是在隐蔽 stealth 操作中。这 kind of 给我们一种方式,我们无法实现建立立足点的目标。
因此,这是一个问题,已通过确保这些文件夹上的适当权限 slowly closed out。另一个大问题是缺失的DLL,我们可以通过查看和理解进程加载或寻找的内容来利用这一点。有时由于开发生命周期、CIDC管道等原因,在开发过程中他们有额外的DLL没有通过或不再需要。这可能是从一种操作系统到另一种操作系统的遗留移除。也许它们不再存在,但它们仍然寻找它们。在这一点上,我们可以找到那些名称,然后只需放入具有相同名称的DLL,它会盲目信任它。最后一个,虽然仍然普遍,但更难实现的是顺序劫持。我之前在DLL侧加载中提到过,但有时它们会在随机位置寻找有效DLL。如果您能找到顺序并且您可以写入其中一个位置,通过将DLL放入该顺序,您 kind of 跳过了线,突然您的DLL被加载而不是另一个,即使您有相同的名称。只是它首先寻找的地方。如前所述,这些以前很好,但随着时间的推移,开发人员更加专注于确保安全编码方法得到遵守,确保许多这些差距不存在,尤其是在操作系统部分。您有时在第三方软件甚至免费软件中更常见这些。
如果DLL劫持不是常态或容易被捕获,我们真正要做什么?这次演讲是关于什么的?嗯,我喜欢称之为DLL代理攻击。我们真正做的是利用两件事之一。我们将讨论第一个,即文件夹权限。尽管我提到操作系统已采取巨大努力确保一切安全锁定,但至今仍有小部件、插件、附加组件放在允许任何人写入的地方,这很好。但如果我们完全接管那个DLL,进程将崩溃,因为我们不提供其函数。我们如何实现两个目标?如何实现加载和建立我们的代码,同时仍然提供对那些函数和所有核心库的访问?因此术语代理。我们通过将我们的DLL引入相同文件夹来实现这一点。现在,首先,有人可能会说,等等,您不能有两个具有相同名称的DLL。您是正确的。因此,我们实际上重命名原始文件。可以是像 totally legitimate、old 或类似的东西。通过这样做,我们可以将确切函数从我们的恶意DLL映射并指向原始文件。当请求到来时,您会看到函数获取它,并几乎像中间代理攻击者一样传递它。如果您熟悉像 evil Gen X two 或任何那些代理身份验证请求的网络钓鱼工具,我们 kind of 做同样的事情,但是使用DLL函数。
现在有些代理攻击甚至不需要DLL在相同文件夹中。我们将触及这一点。但我刚刚描述的,我有一个很好的视觉来解释它。假设这样,我们劫持 process exe,因此我们的DLL在其中,我们的代码正在运行,但 process exe 突然说嘿,我需要这个函数。假设是 virtual allocate。嗯,我们的DLL将获取该请求,并说等等,这个函数不是我的恶意函数之一,它是一个有效函数。我将去找原始DLL,比如 DLL old,发送该请求给它并加载它,以便它可以实际做需要做的事情,这样进程就不会崩溃。您可能在想,这怎么可能?实际上非常非常容易。并且某种东西是 kind of 原生开发给所有DLL文件的,即定义文件。这些只是可以 dictating 属性或DLL操作的功能文件。因此,可以是像嘿,当您编译此时,我需要此文件具有文件详细信息中的这些属性。甚至在某些情况下,对于二进制文件,您可以说我希望它显示此图像。因此,在我们的案例中,我们实际上做的是定义导出。我们将说,嘿,我们将有此导出,但当您调用它时,我们将说它在此文件中,函数名称,正如您在下面看到的。因此,这是您刚刚附加到您的交易的代码。它非常优雅,非常简单,这允许任何时候这些函数被调用,我们将其传输到,在这种情况下是 one auth legitimate,然后是函数名称。
因此,当我们的恶意DLL加载时,进程永远不会失去任何功能能力。用户从未注意到服务中断, like 东西不可用,进程不崩溃,一切对系统看起来正常,这在操作角度思考保持 under the radar 时也非常好。如果一切 operating,并且很难 tell,那就是我们想要的。那么如何发现这些?嗯,最简单的方法是使用 process monitor。这是一个微软提供的免费工具,它可以监控操作系统中的每个事件。那可能是从加载图像事件到甚至查询说嘿,谁 touched 了一个文件?但它给您很多。就像从消防水管喝水。因此,您真的需要掌握过滤功能。这些是我推荐的过滤器,也是我用来查看的内容。因此,我会寻找加载图像。那是什么意思?那是进程说当它们加载DLL时的实际操作。我寻找的是不在受管理权限保护的地方的DLL。因此显然没有 Windows,没有程序文件。然后我 just let 计算机运行。我可能假装我是一个用户,安装了软件。因此我会假装,哦,要打开Outlook,发送电子邮件,哦,我要做我的电子表格,无论是什么,正常操作,随着事情进展,事件被计算和记录。因此,通过 just looking for 加载图像事件不在这些两个地方,我们可以开始查看哪些进程从其他位置加载DLL。
现在这是启动所有研究的示例,即Outlook。Outlook是所有企业中最常用的工具。它用于电子邮件。我不认为这些天有甚至 rival 它的替代品仍然被企业使用。因此您可以看到在这里,在这个用户低权限下,在 appdata local 中有一个 teams 附加组件被加载,您可以看到一堆DLL just get loaded。关于 app data 文件夹中的任何东西的伟大之处是,您作为用户具有读写权限。因此所有这些DLL我们可以操纵,那意味着我们可以重命名它们,我们可以放入一个新的DLL在那里,这正是我们刚刚描述的需要做的事情以成功。您可以看到在这里在 actual folder 中,我们可以看到低权限用户具有读取、执行、修改、完全控制 over files in here。那是这种攻击成功所需的关键事情。回到您之前看到的,我们只需要做的是然后扫描那个DLL,查看其函数是什么,将它们映射到这个导出文件,重命名旧文件,说 legitimate,然后编译,放入我们的恶意文件。现在下次Outlook运行时,它将加载我们的DLL。在这种情况下,这个 just going to show 一个简单的 hello world from Rust。如果您没有听过或 ever listened to 任何我的演讲或 followed me on Twitter 或 x,我真的是一个大的 rust fan。因此一切都是 rust。因此我道歉。如果您看到 rust 代码,您 like 哦我的上帝, trust me,它是最好的语言。 personally by my take。因此使用以下代码,我们将 kind of see 下次Outlook运行时会发生什么。
因此 instantly 我们得到一个弹出消息,因为它 just loaded。如果我们实际去查看文件夹,我们可以首先看到我们的Outlook,然后 Outlook legitimate,然后我们在下一部分看到那个箭头所在的地方。那是所有DLL, that outlook launch。我知道Outlook有点 cropped out,但那是进程。我们可以看到 one off 在那里,当我们双击它使用 process Explorer 时,我们可以看到从这一点起,事实上所有那些函数都有一个 accl reference 到那个 legitimate one。因此那是它的样子。因此任何时候 startup 被调用,Outlook将说嘿, one off,我需要 startup,RDL将去 ok,startup 在 one off legitimate,传递该请求,这样就没有服务中断,我们的DLL被加载,没有人更聪明。
因此 just by briefly doing this 并且如我在开头提到的,CPT的驱动是我们需要找到跨多个不同客户常见的东西,因为我们 targeting 一大群客户。那意味着,不仅仅是20个用户,而是可能跨20个不同客户的30个用户。因此您必须 kind of,我们查看的共同点是什么?嗯,每个人都使用Outlook和 teams,因此 focusing in on 我们实际上使用该方法论找到了四个不同的DLL,这些是跨 Windows ten、Windows eleven 和所有那些不同版本通用的,这些主要是可以 easily hijacked 的。这种技术,所有您必须做的是将文件放入这个地方。显然您需要知道哪个版本。因此一个快速的注册表查询以查看运行的是哪个版本的Outlook,那是容易的事情。并且显然知道您运行 under 的用户,但自动化 that to just to drop a DLL and then wait for the next time outlook ran 将导致我们通过某种 c two 获得远程访问。
现在如果我们更深入 under the hood 并查看这一点,如我之前提到的, just because 我们的DLL加载并不意味着我们的代码 instantly runs 并且我们有 shells。
Michael Allen
抱歉。
Matthew Eidelberg
因此我们需要寻找一种方式来实现这一点,因为显然没有进程将运行我们的邪恶 cPT 代码或类似的东西。因此我们必须弄清楚如何自动让我们的代码在DLL加载时立即启动。我们必须找到 universal 的东西,保证如果您做过任何类型的恶意软件开发。您可能知道我们要去哪里,但我们将 kind of talk about the fact that DLL main is present。那是我们选择的。为什么DLL main?因为它是一个通用函数,它的作用是,当DLL被加载或进程终止时 serve 一个主要目的,因为它所做的只是初始化DLL依赖项。并且如果您正在关闭或说DLL需要被卸载,DLL main 进行后初始化以确保内存空间被清除并且没有错误或不需要的东西。这是一个非常常见的函数。它用于许多不同的基本操作结构。如果您 ever go and just search and like on Microsoft documents or GitHub,show me a sample DLL,您将看到第一个示例将总是有一个DLL main。并且在里面会有像当它附加到进程时发生什么、当线程被调用时发生什么之类的东西,然后它 just handles all those things。因此通过附加到 that,我们 almost attaching to the startup operation,或者如果您 almost think of it as a computer,我们 attaching ourselves to the boot process of a DLL。
有人可能想,等等,那不是 kind of dangerous 吗?您是对的,通过将您的代码附加到DLL main 有很多问题。最大的一个是进程死锁。我怎么知道?因为我 deadlocked many processes developing this 因为当DLL main 加载时,它进行快速系列检查,然后 just waits for something to happen in a process。当它加载时,我们会说,嘿,先加载这个DLL。好的,现在那个DLL工作,一切良好。转到下一个。刚刚收到消息那个DLL工作。一切良好,加载。好。因此如果我们将 something 加载到DLL main 中,并且当它被调用时,需要很长时间,我们冻结那个进程并死锁它,这 disrupts it。它正在等待 something。因此如果我们的 shell code 在那里,例如,并且我们阻止进程 kind of loading the additional resources,something gets skipped 或甚至进程冻结。首先,用户注意到,那已经是游戏结束。但有时您实际上可能导致进程崩溃,使其非常不稳定,这 then doesn’t resolve in us maintaining access。
因此微软实际上,因为这个和 how easy it is to cause,推荐任何类型的函数或任何类型的附加东西尽可能推迟直到初始化过程之后。这只是为了确保没有附加动作 superseding。进程需要 kind of load everything and get every environmental variable set up and make sure support across DLL’s is there。并且它们实际上有一个很好的参考。这里有一个超链接关于DLL main 应该 never perform 的事情。您可以看到许多这些是您会看到的关于任何类型的 shellcode 或恶意软件开发 load library、create process threads,因为它可能导致这种死锁问题,并且一旦进程死锁,您无法真正恢复它。
那么我们如何绕过这个解决方案?嗯,您可能 seem like a very simple fix,但 boy,它确实花了一段时间才弄清楚,即它们的文档中有一个参考说嘿,您应该推迟一切直到 later。现在我不想推迟或任何其他事情。因此我实际上想出的主意是 actually creating a thread。并且 inside that thread there’s a sleep function that will,once that sleep function is done,we’ll run our shellcode。现在我们为什么想创建一个线程或有人说,嘿,与线程有什么区别?嗯,当您创建一个线程时,您创建 almost like a parallel series of function processes that can operate。因此 everything that’s in this thread can run。那是主要的。我们创建这个分支,以便其他函数然后可以运行。并且因为 that we can just wait。好的,我们将等待10秒或15秒,那应该确保一切加载,然后我们将运行我们的代码。这实际上 works very elegantly。它是如此简单的解决方案。并且除了 COVID 选项,因为进程加载并在长时间后我们的代码执行,许多传统产品、安全产品将扫描、检查并说嘿,现在没有奇怪行为。然后几秒钟后 something happens and kind of helps us stay under the radar。它并不总是保证的规避技术。并且请不要 off and say,Matt says if you sleep it long enough you can bypass every EDR。那不是我在说的,但它确实有帮助。但它也 mainly just makes sure that everything gets loaded and there’s no disruption。并且那 worked really great。
并且我将 kind of mention this,但我们向微软提供了许多这些信息,并且他们 kind of said,感谢您的建议,真的。我们理解这是一个问题。我们将此 under future production,environmental changes。我 wondered why they weren’t taking this seriously。并且我发现他们实际上正在停用许多Outlook和Microsoft Teams,用于他们一直在开发的新版本。那是我决定更深入挖掘的地方。那是研究的下半部分 came into play。因此 Windows apps,现在您可能 like 什么是 Windows apps?因为那是我的问题。当我们查看时,它们实际上在一个与传统文件分开的文件夹中。您可以看到在这里新版本的 teams。它在 C program、Windows apps 中,任何来自新Outlook的东西,如果您想知道他们重命名为 olk。不要问我为什么,但它们在