CVE-2019-19470:TinyWall防火墙本地提权漏洞分析

来源:岁月联盟 编辑:猪蛋儿 时间:2020-01-29

在本文中,我们介绍了TinyWall在2.1.13版本前存在的一个本地提权漏洞,本地用户可以借助该漏洞提升至SYSTEM权限。除了命名管道(Named Pipe)通信中存在的.NET反序列化漏洞之外,我们也介绍了一个认证绕过缺陷。
 
0x01 背景
TinyWall是采用.NET开发的一款本地防火墙,由单个可执行文件构成。该可执行程序会以SYSTEM权限运行,也会运行在用户上下文中,方便用户进行配置。服务端在某个命名管道上监听传入消息,消息使用BinaryFormatter以序列化对象流的形式传输。然而这里存在一个身份认证检查机制,需要进一步研究。在本文中我们将详细分析这种机制,因为其他产品也有可能使用这种机制来防御未授权访问风险。
为了简单起见,下文中我们将使用“服务端”来表示接收消息的SYSTEM上下文进程,使用“客户端”来表示位于已认证用户上下文中的发送进程。需要注意的是,已认证用户并不需要任何特殊权限(如SeDebugPrivilege)就能利用本文描述的该漏洞。
 
0x02 命名管道通信
许多(安全)产品会使用命名管道(Named Pipe)作为进程间通信的渠道(可以参考各种反病毒产品)。命名管道有一个优势,服务端进程可以通过Windows的认证模型来获取发送方的其他信息,比如原始进程ID、安全上下文等。从编程角度来看,我们可以通过Windows API来访问命名管道,但也可以通过直接访问文件系统来实现。我们可以通过命名管道的名称,配合//./pipe/前缀来访问命名管道文件系统(NPFS)。
如下图所示,该产品用到了TinyWallController命名管道,并且任何已认证用户可以访问并写入该管道。

 
0x03 SYSTEM进程
首先我们来看一下命名管道的创建及使用过程。当TinyWall启动时,会调用PipeServerWorker方法完成命名管道创建操作。Windows提供了一个API:System.IO.Pipes.NamedPipeServerStream,其中某个构造函数以System.IO.Pipes.PipeSecurity作为参数,这样用户就能使用SecurityIdentifiers等类,通过System.IO.PipeAccessRule对象实现细粒度的访问控制。此外,从上图中我们可以观察到,这里唯一的限制条件在于客户端进程必须在已认证用户上下文中运行,但这看上去似乎并不是一个硬性限制。

然而如上图所示,实际上这里还存在其他一些检查机制。该软件实现了一个AuthAsServer()方法,会进一步检查一些条件。我们需要到达调用ReadMsg()的代码块,该调用负责反序列化已收到的消息内容。

如果未能通过检查,则代码会抛出异常,内容为“Client authentication failed”(客户端认证失败)。跟踪代码流程后,我们找到了一个“认证检查”代码块,代码逻辑基于进程ID来检查,判断服务端与客户端进程的MainModule.FileName是否一致。开发者之所以使用这种逻辑,可能是想确保相同的、可信的TinyWall程序能通过命名管道来发送和接收封装好的消息。

我们可以在调试上下文中使用原始程序,这样就不会破坏MainModule.FileName属性,从而绕过该限制。接下来我们先使用调试器来验证不可信的反序列化操作。
 
0x04 测试反序列化
因此,为了测试是否可以使用恶意对象来反序列化,我们可以使用如下方法。首先,我们通过调试器(比如dnSpy)启动(而不是attach)TinyWall程序,在客户端向管道写入消息之前的位置上设置断点,这样我们就能修改序列化后的对象。在运行过程中,我们可以考虑在Windows System.Core.dll中的System.IO.PipeStream.writeCore()方法上设置断点,以便完成修改操作。完成这些设置后,很快断点就会被触发。

现在,我们可以使用ysoserial.NET和James Forshaw的TypeConfuseDelegate gadget来创建恶意对象,弹出计算器。在调试器中,我们使用System.Convert.FromBase64String("...")表达式来替换当前值,并且相应地调整计数值。

释放断点后,我们就能得到以SYSTEM权限运行的计算器进程。由于反序列化操作会在显式转换前触发,因为我们的确能完成该任务。如果大家不喜欢出现InvalidCastExceptions,那么可以将恶意对象放在TinyWall的PKSoft.Message对象参数成员中,这个练习留给大家来完成。
 
0x05 伪造MainModule.FileName
通过调试客户端验证反序列化缺陷后,接下来我们可以看一下是否能抛开调试器完成该任务。因此,我们必须绕过如下限制:

GetNamedPipeClientProcessId()这个Windows API用来获取特定命名管道的客户端进程标识符。在最终的PoC(Exploit.exe)中,我们的客户端进程必须通过某种方式伪造MainModule.FileName属性,以便匹配TinyWall的程序路径。该属性通过System.Diagnostics.ProcessModule的System.Diagnostics.ModuleInfo.FileName成员来获取,后者通过psapi.dll的GetModuleFileNameEx()原生调用来设置。这些调动位于System.Diagnostics.NtProcessManager上下文中,用来将.NET环境转换为Windows原生API环境。因此,我们需要研究一下是否可以控制该属性。

[1] [2]  下一页