标题 | 简介 | 类型 | 公开时间 | ||||||||||
|
|||||||||||||
|
|||||||||||||
详情 | |||||||||||||
[SAFE-ID: JIWO-2024-3070] 作者: 羽毛 发表于: [2022-04-09]
本文共 [180] 位读者顶过
使用vulhub搭建环境,下载vulhub
spring目录下有docker镜像直接启起来。
访问8080端口即可查看。
环境搭建ok,其实这里使用构造的payload不知道为什么不可以,稍后尝试,先使用exp去执行,在环境中刚好有exp,我们只需要修改目标ip。
修改执行的命令,
执行EXP,
进入docker容器查看是否成功生成数据。
验证EXP成功利用,这里尝试一下反弹shell,在另一台终端监听一个端口。
修改EXP,
得到容器的shell,
由于在线编码的平台不能使用,所以需要自己做一下base64的编码然后再解码,但是这里为什么直接反弹的shell不能够执行呢? 是因为管道符、输入输出重定向,只有在bash环境下才能用。由于项目环境为Java环境不支持管道符、输入输出重定向等。重定向和管道符的使用方式在正在启动的进程的中没有意义。例如ls > 1.txt在shell中执行为将当前目录的列表输出到命名为 1.txt。但是在 exec()函数的中,该命令为解释为获取 >和 1.txt目录的列表。 下载源码
新建项目导入pom.xml文件搭建环境,配置配置文件。
运行本地已搭建,
本地搭建目的是方便调试。 修改代码位置,
修改connect方法, function connect() { var header = {"selector":"T(java.lang.Runtime).getRuntime().exec('calc.exe')"}; var socket = new SockJS('/gs-guide-websocket'); stompClient = Stomp.over(socket); stompClient.connect({}, function (frame) { setConnected(true); console.log('Connected: ' + frame); stompClient.subscribe('/topic/greetings', function (greeting) { showGreeting(JSON.parse(greeting.body).content); },header); }); } 保存后重新运行,Websocket连接,send发送任意信息即可触发calc.exe。
分析本地windows的触发条件更能清楚的理解,exec中代码执行的条件是由于建立socket通信之后发送信息的时候触发的,这里通过下断点来调试。 首先先了解几个概念,没有java框架开发经验的话确实很让人头疼,SpEL表达式,是Spring表达式的简写,能够以一种强大而简洁的方式将值装配到Bean属性和构造器参数中,在这个过程中所使用的表达式会在运行时计算得到值。简单理解就是利用简单的表达形式来实现操作。 SpEL支持如下表达式:
STOMP协议 STOMP是一个简单的可互操作的基于帧的协议, 作用于中间服务器在客户端之间进行异步消息传递,STOMP协议基于TCP协议,类似于HTTP协议,使用了以下命令: CONNECT SEND SUBSCRIBE UNSUBSCRIBE BEGIN COMMIT ABORT ACK NACK DISCONNECT
根据披露的漏洞位置,直接搜索问题类DefaultSubscriptionRegistry
在Protected属性addSubscriptionInternal方法中,定义了selectorHeaderInUse的属性为true。
95行的时候把四个参数,sessinId,subsId,destination(订阅地址("/topic/greetings")以及expression添加进subscriptionRegistry属性中。 app.js修改的代码位置为
private属性中的filterSubscriptions方法在什么时候会触发呢?下断点调试会发现,在send发送信息的时候会传入message参数,这个时候就会调用前端传入的selector构造的内容即SpEL表达式的内容,从第二种的复现方式来看就是这样的,但是在调试的时候正常的利用是首先触发。
118行调用findSubscriptionsInternal函数。
在AbstractSubscriptionRegistry类中找到了在满足else的时候调用了findSubscriptionsInternal函数,可能在这里也许有师傅有点困惑,在这里我们需要明白的是参数destination(订阅地址)和参数message(含有SpEL表达式即payload)的内容。 但是这里有个疑问,那么哪里利用到了STOMP协议的内容呢? 上文提到了STOMP协议的命令,里面涉及到的SUBSCRIBE命令,在SUBSCRIBE命令下selector头值会作为表达式存储,在实现addSubscriptionInternal方法的方法生成sessionID的时候表达式已经实现了存储。 这个时候就很明显了,seesionid的生成就涉及到了websocket实现客户端和服务器之间的交互。
到这里分析就结束了,但是函数调用以及漏洞触发的原因已经分析的比较清楚了。 小结Java的东西忘记的差不多了,IDEA的快捷键都给忘了,突然分析起来很头大,可参考的内容也比较少,走的坑也比较多吧,有问题的地方欢迎师傅们指正。 参考文章
|