前言
背景为某次红队评估项目在钓鱼、常规打点无果的情况下获取到了一份和目标单位某站点相匹配的代码,自此开始对这套代码进行审计。最终挖掘了众多小漏洞,在目标为springboot的情况下,利用jdk的懒加载特性巧妙串联诸多漏洞后拿下了该站点的shell。
漏洞挖掘和利用
JWT Token伪造漏洞
这个漏洞比较经典,很多系统在判断jwt token的时候会用一个固定的密钥去加解密,这时候如果攻击者在代码中拿到了这个密钥,那么他就可以反向的加密出一个加密的token来伪造任意用户,造成越权
这里就是这种情况,我们用代码中的token可以直接加密的得到一个管理员token。此种情况如果要修复的话,可以再加一个验证,在jwt token解密之后,调取数据库记录,验证这个user是否登录过,如果没登录过就说明这个token是被构造出来的,就应该拒绝登录。
jwt构造代码大致如下
Zip解压缩漏洞
发现某后台上传接口利用了一个第三方jar包做解压缩,这个jar包在解压缩过程中有zip解压缩漏洞,会造成文件在解压时可以落地到任意目录。
到这里为止,以及有了一个能在任意文件夹上传任意文件的漏洞,如果是Tomcat的话,直接传一个jsp就可以getshell了,不过这个系统是一个springboot,就没办法去传shell了。
springboot文件上传getshell之前网上也有人分享过覆盖charsets.jar,通过其懒加载特性去执行任意代码(https://landgrey.me/blog/22/)。核心思想是:如果charsets.jar中的类在系统运行过程中没有被使用过,则这个jar不会被jvm载入内存中。我们用文件上传漏洞把charsets.jar替换为我们的恶意jar,通过http请求包中添加Accept头或者fastjson等方法去主动加载charsets中的类,这时候jvm加载的就是我们的恶意charsets.jar了,在初始化的时候就会执行任意代码,具体的操作可以参考上面链接中的文章。我搭建这个系统后发现,这个系统在启动时有一些类调用到了charsets.jar中的类,所以charset.jar在系统启动的时候就被加载到内存中了,后面再去替换就没用了。
这时候问题陷入了僵局,但在思考后我想到,既然charsets.jar有懒加载的特性,其他的jar是否也有懒加载特性呢?最后找到了jre/lib/ext/nashorn.jar。那么接下来的问题就是我覆盖写了nashorn.jar后,怎么去加载我的恶意类了
sql执行
这个是同事发现的,看来像后门一样,直接执行任意sql语句。印象中是mysql数据库,总之无法通过此处getshell
触发类的实例化
jdbc触发
在对代码进行研究后发现有一个加载jdbc的地方,这里cnfgStr的值是从数据库读到的,因为我们可以执行任意sql语句,所以cnfgStr我们就完全可控了
下面再谈怎么通过jdbc连接触发类加载。一般我们在jdbc反序列化时,常见的jdbc连接字符串如下
1 | jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&statementInterceptors=com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor&user=yso_JRE8u20_calc |
其中statementInterceptors这个参数指定了一个类名,这个类在jdbc连接过程中是要被实例化然后调用的,因此我只要把这个参数中的类名改成我修改的nashorn.jar中的恶意类名就行了
fastjson触发
jdbc触发的问题在于,不能传参数给我们的恶意类(可能有办法,没去细看mysql文档,因为要花时间去挖掘,时间比较紧张),这就意味着我们的恶意内容比如执行java代码或执行系统命令,只能在实例化的时候触发一次。类似于
执行完之后nashorn.jar已经被加载到内存中了,这时候类的内容就不能改了。假如我们选择的是执行任意java代码,如果没注入进去,那后面就没机会再去试了。因此jdbc触发方案可行,但容错率太低,赌不起。
恰好看到了在进入jdbc连接前的代码是Fastjson的parseObject,于是想到能不能用Fastjson来触发类的实例化,Fastjson的好处是在用@type实例化类的时候是可以指定参数的,这样我们可以控制参数的不同,来每次都执行不一样的代码。大概代码如下
因此最后我选择了用fastjson的方式来触发,这样会给我们更多的尝试机会来getshell,容错率比较高
构造懒加载jar包
这个jar包的修改比较讲究,首先我们选择随便nashorn.jar中的一个类,这里打比方就修改jdk.nashorn.tools.Shell类,由于我们用的是fastjson触发,所以要考虑fastjson的版本。不巧的是,当前系统的fastjson是1.2.83的,fastjson1.2.83在实例化类的时候有一个@type白名单,我们的jdk.nashorn.tools.Shell必然不在fastjson白名单里面。这时候我想到了问chatgpt,怎么样可以绕过去这个白名单限制
不过我测试了下,这样在fastjson1.2.83下还是不行,要在类定义上方再加一个@JSONType才行。最后构造出来的恶意类如下:
然后把这个恶意类编译后替换原本的nashorn.jar即可,把这个jar通过之前的zip解压缩漏洞覆盖掉目标本地jdk的nashorn.jar
然后将数据库cnfgStr值改成如下。javaCode中填写自己要执行的java字节码的base64即可。执行完一次再执行第二次的话,更新数据库的javaCode内容再触发一次fastjson调用即可即可
1 | {"@type":"jdk.nashorn.tools.Shell","javaCode":"xxx"} |
总结
实战中漏洞利用>漏洞挖掘,漏洞拿不下目标就都是空谈