Java代码审计-flink-streaming-platform-web
9h0st 发表于 江苏 历史精选 2735浏览 · 2024-04-21 09:05

前言

项目地址:https://github.com/zhp8341/flink-streaming-platform-web
flink-streaming-platform-web是一个将flink封装的一个可视化的、轻量级的flink web客户端系统,用户只需在web 界面进行sql配置就能完成流计算任务。

项目结构


flink-streaming-common: flink流计算相关公共类
flink-streaming-core: flink流计算核心模块
flink-streaming-valication: sql校验模块
flink-streaming-web: web平台模块 1. 2. 用户管理 3. 日志管理 4. 系统配置等.
flink-streaming-web-alarm: web平台报警接口
flink-streaming-web-common: web平台模块公共类
flink-streaming-web-config: web平台配置类

环境搭建
这里搭建的话使用linux比较合适,flink现在支持linux多一些,在win下会出现一些问题
Web平台使用的springboot,所以环境搭起来也比较方便,创建数据库为flink_web,然后执行docs下的flink_web.sql文件即可

审计过程

环境搭建成功之后只有一个后台登录的功能,所以这里先看登录的逻辑是怎么样的,在UserApiController#login方法中打下断点,提交数据包:

先从数据库中查找对应的用户,然后判断用户是否存在和被禁用,后续对传入的密码和用户的密码进行校验,并返回base64编码的session,用户的登录没什么问题,下一步查看是怎么鉴权的

权限绕过

在LoginInterceptor类里面定义了拦截器的具体逻辑:

先判断请求的路径是否为/则重定向到登录页面,然后从session中取出对应的值

继续判断是否为ajax请求:

这里主要判断请求头中是否包含了X-Requested-With并且值为XMLHttpRequest,如果有这个请求头则需要进行权限校验,回到preHandle,WebUtil.isAjaxRequest(request)返回false的话就可以不进入if判断,跳过权限校验了

测试权限绕过添加用户:

POST /api/addUser HTTP/1.1
Host: ***
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:124.0) Gecko/20100101 Firefox/124.0
Accept: application/json, text/plain, */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Referer: https://panflinkweb.lingzhuyun.com/static/ui/index.html
Content-Length: 50
Origin: ***
Connection: keep-alive
Cookie: flink-streaming-platform-web-sessionid=eyJuYW1lIjoidGVzdCIsInBhc3N3b3JkIjoiYTU5MGE3NDU5ODFlYjM0ZTU2ZWY5MzBmNDJkNjMzZDgiLCJ1c2VyaWQiOjJ9; Admin-Token={%22id%22:2%2C%22username%22:%22test%22%2C%22name%22:%22test%22%2C%22avatar%22:%22avatar.gif%22%2C%22introduction%22:%22%22%2C%22roles%22:[%22admin%22]}
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: no-cors
Sec-Fetch-Site: same-origin
Content-Type: application/x-www-form-urlencoded
Pragma: no-cache
Cache-Control: no-cache

name=test3&fullname=test&pwd1=test321&pwd2=test321

具体回显

成功登录

命令执行

在该项目的readme中,描述了怎么去调用的flink

通过java原生的runtime来调用flink,下一步就是去看看具体的调用过程了,是否存在可控的参数
找到具体提交命令的类方法CommandRpcClinetAdapterImpl#submitJob()

这里要确定command,是否可控,继续往上找在哪里调用了在JobBaseServiceAOImpl的submitJobForStandalone和submitJobForYarn俩个方法中都调用了submitJob,继续往上寻找哪里调用了这两个方法


在JobBaseServiceAOImpl#aSyncExecJob()方法中创建线程池的时候,正好创建了Runnable类来执行这些方法
继续往上找哪里调用了aSyncExecJob

这里的两个业务类JobStandaloneServerAOImpl和JobYarnServerAOImpl分别对应提交任务到本地,或者是远程Yarn服务,这里主要看本地的,远程Yarn需要配置,不好满足利用条件

在JobStandaloneServerAOImpl#start()中调用了aSyncExecJob,对应的controller层就是在JobConfigApiController#start()方法中
所以这里如果要测试的话就需要通过上面的绕过权限校验来创建一个额外的用户登录

创建一个新的jar包任务

这里的jar包是可以通过后台的第三方jar包管理上传的(仅作为文件使用,并不能解析),开启任务之后直接启动,进入start方法


getJobServerAO(id)根据id返回对应处理任务的类,进入JobStandaloneServerAOImpl#start


根据id返回对应的配置信息包含在JobConfigDTO类中
后续就是根据配置信息做一些处理,任务执行情况,参数是否合法,插入日志信息等

这里需要注意下writeSqlToFile()方法,在这个方法里面,把一些系统配置(flink的路径,项目的配置路径等信息,yran的地址,flink的地址)设置到了返回值中,后续需要使用,所以这里有利用前提,需要配置了系统的信息之后才会有这个rce的漏洞,不过在搭建环境的时候就需要配置这些系统信息,否则大部分的功能无法使用

系统信息

进入aSyncExecJob方法


这里没有使用到nacos,跳过第一个if,直接进入Runnable#run()方法


配置了一些输出的日志信息之后,设置jar包下载地址

在downJar(jobRunParamDTO, jobConfigDTO)中下载了对应的远程jar包
所以这里也算有一个ssrf,不过没什么太大的危害
然后进入到switch语句中,因为设置的是单机模式,所以进入STANDALONE中


在getFlinkAddress对flink_rest_http_address这个系统配置进行存活校验

进入buildRunCommandForCluster,创建执行flink的命令


先配置flink的可执行目录加上run -d
再根据address,savepointPath,是否为STANDALONE模式,存在第三方jar包等加入对应的命令

进入switch的JAR case中,这里就是在配置任务的时候设置的需要执行的类,这里就是外部可以控制的参数了
所以整体执行的命令是:
/javaTocms/flink-1.9.0/bin/flink run -d -c org.example.Main /javaTocms/tmp/udf_jar/evaljAR.jar
修改org.example.Main为需要执行的命令就可以了
回到submitJobForStandalone方法中


进入submitJob,提交命令

执行命令完成

2 条评论
某人
表情
可输入 255
ddlll
2025-03-10 07:06 0 回复
evaljAR.jar的代码怎么写的,sql流任务代码吗
1544206598782977
2024-04-23 07:20 河北 0 回复

看不懂


目录