内网防御规避(一)-使用blockdlls保护你的代码

Cobalt Strike 3.14新增了blockdlls功能,该功能可以限定子进程只能加载带有微软签名的dll。

通过该特性我们可以防止第三方的应用(如安全软件)对子进程进行DLL注入,避免安全软件对我们的程序进行查杀。

0x00 前言

Cobalt Strike 3.14新增了blockdlls功能,该功能可以限定子进程只能加载带有微软签名的dll。

通过该特性我们可以防止第三方的应用(如安全软件)对子进程进行DLL注入,避免安全软件对我们的程序进行查杀。

本文就来分析blockdlls功能的具体是如何实现的。

本文重点

  • Cobaltstrike中的blockdlls
  • 创建blockdlls进程
  • 修改当前进程并开启blockdlls
  • 通过API思考blockdlls的优劣性

0x01 cobaltstrike中的blockdlls

先看一下cobaltstrike里面的介绍

该功能在win10/win server 2012以上版本生效,可以通过该功能对子进程保护,非微软官方DLL不能加载进子进程。

在非win10之前的系统上执行,会返回非win8以后系统,因此我们可以大概推断,该功能准确来说应该是win8之后的系统生效。

功能操作也很简单,blockdlls [start/stop],分别是开启与关闭。

0x02 创建blockdlls进程

创建blockdlls进程需要用到的函数是UpdateProcThreadAttribute,其中需要注意的是:lpValue是指向属性值的指针。该值应保持不变,直到使用DeleteProcThreadAttributeList函数销毁该属性为止。

首先我们来看C++代码,XPN在博客中分享了实现同样功能的c代码,地址如下:

https://blog.xpnsec.com/protecting-your-malware/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <Windows.h>

int main()
{
STARTUPINFOEXA si;
PROCESS_INFORMATION pi;
SIZE_T size = 0;
BOOL ret;

// Required for a STARTUPINFOEXA
ZeroMemory(&si, sizeof(si));
si.StartupInfo.cb = sizeof(STARTUPINFOEXA);
si.StartupInfo.dwFlags = EXTENDED_STARTUPINFO_PRESENT;

// Get the size of our PROC_THREAD_ATTRIBUTE_LIST to be allocated
InitializeProcThreadAttributeList(NULL, 1, 0, &size);

// Allocate memory for PROC_THREAD_ATTRIBUTE_LIST
si.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(
GetProcessHeap(),
0,
size
);

// Initialise our list
InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &size);

// Enable blocking of non-Microsoft signed DLLs
DWORD64 policy = PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON;

// Assign our attribute
UpdateProcThreadAttribute(si.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY, &policy, sizeof(policy), NULL, NULL);

// Finally, create the process
ret = CreateProcessA(
NULL,
(LPSTR)"C:\\Windows\\System32\\cmd.exe",
NULL,
NULL,
true,
EXTENDED_STARTUPINFO_PRESENT,
NULL,
NULL,
reinterpret_cast<LPSTARTUPINFOA>(&si),
&pi
);
}

根据xpn对cobaltstrike beacon的分析,我们可以知道该关键函数UpdateProcThreadAttribute,并可以知道PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY的值为0x20007,PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON的值为0x100000000000。

简单说明一下UpdateProcThreadAttribute函数的输入参数。

1
lpAttributeList

指向由InitializeProcThreadAttributeList函数创建的属性列表的指针

1
Attribute

在属性列表中更新的属性键。其中的PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY属性便是blockdlls的主角,通过修改该值可以起到了阻止加载非Microsoft签名dll的作用。

之后我用C#代码又实现了一遍UpdateProcThreadAttribute函数的过程。为了用C#实现还是踩了很多坑,最坑的地方就是需要写入cbSize(lpValue参数指定的属性值的大小),不能设为IntPtr.Zero。

首先需要通过STARTUPINFOEX结构体指定了要创建子进程的安全策略。

1
2
3
4
5
6
[StructLayout(LayoutKind.Sequential)]
public struct STARTUPINFOEX
{
public STARTUPINFO StartupInfo;
public IntPtr lpAttributeList;
}

以及其他需要的结构体

1
2
3
4
5
6
7
8
[StructLayout(LayoutKind.Sequential)]
public struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
1
2
3
4
5
6
7
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public int nLength;
public IntPtr lpSecurityDescriptor;
public int bInheritHandle;
}
1
2
3
4
5
6
[StructLayout(LayoutKind.Sequential)]
public struct DWORD64
{
public uint dwPart1;
public uint dwPart2;
}

