安全矩阵

 找回密码
 立即注册
搜索
查看: 691|回复: 0

深入挖掘红队实战中WinRM的使用技巧

[复制链接]

77

主题

77

帖子

257

积分

中级会员

Rank: 3Rank: 3

积分
257
发表于 2022-11-17 20:30:32 | 显示全部楼层 |阅读模式
本帖最后由 Aspark 于 2022-11-17 20:38 编辑

在红队的内网渗透活动中经常会出现WinRM的身影,众所周知通过WinRM能够在内网执行远程命令来进行横向移动。那么WinRM是如何实现命令执行这一功能的?除了远程执行命令,能否利用该协议达到更多隐蔽目的呢?本文将从协议原理和实战技巧方面,探究WinRM的通信过程并挖掘一些新的利用方法,最后通过编写一个用于命令执行的BOF实例加深学习。0x00 WSMan 与 WinRM什么是WSMan?
以下定义来自 DMTF WS-MAN 和 微软 WS-Management Protocol
WSMan全称 Web Services Management,也叫WS-Man、WS-Management,是一个基于SOAP的设施管理协议的开源标准,可用于与任何实现了该协议的计算机设备交换管理数据。注意WSMan是标准定义而不是具体实现。
先介绍几个核心的概念:
  • Schema and Message

WSMan使用消息进行管理数据交换,这些消息由XML进行表示,WSMan中定义了一系列的XML Schemas用于构建消息 ,查看示例:https://www.dmtf.org/dsp/DSP8015
  • Resource

在每一个消息中会引用要处理的目标资源(Resource),资源由一个唯一的URI来对应(Resource URI),示例:http://schemas.microsoft.com/wbem/wsman/1
  • Action

此外WSMan还为消息定义了要对引用资源进行的动作(Action),比如WinRM支持 Get、Put、Create、Delete、Enumerate等,winrs.exe就是通过创建http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd这个资源的新实例来实现命令执行。什么是WinRM?WinRM全称Windows Remote Management是微软对WSMan标准的实现,提供了比较丰富的资源管理功能,从Windows XP开始集成于Windows操作系统中。下面给出一个在执winrm get winrm/config/clientWinRM客户端与服务端交互的消息,可以看到上面介绍的Resource以及Action。这个消息表示对资源http://schemas.microsoft.com/wbem/wsman/1/config/client进行Get操作。

  1. <s:Envelope
  2.   xmlns:s="http://www.w3.org/2003/05/soap-envelope"
  3.   xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing"
  4.   xmlns:w="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd"
  5.   xmlns:p="http://schemas.microsoft.com/wbem/wsman/1/wsman.xsd">
  6.   <s:Header>
  7.     <a:To>
  8.       http://127.0.0.1:47001/wsman
  9.     </a:To>
  10.     <w:ResourceURI
  11.       s:mustUnderstand="true">
  12.         http://schemas.microsoft.com/wbem/wsman/1/config/client
  13.     </w:ResourceURI>
  14.     <a:ReplyTo>
  15.       <a:Address
  16.         s:mustUnderstand="true">
  17.           http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous
  18.       </a:Address>
  19.     </a:ReplyTo>
  20.     <a:Action
  21.       s:mustUnderstand="true">
  22.         http://schemas.xmlsoap.org/ws/2004/09/transfer/Get
  23.     </a:Action>
  24.     ...
  25.   </s:Header>
  26.   ...
  27. </s:Envelope>
复制代码
WinRM服务返回200响应,附带如下SOAP响应消息:

  1. <s:Envelope
  2.   ...>
  3.   <s:Header>
  4.     <a:Action>
  5.       http://schemas.xmlsoap.org/ws/2004/09/transfer/GetResponse
  6.     </a:Action>
  7.     <a:MessageID>
  8.       uuid:D6A2FE78-BC98-4144-BF60-E425BC57BE58
  9.     </a:MessageID>
  10.     <p:OperationID
  11.       s:mustUnderstand="false">
  12.         uuid:1C91B382-5E0D-4F5D-A4AE-956B5E60F88D
  13.     </p:OperationID>
  14.     <p:SequenceId>
  15.     1
  16.     </p:SequenceId>
  17.     <a:To>
  18.       http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous
  19.     </a:To>
  20.     <a:RelatesTo>
  21.       uuid:3B8307A8-01C9-4C10-86D9-08E051E943F8
  22.     </a:RelatesTo>
  23.   </s:Header>
  24.   ...
  25. </s:Envelope>
