0x00 前言在上篇文章《VMware Workspace ONE Access漏洞调试环境搭建》 提到連接數據庫的口令加密保存在文件/usr/local/horizon/conf/runtime-config.properties中,本文將要基於調試環境,分析加密流程,介紹詳細的解密方法。
0x01 簡介本文將要介紹以下內容
加密流程
解密方法
數據庫操作
0x02 加密流程1.定位關鍵文件經過一段時間的尋找,找到實現加密功能對應的文件為/opt/vmware/certproxy/lib/horizon-config-encrypter-0.15.jar
反編譯獲得加密的實現代碼如下:
publicfinalStringencrypt(byte[]data){
if(data!=nulldata.length!=0){
if(!this.getKeyMgmt().randomKeyEnabled()!this.getKeyMgmt().customKeysAvailable()){
log.error('Nocustomencryptionkeysavailable,abortingencrypt.');
returnnull;
}else{
CipherencryptCipher=this.getEncryptCipher();
try{
if(encryptCipher!=null){
byte[]utf8=ArrayUtils.addAll(encryptCipher.getIV(),encryptCipher.doFinal(data));
ByteBufferkeyBuffer=ByteBuffer.allocate(2);
keyBuffer.putShort(this.getKeyMgmt().getCurrentKey());
utf8=ArrayUtils.addAll(keyBuffer.array(),utf8);
utf8=ArrayUtils.insert(0,utf8,newbyte[]{(byte)this.getKeyMgmt().getCurrentCipherVersion()});
byte[]dec=Base64.encodeBase64(utf8);
returnnewString(dec,StandardCharsets.US_ASCII);
}
}catch(IllegalBlockSizeException|IllegalStateException|BadPaddingExceptionvar6){
log.error(var6.getMessage(),var6);
}
returnnull;
}
}else{
returnnull;
}
}2.動態調試
為了提高分析效率,採取動態調試的方法,流程如下:
(1)新建Java工程
下載VMware Workspace ONE Accessd服務器中/opt/vmware/certproxy/lib/下的所有jar文件並保存,在Java工程導入以上jar文件
新建package:com.vmware.horizon.common.utils.config
新建文件ConfigEncrypterImpl.java,內容如下:
packagecom.vmware.horizon.common.utils.config;
importcom.google.common.annotations.VisibleForTesting;
importcom.vmware.horizon.api.ConfigEncrypter;
importcom.vmware.horizon.random.SecureRandomUtils;
importcom.vmware.horizon.security.SecurityProviderHelper;
importjava.nio.ByteBuffer;
importjava.nio.charset.Charset;
importjava.nio.charset.StandardCharsets;
importjava.security.InvalidAlgorithmParameterException;
importjava.security.InvalidKeyException;
importjava.security.NoSuchAlgorithmException;
importjava.security.SecureRandom;
importjavax.annotation.Nonnull;
importjavax.annotation.Nullable;
importjavax.crypto.BadPaddingException;
importjavax.crypto.Cipher;
importjavax.crypto.IllegalBlockSizeException;
importjavax.crypto.NoSuchPaddingException;
importjavax.crypto.SecretKey;
importjavax.crypto.spec.IvParameterSpec;
importjavax.crypto.spec.SecretKeySpec;
importorg.apache.commons.codec.binary.Base64;
importorg.apache.commons.lang3.ArrayUtils;
importorg.apache.commons.lang3.StringUtils;
importorg.bouncycastle.crypto.fips.FipsUnapprovedOperationError;
importorg.slf4j.Logger;
importorg.slf4j.LoggerFactory;
publicclassConfigEncrypterImplimplementsConfigEncrypter{
publicstaticfinalCharsetencodingCharset;
privatestaticfinalLoggerlog;
privatestaticfinalSecureRandomsrand;
privatestaticConfigEncrypterImplstaticKeyInstance;
privatestaticfinalConfigEncrypterImplrandomKeyInstance;
privatestaticfinalObjectkeyInstanceLock;
privateConfigEncrypterKeyMgmtkeyMgmt;
privatestaticConfigEncrypterImplcreateRandomKeyInstance(){
SecurityProviderHelper.initializeSecurityProvider();
returnnewConfigEncrypterImpl(false);
}
publicstaticConfigEncrypterImplgetInstance(){
synchronized(keyInstanceLock){
if(staticKeyInstance==null){
staticKeyInstance=newConfigEncrypterImpl(true);
}
returnstaticKeyInstance;
}
}
publicstaticConfigEncrypterImplgetRandomKeyInstance(){
returnrandomKeyInstance;
}
privateConfigEncrypterImpl(booleanuseStaticKey){
if(useStaticKeyBoolean.parseBoolean(ConfigPropertiesUtil.getProperties().getProperty('components.configEncrypter.kms.enable'))){
log.info('Notinitializingstaticconfigkeystore.UsingKMSforsecureconfigproperties');
this.keyMgmt=null;
}else{
this.keyMgmt=newConfigEncrypterKeyMgmt(useStaticKey);
}
}
@VisibleForTesting
ConfigEncrypterImpl(ConfigEncrypterKeyMgmtkeyMgmt){
this.keyMgmt=keyMgmt;
}
@Nullable
publicfinalStringdecrypt(Stringdata){
if(StringUtils.isBlank(data)){
returnnull;
}else{
byte[]encrypted=data.getBytes(encodingCharset);
booleanb64;
try{
b64=Base64.isBase64(encrypted);
}catch(ArrayIndexOutOfBoundsExceptionvar11){
b64=false;
}
if(b64){
encrypted=Base64.decodeBase64(encrypted);
}
if(ArrayUtils.isEmpty(encrypted)){
returnnull;
}else{
intcipherVersion=Math.abs(encrypted[0]);
CipherdecryptCipher=null;
if(cipherVersion=this.getKeyMgmt().getMinCipherVersion()cipherVersion0){
returnnewString(utf8,encodingCharset);
}
log.debug('zerolengthdecryption');
}catch(BadPaddingExceptionvar7){
log.debug('Failedtodecryptthegivenvalue(padding)');
}catch(IllegalBlockSizeExceptionvar8){
log.debug('Failedtodecryptthegivenvalue(blocksize)');
}catch(ArrayIndexOutOfBoundsExceptionvar9){
log.debug('Failedtodecryptthegivenvalue(macverification)');
}catch(IllegalStateExceptionvar10){
log.debug('Failedtodecryptthegivenvalue(illegalstate)');
}
}
returnnull;
}
}
}
@Nullable
publicfinalStringencrypt(@NonnullStringdata){
returnStringUtils.isBlank(data)?null:this.encrypt(data.getBytes(encodingCharset));
}
@Nullable
publicfinalStringencrypt(byte[]data){
if(data!=nulldata.length!=0){
if(!this.getKeyMgmt().randomKeyEnabled()!this.getKeyMgmt().customKeysAvailable()){
log.error('Nocustomencryptionkeysavailable,abortingencrypt.');
returnnull;
}else{
CipherencryptCipher=this.getEncryptCipher();
try{
if(encryptCipher!=null){
byte[]utf8=ArrayUtils.addAll(encryptCipher.getIV(),encryptCipher.doFinal(data));
ByteBufferkeyBuffer=ByteBuffer.allocate(2);
keyBuffer.putShort(this.getKeyMgmt().getCurrentKey());
utf8=ArrayUtils.addAll(keyBuffer.array(),utf8);
utf8=ArrayUtils.insert(0,utf8,newbyte[]{(byte)this.getKeyMgmt().getCurrentCipherVersion()});
byte[]dec=Base64.encodeBase64(utf8);
returnnewString(dec,StandardCharsets.US_ASCII);
}
}catch(IllegalBlockSizeException|IllegalStateException|BadPaddingExceptionvar6){
log.error(var6.getMessage(),var6);
}
returnnull;
}
}else{
returnnull;
}
}
@Nullable
privateCiphergetDecryptCipher(intcipherVersion,byte[]decryptionKey,byte[]iv){
CipherdecryptCipher=null;
if(!ArrayUtils.isEmpty(iv)){
try{
decryptCipher=Cipher.getInstance(this.getKeyMgmt().getCipher(cipherVersion),SecurityProviderHelper.getJceProvider());
IvParameterSpecivSpec=newIvParameterSpec(ArrayUtils.subarray(iv,0,this.getKeyMgmt().getCipherNonceSize(cipherVersion,decryptCipher.getBlockSize())));
SecretKeysecret=newSecretKeySpec(decryptionKey,this.getKeyMgmt().getCipher(cipherVersion));
decryptCipher.init(2,secret,ivSpec,srand);
}catch(InvalidAlgorithmParameterException|InvalidKeyException|NoSuchAlgorithmException|NoSuchPaddingException|IllegalArgumentException|FipsUnapprovedOperationErrorvar7){
log.error(var7.getMessage(),var7);
decryptCipher=null;
}
}
returndecryptCipher;
}
@Nullable
privateCiphergetEncryptCipher(){
CipherencryptCipher=null;
try{
encryptCipher=Cipher.getInstance(this.getKeyMgmt().getCipher(),SecurityProviderHelper.getJceProvider());
byte[]iv=newbyte[this.getKeyMgmt().getCipherNonceSize(encryptCipher.getBlockSize())];
srand.nextBytes(iv);
SecretKeysecret=newSecretKeySpec(this.getKeyMgmt().getKey(),this.getKeyMgmt().getCipher());
IvParameterSpecivSpec=newIvParameterSpec(iv);
encryptCipher.init(1,secret,ivSpec,srand);
}catch(InvalidAlgorithmParameterException|IllegalArgumentException|NoSuchPaddingException|NoSuchAlgorithmException|InvalidKeyException|FipsUnapprovedOperationErrorvar5){
log.error(var5.getMessage(),var5);
}
returnencryptCipher;
}
@VisibleForTesting
ConfigEncrypterKeyMgmtgetKeyMgmt(){
returnthis.keyMgmt;
}
@VisibleForTesting
publicvoidsetCustomEncryptionKeyst