【技术分享】如何监控Windows控制台活动(Part 1)

https://p0.ssl.qhimg.com/t01c6f856f5e2ea29d0.jpg

译者:興趣使然的小胃

预估稿费:170RMB

投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿


一、简介

Mandiant在处理应急响应事件时,发现攻击者有时会主动使用被突破网络中的操作系统。攻击者经常通过RDP方式来使用交互式控制台(console)程序,比如命令行、PowerShell、以及自定义的C2(命令与控制)控制台工具等。以此为契机,Mandiant的ICE(Innovation and Custom Engineering)团队研究了如何捕捉端点上的黑客活动轨迹。

捕捉这类数据的难度取决于目标Windows系统的版本,比如,在实时系统上捕捉相关数据是非常困难的一件事情。这一任务的困难程度与过去十几年中Windows对虚拟控制台的具体实现方式密切相关。

在本文中,我们讨论了过去几年中Windows对控制台架构的具体实现,重点分析了当前Windows版本中控制台的具体实现机制。


二、控制台概述

Windows PE加载器会根据PE选项头中的“Subsystem”字段来判断某个文件是否为控制台应用程序。如果这个字段的值为IMAGESUBSYSTEMWINDOWS_CUI,加载器会为该进程分配一个控制台服务器(console server)。在不同版本的Windows系统中,控制台服务器的具体实现也有所不同,从Windows XP起,控制台的实现方式经过了三次较大的改动。不过无论具体的实现细节如何,当运行某个客户端(如cmd.exe、powershell.exe等)时,默认情况下,系统通常会使用AllocConsole这个Win32 API来建立与控制台服务器的连接。用户键入命令时,通常需要与服务器进程进行交互,这些命令随后会通过进程间通信(Interprocess communication,IPC)机制传递给客户端进程。一个控制台服务器可以同时托管一个或多个客户端。


三、Windows控制台的演进历史

从Windows XP起到Windows Vista系统,Windows使用客户端/服务器运行时子系统(Client/Server Runtime Subsystem process,CSRSS)来负责处理用户的输入,并将输入数据发送给客户端进程。客户端与CSRSS之间使用本地过程调用(Local Procedure Call,LPC)端口进行通信,以发送收到的输入数据。在Windows XP以及Vista系统中使用的客户端-服务器控制台架构如图1所示。

http://p6.qhimg.com/t01997fe543b7b4ba3c.png

图1. Windows XP/Vista中的控制台架构

在这个模型中,由于客户端以当前用户身份运行,而服务器以本地系统账户(Local System)身份运行,因此容易受到权限提升漏洞影响。攻击者可以利用CSRSS这个脆弱点,在低权限用户模式下触发CSRSS的漏洞代码路径,获取SYSTEM级别的访问权限。

随着Windows 7以及Windows Server 2008 R2的发布,这个架构问题也随之解决。现在CSRSS不是系统中唯一的控制台服务器,系统新引入了一个控制台宿主进程(conhost.exe),以托管控制台的输入线程。这个进程现在与客户端运行在同一个上下文环境中,因此可以消除这种攻击场景。更新后的Windows 7控制台架构如图2所示。

http://p4.qhimg.com/t0143891d8b33cec8e9.png

图2. Windows 7/Server 2008 R2中的控制台架构

当Windows 7中分配一个控制台时,CSRSS就会运行一个新的conhost.exe实例进程。操作系统会使用RPC ControlConsoleLPC-<conhost_pid>-<random_number>这种名称来创建一个高级本地过程调用(Advanced Local Procedure Call,ALPC)端口。这个端口与映射到客户端和服务器进程中的一个共享对象配合使用,因此可以轻松共享命令行数据。此外,系统也会使用RPC ControlConsoleEvent-<conhost_pid>-<random_number>这种名称创建一个事件对象,以便在新的数据出现时,客户端与服务器之间能够使用该对象通知对方。一个conhost.exe进程可以为多个客户端应用提供服务,如图3中Windbg的输出信息所示。

http://p6.qhimg.com/t01463bf91c7827c082.png

图3. 使用Windbg分析Windows 7系统中的ALPC端口,该端口对应一个conhost进程与多个控制台应用

从Windows 8开始,Windows引入了新的控制台实现机制。新的架构与之前架构最大的不同在于,新架构中有个专门的内核驱动来负责处理客户端与服务器进程之间的控制台I/O数据。这个驱动就是ConDrv.sys,负责系统上的所有控制台通信。驱动通过名为DeviceConDrv的驱动对象为用户模式下的应用程序提供接口。用户模式下的应用程序可以使用一些命名空间参数(如Connect、Server、Input、Output、Reference、CurrentIn以及CurrentOut)来打开这个驱动对象,应用程序可根据实际需求决定具体使用哪些参数。根据驱动所需的具体功能,客户端应用程序通常会打开控制台驱动的多个句柄,如图4所示。

http://p7.qhimg.com/t014326a215a1649a08.png

图4. 命令行应用程序打开多个ConDrv句柄

当某个命令行进程分配一个控制台时,kernelbase.dll就会打开DeviceConDrv的一个句柄,并请求系统创建一个新的conhost.exe进程。ConDrv会在内核模式下运行这个进程,并分配内存描述符列表(memory descriptor list ,MDL)链。MDL链可以用来映射Conhost进程及其客户端的内存页面,以便该进程与客户端之间共享数据。与之前版本所使用的LPC/ALPC端口不同,系统现在通常会使用Fast I/O将消息传递给控制台驱动。在Fast I/O的帮助下,应用程序可以与驱动进行通信,并且无需为每个请求创建I/O请求数据包(I/O request packet,IRP)。IRP是一种操作系统架构,用来将I/O数据投递给设备驱动。这些快速I/O请求由ConDrv驱动负责,可以用来读取或写入控制台。

在Windows 10中,conhost.exe主要充当容器进程角色。ConhostV2.dll或者ConhostV1.dll负责主要的输入线程以及所有的服务器功能。默认情况下,系统会加载ConhostV2.dll并向Windows 10用户提供新的控制台功能(比如控制台窗口全屏化)。ConhostV1.dll实现了“传统模式”的控制台功能,启用这种模式后,控制台的表现与Windows 7以及之前版本的系统类似。不管具体使用的是哪个版本,系统都使用ConDrv.sys来传输控制台客户端与服务器之间的消息。整体结构如图5所示。

http://p6.qhimg.com/t0118a6d288dbbe9efa.png

图5. Windows 10中基于控制台驱动的架构

读者可以阅读第二篇文章了解后续内容。

(完)