在某次红蓝演练中,通过帆软反序列化拿下服务器之后,发现数据库的配置信息被加密,通过查看帆软源代码最终将其解密。
环境搭建
使用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
根据路由日志可得知该请求为:
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
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公钥、私钥
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); } }
|
问题
- 在使用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
|
- 发现打包之后的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>
|