内存Dump数据库密码的补充

渗透技巧 2年前 (2022) admin
880 0 0

背景介绍:

之前我在公众号上看到了Beichen师傅发表了一篇关于dump内存数据库密码的文章,于是去分析其底层原理以及实战效果的时候发现了一些问题,借着这篇文章输出给大家。

问题描述:

当我在使用的过程当中发现,dump内存中数据库时候会出现dump不出来的情况,于是我实战了多个目标,发现大多数目标都存在dump不出来的情况,只有少部分情况确实dump出了数据库密码。借着这次机会,我本地搭建了一个项目模拟JDBC的项目运行,然后测试。


内存Dump数据库密码的补充


很简单的用springboot搭建的JDBC项目,运行之后使用beichen提供的代码注入到目标进程当中。为了调试方便,我在hook的函数当中打印了一些调试的信息。


内存Dump数据库密码的补充


例如我打印了运行hook函数时的时间,以及触发的信息,方便查看hook运行的时机。


内存Dump数据库密码的补充


目标项目运行完成。


内存Dump数据库密码的补充列出了jvm运行当中的进程,pid为81817注入。


内存Dump数据库密码的补充

注入成功后打印了hook的类。

然后我查看了本地的jdbc的输出目录并未发现jdbc的打印信息,此处我推测并未触发目标和mysql建立连接connect函数,于是打开登录页面,随便输入账号密码触发查询。

内存Dump数据库密码的补充


内存Dump数据库密码的补充


查看控制台的输出日志确实触发了connect函数hook到了账号密码,但是当我再次在前端提交数据的时候,发现了一个问题,connect函数再也触发不了了。

于是我推测,多处的实战测试都不能dump出密码的原因就在于此。第一次可以dump出来是因为项目刚启动,mysql的连接池并未初始化,当我们提交第一次表单的过程当中顺带触发了connect函数 。但是在后续的过程中因为连接池已经被初始化,再次提交表单就再也无法触发connect函数,也就导致之前下的connect的hook再也无法触发了,也就dump不了内存密码。

解决方法:

既然发现问题是因为连接池初始化的问题,在连接池已经触发的情况下,connect函数将不再触发。于是我查阅了很多资料,只要我在JavaAgent的函数agentmain当中销毁掉连接池,然后在触发页面的数据库查询时将重新触发connect函数dump明文密码。但是,比较可惜的是。我审计了jdbc的所有的注册流程,跟踪了很多链,都没有找到有效的真正销毁连接池的方式。

但是在我查阅资料的期间,发现目标项目的控制台的连接池自动销毁了,并且自动触发了connect函数。


内存Dump数据库密码的补充



于是,我挂着当前项目寻找普遍的规律,在github拉了很多项目然后注入到目标进程当中,同样存在这样的规律。而且经过对触发connect的时间进行对比,发现时长都为半个小时。所以我得出一个结论,数据库连接池有一个超时时间,当数据库初始化的时间到达半个小时,连接池会自动销毁然后再次自动创建触发connect。

借着这样的思想,我又去查阅了设置数据库超时时间的api,在agent注入的阶段,将数据库连接池超时时间设置较小,然后在下hook会直接触发connect函数。

内存Dump数据库密码的补充

带着这样的参数放到目标项目当中进行实验,可惜的是并未达到预期的效果。猜测这样的函数可能更改的是springboot内置的连接池,并非是mysql的。

于是思路再次发生转变,如果我在关闭连接池和设置连接池的超时时间的思路上解决不了问题,那么我是不是可以打满连接池。

我使用python编写了个简单的并发请求,对目标项目进行网络请求,企图打满连接池看看什么效果。


内存Dump数据库密码的补充


内存Dump数据库密码的补充


这次和我想象的一样,果不其然在python发送并发请求的同时,mysql的连接池果然被打满,于是同时触发了connect函数dump出了密码。

但是值得深思的一点是,如果目标存在一些NoSQL的数据库在SQL数据库之前的话,我提供了模型图供大家参考:


内存Dump数据库密码的补充


可以预想到的是,如果在SQL数据库之前存在NOSQL数据库的话,如redis,mongodb等等会先在NOSQL数据库进行查询,没有的情况下才会走SQL数据库。基于这样的条件,我们的python并发脚本就会失效,因为并发并不能走到SQL的层面上去。这是问题之一,问题之二是这样的解决并不优雅,如果对方连接池设置数量较大,并发可能比较不易打满。

于是我又借着这样的思路,查阅我所理解的连接池的数量在数据库哪里配置。经过资料的查阅,发现了这样的字段。


内存Dump数据库密码的补充


原来控制连接池数量是由max_connections这个字段配置的,默认值为151。我把这个值改为1之后。


内存Dump数据库密码的补充


内存Dump数据库密码的补充

内存Dump数据库密码的补充


得出结论,当我们将连接池的最大连接数设置为1的时候,每次提交数据都会触发connect函数,所以我们只需要在agent当中反射获取database对象然后执行修改连接数量之后,触发成功再将connect改回就可以。


内存Dump数据库密码的补充


但是有以下弊端,在修改max_connections的期间,可能业务会有短暂的问题,例如出现


内存Dump数据库密码的补充


的异常此为其一,其二为修改的兼容性。目标采用的技术框架会导致反射获取的连接代码不一样。

思路回归:

到这里我就没有再深究了,这条路走下去其实始终不是最终的解决办法。目标的系统架构,以及技术选型都会在一定程度上影响我们的设计思路。最终最彻底的解决办法一定是想办法摧毁或者关闭连接池,并非是在连接数量上下手。

例如我提出的两种解决思路:

第一种思路:使用python的并发请求持续对目标发起请求,可以打满连接池以至于重新申请连接池触发connect函数,他的弊端就在于对方设置连接数量过大无法打满,或者对方存在nosql数据库请求到不了sql层面。

第二种思路:我找到了设置数据库连接池的函数max_connections字段,默认值为151,在agent函数中修改此字段为较小的数字也能解决问题。但是弊端在于修改max_conections字段过小会一定程度导致业务的正常运行,过大会导致修改毫无意义。而且对于目标框架的选型也有一定要求,不同的框架反射数据库执行的驱动大不相同,兼容性存在问题。

所以基于这样的思想,我们最终的解决办法应该还是在强制摧毁和关闭数据库连接池处,联系作者等更新白piao吧。

原文始发于微信公众号(封阳):内存Dump数据库密码的补充

版权声明:admin 发表于 2022年3月27日 下午2:48。
转载请注明:内存Dump数据库密码的补充 | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...