复制代码
再来看winrm/config/client这个URI,通过winrm命令可以查询到它是一个别名。

  1. PS> winrm help aliases
  2. wmi      = [url]http://schemas.microsoft.com/wbem/wsman/1/wmi[/url]
  3. wmicimv2 = [url]http://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2[/url]
  4. cimv2    = [url]http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2[/url]
  5. winrm    = [url]http://schemas.microsoft.com/wbem/wsman/1[/url]
  6. wsman    = [url]http://schemas.microsoft.com/wbem/wsman/1[/url]
  7. shell    = [url]http://schemas.microsoft.com/wbem/wsman/1/windows/shell[/url]
复制代码
0x01 在Windows中使用WinRM
本小节存在大量命令行实例,如未特别指定则默认在Powershell环境下进行测试。
配置WinRM
从Windows Server 2012 R2开始,WinRM服务默认自动启动,并监听0.0.0.0:5985和127.0.0.1:47001,更老的版本需要使用命令行进行配置。
  • 使用winrm命令的quickconfig子命令快速配置winrm服务:
    1. winrm quickconfig -q -force
    2. # -q[uiet] 安静模式,不提示确认操作
    3. # -force 已经配置过不提示确认
    复制代码
    quickconfig主要改动的地方:
    • 配置winrm服务为自启动;
    • 启用对远程管理服务的防火墙例外;
    • 添加默认端口的监听器。

    • Powershell中也有对应的cmdlet能够进行快速配置:


    Set-WSManQuickConfig -Force

    • 某些情况下也可以手动修改WinRM服务与相关注册表以及防火墙来达到和快速配置相同的目的:

  1. # winrm服务自启动
  2. sc.exe config winrm start= auto
  3. # 添加HTTP的监听器,默认监听5985端口
  4. reg.exe add HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WSMAN\Listener\*+HTTP /v Port /t reg_dword /d 5985 /f
  5. # 添加监听的url前缀,默认是wsman
  6. reg.exe add HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WSMAN\Listener\*+HTTP /v uriprefix /t reg_sz /d wsman /f
  7. # 启动服务
  8. sc.exe start winrm
  9. # 启用防火墙例外
  10. netsh advfirewall firewall set rule group="Windows 远程管理" new enable=yes
复制代码
客户端侧,需要配置允许连接任意远程WinRM服务,否则将无法访问任何外部WinRM服务,本文的末尾会介绍如何绕过这一限制;Powershell中实现了一个名为WSMan的驱动器,能够如同操作注册表那样访问WinRM的配置。

  1. # 透过Powershell
  2. Set-Item WSMan:\localhost\Client\TrustedHosts * -Force
  3. # 或者
  4. winrm set winrm/config/client '@{TrustedHosts="*"}'
  5. # 或者
  6. reg.exe add HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WSMAN\Client /v trusted_hosts /t reg_sz /d * /f
  7. # 查看配置项列表
  8. Get-ChildItem WSMan:\localhost
  9. # 修改配置
  10. Set-Item WSMan:\localhost\Client\TrustedHosts * -Force
  11. # 读取配置,可用TAB键自动补全
  12. Get-Item WSMan:\localhost\Client\TrustedHosts
复制代码
连接远程Powershell会话WinRM实现WSMan的规范并扩展增强了部分的管理能力,通过WinRM能够连接到交互式的Powershell,支持断开重连,相当于是Windows版本的ssh。在Powershell中使用WinRM:

  1. # 创建会话
  2. New-PSSession -ComputerName 10.1.1.1 -Port 5985 -Name NicheSess
  3. # 从远端获取一个已有的会话
  4. Get-PSSession -ComputerName 10.1.1.1 -Port 5985
  5. # 进入会话,会话中exit退出后可通过此方式再次进入
  6. Enter-PSSession (Get-PSSession -ComputerName 10.1.1.1)
  7. Enter-PSSession -Name NicheSess
  8. # 通过GUID获取缓存会话
  9. Get-PSSession -InstanceId 'a7be4d04-d1d6-4ba7-b6b4-10b7bed6b5df'
  10. # 通过名称获取缓存会话
  11. Get-PSSession -Name NicheSess
复制代码
执行命令
  1. winrs.exe -r:127.0.0.1:47001 -u:Administrator -p:wkpass@321 -nop
  2. # -nop指定不加载目标用户的配置文件,建议渗透时使用此选项
  3. # 指定-un可使流量不加密,方便调试