实现函数功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public static bool CreateProcess()
{

var pInfo = new PROCESS_INFORMATION();
STARTUPINFOEX sInfoEx = new STARTUPINFOEX();

sInfoEx.StartupInfo.cb = (uint)Marshal.SizeOf(sInfoEx);
IntPtr lpValue = IntPtr.Zero;

SECURITY_ATTRIBUTES pSec = new SECURITY_ATTRIBUTES();
SECURITY_ATTRIBUTES tSec = new SECURITY_ATTRIBUTES();
pSec.nLength = Marshal.SizeOf(pSec);
tSec.nLength = Marshal.SizeOf(tSec);

//从进程的非托管内存中分配内存
IntPtr pntpSec = Marshal.AllocHGlobal(Marshal.SizeOf(pSec));
Marshal.StructureToPtr(pSec, pntpSec, false);
IntPtr pnttSec = Marshal.AllocHGlobal(Marshal.SizeOf(tSec));
Marshal.StructureToPtr(tSec, pnttSec, false);

IntPtr lpSize = IntPtr.Zero;

//初始化指定的属性列表以创建进程和线程
InitializeProcThreadAttributeList(IntPtr.Zero, 1, 0, ref lpSize);
sInfoEx.lpAttributeList = Marshal.AllocHGlobal(lpSize);
InitializeProcThreadAttributeList(sInfoEx.lpAttributeList, 1, 0, ref lpSize);

//获取进程句柄
IntPtr parentHandle = Process.GetProcessById(1408).Handle;
lpValue = Marshal.AllocHGlobal(IntPtr.Size);
Marshal.WriteIntPtr(lpValue, parentHandle);

//获取要启动的进程路径
var lpApplicationName = Path.Combine(Environment.SystemDirectory, "cmd.exe");

//设置将Microsoft only(blockdlls)

DWORD64 policy = new DWORD64();
policy.dwPart1 = 0;
policy.dwPart2 = 0x1000;


IntPtr lpMitigationPolicy = Marshal.AllocHGlobal(IntPtr.Size);
Marshal.WriteInt64(lpMitigationPolicy, PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON);

UpdateProcThreadAttribute(sInfoEx.lpAttributeList, 0, (IntPtr)PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY, lpMitigationPolicy, (IntPtr)Marshal.SizeOf(policy), IntPtr.Zero, IntPtr.Zero);


if (!CreateProcess(lpApplicationName, null, ref pSec, ref tSec, true, CreateSuspended | EXTENDED_STARTUPINFO_PRESENT | CREATE_NEW_CONSOLE, IntPtr.Zero, null, ref sInfoEx, out pInfo))
{
return false;
}
return true;
}

C++与C#生成的代码略微不同,在对C++创建的进程进行DLL注入时会提示错误,而C#则没有。两者都不可以注入DLL文件。有对windows了解的大牛可以告诉我是什么原因。

C#:

不能注入DLL,但没有报错。

C++:

注入DLL文件触发报错。

0x03 修改当前进程并开启blockdlls

修改当前进程并开启blockdlls的原理是先获取当前进程的安全策略,然后再启用MicrosoftSignedOnly功能。这就需要用到两个API,获取进程安全策略GetProcessMitigationPolicy,修改进程安全策略SetProcessMitigationPolicy

根据API文档,我们可以确定这两个函数都是在win8之后系统可用。

3gstudent已经实现了win10系统下的代码ForWin10_CurrentProcess.cpp),可以实现查询修改安全策略。其中在Win8系统,代码ForWin8_CurrentProcess.cpp)需要使用NtQueryInformationProcess()NtSetInformationProcess()进行查看和修改安全策略。

0x04 查询安全策略

通过powershell可以查询出是否启用了MicrosoftSignedOnly功能。

1
get-process | select -exp processname -Unique | % { Get-ProcessMitigation -ErrorAction SilentlyContinue -RunningProcesses $_ | select processname, Id, @{l="Block non-MS Binaries"; e={$_.BinarySignature|select -exp MicrosoftSignedOnly} } }

0x05 后语

开启blockdlls后,我们可以稍微保护我们的代码,虽然目前已有不经过微软签名的DLL也能注入到进程当中,但对于大多数安全软件来说,该功能还是比较实用的。缺陷也很明显,就是系统要求比较高,需要在win8系统之后才会用到该功能。

参考链接

Cobalt_Strike的blockdlls利用分析

Protecting Your Malware with blockdlls and ACG

https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-updateprocthreadattribute

https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getprocessmitigationpolicy

https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setprocessmitigationpolicy

https://github.com/b4rtik/RedPeanut/

Author: rootrain
Link: https://rootrain.me/2020/02/29/内网防御规避(一)-使用blockdlls保护你的代码/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.