先模拟一段获取密码的过程,假设用户密码通过一系列的操作存储在配置文件中,在被调用时进行加载并解密还原,如果在使用完毕之后不立即销毁数据,理论上密码信息依旧在内存中,这时候如果能获取内存的所有数据并查找到密码存储的对象数据,就可以不用还原解密过程而轻松取得密码信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
class my_passwd { private String passwd; public my_passwd() { this.passwd = "Changeme_123"; } public String get_passwd() { return this.passwd; } } public class jmap_mat_demon { public static void main(String[] args) { my_passwd test = new my_passwd(); //do something with the password //test = null; try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } } } |
将上面的内容编译并运行,然后查看java运行程序的进程ID(假设为9552),通过jmap导出java堆内存
jmap -dump:file=dump.bin 9552
导出文件之后,使用mat工具打开dump.bin,点击OQL图标进入Query查询工具,执行如下查询
select * from instanceof my_passwd
在查询结果中,获取密码信息
可以查询到密码信息,是因为对象test没有被释放,假如在使用完test对象之后将其释放,那么获取内存数据的时机应该就在对象创建到释放之间,可利用的间隙就会缩小很多,此时在通过手工获取 PID之后进行jmap导出,查询返回结果是没有数据的,相对来说可以减少一些安全风险
参考:
https://my.oschina.net/u/658658/blog/606984
mat下载地址:
https://www.eclipse.org/mat/downloads.php