复制代码
访问WMI推荐在WinRM可用时使用WinRM替代WMI来规避WMI的流量特征,由于远程WMI使用RPC作为底层协议,从流量层面来看会更加敏感一些,而WinRM的http(s)流量通过可配置url前缀、端口号更能达到隐蔽效果。这里给出两种WinRM访问WMI的方法:winrm命令中使用enum枚举WMI对象,并使用invoke子命令调用对象上的方法:

  1. # 获取进程列表
  2. winrm enum wmicimv2/Win32_Process
  3. # 创建进程
  4. winrm invoke create wmicimv2/Win32_Process '@{CommandLine="calc.exe"}'
  5. # 获取共享列表
  6. winrm enum wmicimv2/Win32_Share
  7. # 添加共享
  8. winrm invoke create wmicimv2/Win32_Share '@{Path="D:\Test";Name="ddd";Type="0"}'
  9. # 查询WQL
  10. winrm enum wmicimv2/* -filter:'select * from Win32_Process where Name="CalculatorApp.exe"'
复制代码
  • Powershell使用-Filter参数枚举WMI对象:

  1. # 查询 lsass.exe 进程
  2. Get-WSManInstance -ComputerName 10.1.1.60 -Filter 'select Processid,commandline from Win32_Process where caption="lsass.exe"' -Enumerate -ResourceURI wmicimv2/*
复制代码
0x02 WinRM插件
https://learn.microsoft.com/en-us/windows/win32/winrm/winrm-plugin-api
WinRM提供了插件机制,可加载native的dll作为插件来扩展WinRM的能力,用户注册自己的插件到WinRM以提供自定义资源。
WinRM把许多原生功能作为插件加载,比如前面用到的shell、powershell、wmi,通过reg query查询所有已注册插件:
  1. PS> reg query HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WSMAN\Plugin

  2. HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\WSMAN\Plugin\Event Forwarding Plugin
  3. HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\WSMAN\Plugin\Microsoft.PowerShell
  4. HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\WSMAN\Plugin\WMI Provider
  5. ...
  6. PS> reg query HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WSMAN\Plugin\Microsoft.PowerShell
  7. ConfigXML    REG_SZ                 <PlugInConfiguration...
复制代码
插件运行在与WinRM服务独立的进程wsmprovhost.exe中,如果为插件开启了进程共享,那么多个插件将运行在同一进程。查看插件配置:

  1. PS> ls WSMan:\localhost\Plugin\microsoft.powershell\
  2. WSManConfig:Microsoft.WSMan.Management\WSMan::localhost\Plugin\microsoft.powershell

  3. Type           Name                           SourceOfValue   Value
  4. ----           ----                           -------------   -----
  5. System.String   xmlns                                         http://schemas.microsoft.com/wbem/wsman/1/config/Plug...
  6. System.String   Name                                          microsoft.powershell
  7. System.String   Filename                                      %windir%\system32\pwrshplugin.dll
  8. System.String   Enabled                                       true
  9. System.String   Architecture                                  64
  10. System.String   UseSharedProcess                              false
  11. System.String   ProcessIdleTimeoutSec                         0
  12. System.String   AutoRestart                                   false
复制代码
利用场景在渗透活动中,如果已经获取了主机的管理权限,那么可以注册自己的插件来实现权限维持,还可以将原生的插件(比如pwrshplugin.dll)替换为红队的恶意DLL文件,当管理员或者自动化程序通过WinRM连接时,达到自启动的目的。一个简单的插件

  1. // 包含必要头文件
  2. #define WSMAN_API_VERSION_1_0
  3. #include <wsman.h>

  4. // 当客户端对资源调用Get操作时会调用服务端的WSManProvGet函数
  5. extern "C" __declspec(dllexport) void WSManProvGet(WSMAN_PLUGIN_REQUEST * request)
  6. {
  7.    WSMAN_DATA data;
  8.    data.type = WSMAN_DATA_TYPE_TEXT;
  9.    // 输出格式为XML,真的是很麻烦啊~
  10.    data.text.buffer =
  11.        L"<test:Test xmlns:test="http://schemas.evilcorp.com/test/Test">\n"
  12.        "<Title>Test WinRMPlugin</Title>\n"
  13.        "<Username>Jonathan</Username>\n"
  14.        "<Message>Hello!</Message>\n"
  15.        "</test:Test>";
  16.    data.text.bufferLength = 163;
  17.    // 输出结果
  18.    WSManPluginObjectResult(request, 0, &data);
  19.    // 完成当前Get操作
  20.    WSManPluginOperationComplete(request, 0, 0, nullptr);
  21. }
复制代码
编译为DLL放到C:\Program Files\winrmplug.dll,注意:编译需要链接WSMSVC.lib,否则会报错找不到符号。注册插件
插件注册需要用到一个XML,这个XML中包含了WinRM服务需要的所有插件配置信息:

  1. <PlugInConfiguration xmlns="http://schemas.microsoft.com/wbem/wsman/1/config/PluginConfiguration"
  2.                     Name="Test"
  3.                     Filename="C:\Program Files\winrmplug.dll"
  4.                     SDKVersion="1"
  5.                     XmlRenderingType="text"
  6.                     Architecture="64"
  7.                     UseSharedProcess="true"
  8.                     Enabled="true">
  9.    <!-- 初始化参数,直接会传递给WSManPluginStartup函数的第三个参数extraInfo -->
  10.    <InitializationParameters>
  11.    </InitializationParameters>

  12.    <Resources>
  13.        <Resource ResourceUri="http://test.evilcorp.com/Test" SupportsOptions="true" ExactMatch="true">
  14.            <!-- 可为每一个资源单独配置安全描述 -->
  15.            <Security Uri="http://test.evilcorp.com/Test" ExactMatch="true" Sddl="O:NSG:BAD:P(A;;GA;;;BA)(A;;GA;;;IU)(A;;GA;;;RM)S:P(AU;FA;GA;;;WD)(AU;SA;GXGW;;;WD)"/>
  16.            <!-- 定义当前资源上允许的动作 -->
  17.            <Capability Type="Get"/>
  18.        </Resource>
  19.    </Resources>
  20. </PlugInConfiguration>
复制代码
命令行注册命令为:

  1. winrm create winrm/config/plugin?Name=Test -file:test.xml
  2. # 注册完成后需要重启winrm服务来应用更改,这是一个很大的缺陷
  3. sc.exe stop winrm
  4. sc.exe start winrm
复制代码
执行winrm get http://test.evilcorp.com/Test验证:
编辑
插件的限制

           
  •         插件DLL只能放在%windir%\System32(32位在SysWOW64)和%ProgramFiles%或子目录;

           
  •         新插件注册需要重启WinRM服务(但是修改已有插件会自动应用修改);
            解决:修改现有插件的注册表配置,将XML中的Filename修改到c:\Program Files,编写DLL动态转发原DLL相关功能,并实现自己的目的。

           
  •         插件输入输出很麻烦,必须是XML格式,不利于工具开发;
            解决:WsmSvc.dll提供了SHELL功能,能够直接收发二进制数据。

           
  •         无法随WinRM服务自动启动,需有客户端访问才会加载插件DLL。

0x03 Remote PSSession 输入输出传递分析前面说到了二进制输入输出的需求,如果直接使用WSManPluginObjectResult是无法直接输出二进制数据的,需要自己构造,并且Get等动作无法实现”长连接“效果。而这些问题在远程的PSSession中不存在,那么是否可以深入了解其实现原理,借鉴到自己的插件上呢?
WSMan.Automation首先来看winrm的执行过程,执行winrm时最终执行的是winrm.vbs,通过查看该文件源码可得知最终是通过WSMan.Automation这一COM完成操作。

           

  •        

'Create an instance of the WSMAN.Automation Classset wsmanObj = CreateObject("WSMAN.Automation")
使用OleView查看该COM所包含的TypeLib定义,其中比较关键的是IWSManEx以及IWSManSession,IWSManSession中看到比较熟悉的Action相关方法:
编辑
编辑
没有看到与数据交互有关的方法,同时winrm也并没有提供一个叫做shell的子命令。
程序集Microsoft.WSMan.Management中用到了{BCED617B-EC03-420b-8508-977DC7A686BD} COM,也就是WSMan.Automation的GUID:
编辑
WsmSvc.dll!WSManSendShellInput再来看远程PSSession执行时的情况,在System.Management.Automation.Runspaces.Internal.ClientRemotePowerShell的构造函数中有这样一段:
编辑
继续跟进CreatePowerShellDataStructureHandler方法,调用了虚方法CreateClientCommandTransportManager:
编辑
WSManClientSessionTransportManager类实现了CreateClientCommandTransportManager方法:
编辑
WSManClientCommandTransportManager::SendData:
编辑
最后调用WsmSvc.dll!WSManSendShellInput完成输入数据的传递,在MSDN可查询该函数的文档,同时Windows SDK也包含该函数的定义在wsman.h头文件中:
编辑
0x04 实现远程连接命令执行的BOF由于BOF存在的一些限制(比如无法长期保持内存),然而WinRM的API存在持续性回调用户代码的行为,故实现长会话的PSSession比较困难,这里采用执行一次连接一次的方式,只为给大家提供一个思路~
Github链接:https://github.com/219adlab/winrmsh
测试环境: CS4.5
编辑
0x05 总结WinRM是一个成熟的、在Windows生态中应用广泛的管理协议,在渗透实战中可优先考虑使用WinRM进行横向和权限维持,通过本文对WinRM原理、实战的一些分析,希望能为攻击与防御带来更多的思路。



回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-3-29 18:03 , Processed in 0.019569 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表