【技术分享】DG on Windows 10 S: 执行任意代码
作者:admin | 时间:2017-8-2 02:52:22 | 分类:黑客技术 隐藏侧边栏展开侧边栏
许多人可能会有一种错觉,那就是在Windows 10 S上执行非Microsoft代码是非常困难的。不过,就像您在前面的文章中看到的那样,事实并非如此,您需要的,无非就是安装Office,获取编写VBA宏脚本的权限,以及一个不含有MOTW的文件。事实上,Windows内核只是对加载可执行文件和DLL做了限制。但是,已经获取相应签名的应用程序将会畅行无阻,例如Office可以创建自己的可执行内容,而这些内容却可能被用户或攻击者所滥用。
因此,您可能还会认为,由于Windows默认安装了许多不同的脚本引擎,因此可以轻松运行任意代码。且慢,许多内置的脚本引擎(如运行JScript/ VBScript的Powershell和Windows Scripting Host(WSH))都是“开明的”。这意味着当它们检测到启用UMCI时,它们将进入锁定模式,例如PowerShell的约束语言模式。如果正在执行的脚本也使用了与二进制可执行内容相同的证书集进行的签名,则这些开明的主机将解锁脚本语言的全部功能。通常情况下,有许多办法可以绕过这些限制,但是,这里只是围绕Windows RT进行介绍。此外,如果有兴趣的话,您可以在这里找到关于BlueHat旁路技术的完整演示文稿。
但从Win10S开始下手也是一个不错的选择。绕过上述限制时,主要求助于脚本引擎,但是就像之前文章中提到的那样,DG策略也提供了相应的黑名单。还有一些其他知名的恶意软件,如MSBuild也被列入了黑名单。所以我想我们必须回到最初的设想:默认的Win10S系统还有很多可执行文件可以被滥用,我们只需要找到它们即可。
关于BinaryFormatter
对象序列化框架通常蕴含着丰富的代码执行漏洞,而.NET也不例外。 不久之前,我在.NET中发现了一个处理WMI类方面的RCE。 您可以在这篇博客文章中阅读更多详细信息,但是简单来说,就是可以将任意字节流传递给内置的BinaryFormatter类,并从内存中加载程序集进而执行任意代码。
虽然不太明显,实际上这也是一种DG旁路技术。尽管PowerShell允许您以约束语言模式来查询任意的WMI服务器和类,但是.NET运行时并不是“开明的”,所以它将从一个字节数组中加载程序集。从字节数组加载很重要,因为正常情况下,.NET将从需要映射到内存的可执行文件中加载程序集。将可执行文件映射到内存中的行为会触发内核中的CI模块来验证签名,根据配置的CI策略,将不允许加载任何代码。对于字节数组,内核不会将程序集视为.NET,所以会进行处理并从中执行任意的托管代码。然而,DCOM漏洞已经被修复了,PowerShell会被阻止,所以我们无法调用WMI方法。但是,如果我们可以找到另一个应用程序,它可以使用字节数组并将其传递给BinaryFormatter,那么,我们就可以重用我以前的漏洞利用中使用的反序列化漏洞链,并使用它来绕过内存中的DG保护。
这里考察的重点是在%SystemRoot%\ Microsoft.Net目录中的可执行文件,其中许多是用.NET编写的,因此很可能可以加以利用。首先引起我的兴趣的是AddInProcess.exe,这主要归功于它的名称。这是一个个可执行文件,实际上,之前我就从Partial Trust沙箱转义的角度对它进行过研究。
这个代码是.NET 4中引入的插件模式的一部分。插件模式提供了一个结构化框架,提供了某些功能,以便于第三方向现有应用程序添加附加功能。为此,开发者需要开发相应的接口,建立管道以及许多其他复杂的工作,但是我们并不关心这些。 有趣的是,这个模式支持Out-of-Process(OOP)插件,这在是AddInProcess的用途。为了托管这些OOP插件,需要启动一个可执行文件。而这个可执行文件的主函数其实很简单,如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
static int Main( string [] args) {
if (args.Length != 2
|| !args[0].StartsWith( "/guid:" )
|| !args[1].StartsWith( "/pid:" )) {
return 1;
}
string guid = args[0].Substring(6);
int pid = int .Parse(args[1].Substring(5));
AddInServer server = new AddInServer();
var server = new BinaryServerFormatterSinkProvider {
TypeFilterLevel = TypeFilterLevel.Full
};
var client = new BinaryClientFormatterSinkProvider();
var props = new Hashtable();
props[ "name" ] = "ServerChannel" ;
props[ "portName" ] = guid;
props[ "typeFilterLevel" ] = "Full" ;
var chnl = new AddInIpcChannel(props, client, server);
ChannelServices.RegisterChannel(chnl, false );
RemotingServices.Marshal(server, "AddInServer" );
Process.GetProcessById(pid).WaitForExit();
return 0;
}
|
这里要指出的是,它使用ChannelServices.RegisterChannel:这表明它正在使用.NET的远程处理技术进行通信。对了,之前我们还在哪里看到过.NET远程处理技术呢? 哦,没错,是我最后一次破解.NET远程处理技术的时候。但是这里的重点在于,它不仅使用了可以被破解的.NET远程处理技术,而且还以Full TypeFilterLevel模式使用了BinaryFormatter,这意味着我们可以对任何数据进行反序列化,而不必担心各种安全限制。
该进程会通过Windows命名管道创建一个IPC通道,并且使用在命令行中传递的portName属性来指定管道的名称。该进程还会接收进程的ID,它会一直等待,直到其他进程退出为止。 因此,我们可以使用以下命令行启动AddInProcess:
1
|
AddInProcess.exe /guid:32a91b0f-30cd-4c75-be79-ccbd6345de11 /pid:XXXX
|
将XXXX替换为相应的进程ID,例如资源管理器。 我们会发现该进程创建了命名管道\\.\pipe\32a91b0f-30cd-4c75-be79-ccbd6345de11。 该服务的名称是通过RemotingServices.Marshal设置的,这里为AddInServer。 因此,我们可以将远程URI构建为ipc:// 32a91b0f-30cd-4c75-be79-ccbd6345de11 / AppInServer,我们可以使用我的ExploitRemotingService工具来验证它是否可以加以利用(当然,是在非DG Windows 10机器上)。
我们需要使用--useser标志与ExploitRemotingService工具,这样就可以不借助于MS已经修复的旧漏洞了。这个useser标志可以发送序列化对象,并从服务器返回,从而允许执行文件操作,如列出目录以及上传/下载文件等。需要注意的是,只有在TypeFilterLevel设置为Full时才这样做才有效。这表明远程通道容易受到任何反序列化的影响。所以,您可以借助我的工具,从.NET DCOM漏洞利用代码中截取相应的字节,替换序列化的字节,这样就可以在AddInProcess的上下文中执行任意代码了。
需要注意的是:如果发送数据到这个IPC服务器的唯一方法是运行一个专门设计来与.NET远程服务通信的、我们可以运行任意代码的工具的话,那么我们就无需旁路了。由于这个通道是一个命名管道,那么,我们是否可以远程利用它呢?很可惜,.NET Framework在这个创建命名管道的时候,使用了一个阻止网络访问的显式安全描述符。
理论上,我们可以修改权限,但就算我们找到了这样的工具,也还是需要另外一台机器——太麻烦了。那么该怎么办?幸运的是,对于我们来说,.NET远程处理协议非常简单。在连接开始时,它没有进行协商,客户端只需将正确格式化的字节集(包括头文件和序列化消息)发送到服务器即可,如果正确,那么服务器将给予响应。我们可以提前创建一个包含序列化请求的二进制文件,并将其写入命名管道。如果我们利用ExploitRemotingService封装请求,并结合之前的.NET序列化漏洞,我们就可以生成一个攻击.NET AddInProcess服务器的二进制文件。
如果我们有一个名为request.bin的文件,则将其写入命名管道最简单的方法,就是使用CMD:
1
|
C:\> type request.bin > \\.\pipe\32a91b0f-30cd-4c75-be79-ccbd6345de11
|
太好了,这的确很简单,不过......我们不能运行CMD。那么,我们还能用什么? 当WSH被阻止时,我们仍然可以在regsvr32中运行scriptlet。 但是,脚本托管环境是开明的,在JScript / VBScript的情况下,意味着在创建的COM对象方面会受到严格的限制。 您可以创建的唯一对象是Scripting.FileSystemObject,它允许您打开任意文本文件并进行读/写操作。同时,它支持打开命名管道,并使用这一功能来处理进程输出。 因此,您可以通过下面的操作将任意数据写入命名管道。
1
2
3
4
5
6
7
8
|
var fso = new ActiveXObject( "Scripting.FileSystemObject" );
var pipe = "\\\\.\\pipe\\32A91B0F-30CD-4C75-BE79-CCBD6345DE11" ;
// Create a new ANSI text file object to the named pipe.
var file = fso.CreateTextFile(pipe, true , false );
// Write raw data to the pipe.
var data = "RAW DATA" ;
file.Write(data);
file.Close();
|
不过,事情没有这么简单。请求数据是任意二进制的,所以我最初尝试使用一个Unicode文本文件,这使得二进制数据的写入变得容易起来。在创建构成请求的文件时,该类首先会写入一个字节顺序标记(BOM)。所以,我开始尝试ANSI模式,但是这会将UCS-2字符从JScript转换为当前的ANSI代码页。在英文Windows系统上,这通常是代码页1252,您可以在UCS-2字符和任意8位字符之间构建映射表。但是,如果您的系统被设置为另一个代码页,例如更复杂的多字节字符集之一,如Shift-JIS,这就难办了。无论如何,我敢肯定,将来可以设法让它在更多的平台上工作,使其可以加载任何任意的.NET代码,并通过DG Win10S强制策略执行这些代码。
我已将代码上传到我的github。您可以在另一台计算机上运行CreateAddInIpcData工具,只要提供IL-only .NET程序集的路径和输出scriptlet文件的名称即可。确保给scriptlet文件提供一个.sct扩展名。 .NET程序集必须包含单个具有空构造函数的公共类,以在反序列化期间充当入口点。类似下面代码的C#_也可以,只需编译成一个类库程序集即可。
1
2
3
4
5
|
public class EntryPoint {
public EntryPoint() {
MessageBox.Show( "Hello" );
}
}
|
将输出脚本文件复制到Win10S机器。使用前面的命令行启动AddInProcess(确保GUID与前面的相同,因为端点URI以序列化请求结尾),并指定PID(从任务管理器获取)。 确保AddInProcess可执行文件不会立即退出,因为这将在命令行中显示错误信息。 执行scriptlet时,可以通过在资源管理器中右键单击它,并选择“注销”,或从资源管理器的“运行”对话框中手动输入以下命令:
1
|
regsvr32 /s /n /u /i:c:\path\to\scriptlet.sct scrobj.dll
|
您现在应该会发现,任意.NET代码都可以在AddInProcess的上下文中执行了。这样,除了从磁盘上的文件加载未签名的.NET程序集外,您还可以随意编写自己喜欢的代码了。
好了,该说的都说了。应该清楚的是,现在UMCI和.NET搭配得还不太好,就像4年前当我用类似的技巧来攻击Windows RT时一样。当然,我不知道Microsoft未来是否会限制从内存中加载.NET程序集。
如果您担心这种安全漏洞,您可以在DG或Applocker策略中阻止AddInProcess。然而,除非Microsoft找到了.NET应用程序混淆代理绕过CI策略的解决方案,否则,肯定还会有其他的旁路技术。如果您打算将该二进制文件添加到自己的DG策略中的话,建议您按照这篇文章中的说明进行操作。此外,不要忘了同时将AddInProcess32加入黑名单。
在后面的文章中,我们将利用这个任意代码执行漏洞来运行一些其他的分析工具,甚至可以提供反向Powershell——从这里可以看出,.NET的确是个好东东,所以你应该始终使用.NET来编写自己的工具。;-)
本文由 安全客 翻译,作者:shan66
原文链接:https://tyranidslair.blogspot.jp/2017/07/dg-on-windows-10-s-executing-arbitrary.html