帆软数据库配置解密

在某次红蓝演练中,通过帆软反序列化拿下服务器之后,发现数据库的配置信息被加密,通过查看帆软源代码最终将其解密。

decode

环境搭建

使用docker搭建

1
docker run -p 8081:8080 -p 8082:8000  -d ysslang/finedocker:persist-2022.05.20

由于MacOS容器跟宿主机不在同一个网络中,可以使用docker.for.mac.host.internal进行连接(容器连接宿主机)

修改容器里tomcat的catalina.sh文件,启动jdwp调试端口

1
export JAVA_OPTS='-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000'

重启容器

分析

前端进行配置操作时,会发起config/finedb请求

com.fr.web.controller.decision.api.migration.MigrationResource.updateFineDBConfig

初始化DB

根据路由日志可得知该请求为:

com.fr.web.controller.decision.api.migration.MigrationResource.updateFineDBConfig

1
2023-03-29 12:07:27 29-Mar-2023 12:07:27.258 信息 [localhost-startStop-1] com.fr.third.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping.register Mapped "{[/{version}/migration/config/finedb],methods=[POST]}" onto public com.fr.decision.webservice.Response com.fr.web.controller.decision.api.migration.MigrationResource.updateFineDBConfig(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse,java.lang.String,com.fr.decision.webservice.bean.migration.DBConfigBean) throws java.lang.Exception

updateFineDBConfig

跟进updateFineDBConfig

1
2
3
4
public void updateFineDBConfig(DBConfigBean var1) throws Exception {
var1.setPassword(this.getFineDBPassword(var1));
MigrationContext.getInstance().updateFineDBConfig(var1);
}

跟进updateFineDBConfig

1
2
3
public synchronized void updateFineDBConfig(DBConfigBean var1) {
MigrationDBConfiguration.getInstance().updateCache(var1.toDBOption());
}

跟进updateCache

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public synchronized void updateCache(DBOption var1) {
this.cachedOption = FineDBProperties.getInstance().get();
Properties var2 = var1.getProperties();
Iterator var3 = var2.stringPropertyNames().iterator();

while(var3.hasNext()) {
String var4 = (String)var3.next();
this.cachedOption.addRawProperty(var4, var2.get(var4));
}

if (Log4jConfig.getInstance().getRootLevel() == Level.DEBUG) {
this.cachedOption.addRawProperty("show_sql", true);
this.cachedOption.addRawProperty("format_sql", true);
}

}

跟进getProperties

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public Properties getProperties() {
return (Properties)this.properties.clone();
}

public DBOption read(InputStream var1) throws IOException {
return this.read(var1, (PasswordCipher)null);
}

public DBOption read(InputStream var1, PasswordCipher var2) throws IOException {
this.properties.load(var1);
this.trimValues();
if (var2 != null && this.properties.containsKey("hibernate.connection.password")) {
this.properties.put("hibernate.connection.password", var2.decode(this.properties.getProperty("hibernate.connection.password")));
}

return this;
}

在进行DB配置读取时会调用decode进行解密,并且decode在com.fr.properties.finedb.DBPropertyCipher进行了实现。跟进之后可以看到具体实现解密功能的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public String decode(String var1) {
if (StringUtils.isEmpty(var1)) {
return "";
} else {
String var2;
try {
var2 = StorageEncryptors.getInstance().decrypt(var1, FineDBSecretImpl.KEY.getPrivateKey());
} catch (Exception var4) {
throw new LifecycleFatalError(var4.getMessage(), var4);
}

if (null == var2) {
throw new LifecycleFatalError("Error to decrypt database password!");
} else {
return var2;
}
}
}

可以看到会调用decrypt函数进行解密,并且密钥为FineDBSecretImpl.KEY.getPrivateKey()。跟进之后发现会判断var1是否为空,如果不为空进入findMatchedText进行正则匹配之后返回var2。如果为空,直接返回

StorageEncryptors.getInstance().getCurrentSecurityKeys().getDecodeKey()

1
2
3
4
5
6
7
8
9
10
11
public String getPrivateKey() {
String var1 = this.getText();
if (StringUtils.isNotEmpty(var1)) {
String var2 = this.findMatchedText(var1, PRIVATE_PATTERN);
if (StringUtils.isNotEmpty(var2)) {
return var2;
}
}

return StorageEncryptors.getInstance().getCurrentSecurityKeys().getDecodeKey();
}

跟进getDecodeKey之后,发现有两个实现:RSA、SM2,根据配置文件信息可得知数据库密码为RSA加密。跟进对应实现之后可以发现最终调用的是getDefaultDecodeKey。通过向DefaultKeys.getInstance().getKey传入不同参数获取公钥、私钥。继续跟进之后可以看到DefaultKeys进行初始化操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
private DefaultKeys() {
Properties var1 = this.read("/58f9/default");
Properties var2 = this.read("/8d30/default");
Properties var3 = this.read("/53c1/default");
Iterator var4 = var1.entrySet().iterator();

while(var4.hasNext()) {
Map.Entry var5 = (Map.Entry)var4.next();
String var6 = var5.getKey().toString();
this.keys.put(var6, var5.getValue().toString() + var2.getProperty(var6, "") + var3.getProperty(var6, ""));
}

}

跟进read函数,可发现最终是由com/fr/security/encryption/storage/keys/impl/var1进行获取

RSA Pri

RSA公钥、私钥

1
2
3
rsa_pub=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAj0yc/l+39O1XukrG1cA4rmJEDlmfdUZHVWFrFkYA3XvZI9FQIYjx/irVurCtXsgn88xWlvEMAlKQVdU5EDvv5q
rsa_pri=MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCPTJz+X7f07Ve6SsbVwDiuYkQOWZ91RkdVYWsWRgDde9kj0VAhiPH+KtW6sK1eyCfzzFaW8QwCUpBV1TkQO+/mpL71fzctXq3JKEVDiFVr6zWf54eFaPc9NNOwQs3tISZoJ3MU0Bx9dgT5znWa9ZKKg6S05FniX1KHexD5v8aKvvSEmT4BEjakTbGYRlAILW+SzytJprL8u4YT48GS4rjaptx9aQGG9dvwaqdbb7cDrWEVrxVJ1mYSrmqoO8JRIHGNWEBOAA7nupCh/e/XeXKfzOT4WD8ph5PWK7GClWpiMsBHKvZ/WFkGKJlPnI23BVWTf7i4/YDIiH6g0A66M1JrAgMBAAECggEAJ8Xt9TSADH0r0ksa8Q0PLmeb2BfMCHLfLbWCUYZQiyjq1eQsx4IJGLCu7chH9ny7ihF3HyH8YVClOw2ZbwYTygKD9gO/Ptp+hcylnN7kRrXcBmvu03qU1Ooqr0t7eIuw60u3x1kT7
sm2=MzA4MjAxNTEwMjAxMDEwNDIwYzQxYTMyYzRhOWMwMTFhYmE0Yzk2NjA4YjUwMDA1NzllNzA2ZmRmZDA2NDE4NjljNmRjNGJkNDY3MmQ1YWI4ZmEwODFlMzMwODFlMDAyMDEwMTMwMmMwNjA3MmE4NjQ4Y2UzZDAxMDEwMjIxMDBmZmZmZmZmZWZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmMDAwMDAwMDBmZmZmZmZmZmZmZmZmZmZmMzA0NDA0MjBmZmZmZmZmZWZmZmZmZmZmZmZmZmZmZmZmZmZm

解密函数实现

根据上述,可得知直接调用对应decode即可对加密数据进行解密

1
2
3
4
5
6
7
8
9
10
11
package org.passwordDecode;

import com.fr.properties.finedb.DBPropertyCipher;

public class pdDecode {
public static void main(String[] args) {
decodePass = DBPropertyCipher.getInstance().decode(encString.replace("\\r\\n","\n").replace("\\=","="));
System.out.println(decodePass);
}
}

decode

问题

  1. 在使用idea+maven进行打包的时候,发现如果将帆软的lib通过外部库引用的方式,打包之后无法调用(类找不到)。通过mvn install:install将对应jar安装到本地仓库即可
1
2
3
4
5
6
7
8
9
10
11
12
mvn install:install-file -Dfile=fine-core-10.0.jar -DgroupId=com.fr -DartifactId=core -Dversion=10 -Dpackaging=jar
mvn install:install-file -Dfile=fine-accumulator-10.0.jar -DgroupId=com.fr -DartifactId=accumulator -Dversion=10 -Dpackaging=jar
mvn install:install-file -Dfile=fine-activator-10.0.jar -DgroupId=com.fr -DartifactId=activator -Dversion=10 -Dpackaging=jar
mvn install:install-file -Dfile=fine-datasource-10.0.jar -DgroupId=com.fr -DartifactId=datasource -Dversion=10 -Dpackaging=jar
mvn install:install-file -Dfile=fine-decision-10.0.jar -DgroupId=com.fr -DartifactId=decision -Dversion=10 -Dpackaging=jar
mvn install:install-file -Dfile=fine-decision-report-10.0.jar -DgroupId=com.fr -DartifactId=decision-report -Dversion=10 -Dpackaging=jar
mvn install:install-file -Dfile=fine-report-engine-10.0.jar -DgroupId=com.fr -DartifactId=report-engine -Dversion=10 -Dpackaging=jar
mvn install:install-file -Dfile=fine-schedule-10.0.jar -DgroupId=com.fr -DartifactId=schedule -Dversion=10 -Dpackaging=jar
mvn install:install-file -Dfile=fine-schedule-report-10.0.jar -DgroupId=com.fr -DartifactId=schedule-report -Dversion=10 -Dpackaging=jar
mvn install:install-file -Dfile=fine-swift-log-adaptor-10.0.jar -DgroupId=com.fr -DartifactId=swift-log-adaptor -Dversion=10 -Dpackaging=jar
mvn install:install-file -Dfile=fine-third-10.0.jar -DgroupId=com.fr -DartifactId=third -Dversion=10 -Dpackaging=jar
mvn install:install-file -Dfile=fine-webui-10.0.jar -DgroupId=com.fr -DartifactId=webui -Dversion=10 -Dpackaging=jar
  1. 发现打包之后的jar找不到主属性清单,通过使用spring-boot-maven-plugin解决
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.3.7.RELEASE</version>
<configuration>
<mainClass>org.passwordDecode.pdDecode</mainClass>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>