Jumpserver-RCE复现及报警规则

影响版本

漏洞分析

简便的方法是,根据影响版本可以得到的信息是,v2.6.2是安全的,所以只需要在github上对比v2.6.2和v2.6.1的版本更迭记录即可。

Compare v2.6.1 and v2.6.2

Analizied 6 changed files with 11 additions and 12 deletions .

uth.py&&ws.py这两个文件,前者删了一个方法,后者加了一些权限校验。接下来逐一分析。

auth.py的代码分析

根据jumpserver官方给的应急解决方案中,定位到了要封禁的接口:

在api_urls.py文件中的url路由规则如下:

UserConnectionTokenApi类对应的是auth.py,所以通过这种方式也可以找到漏洞发生的地方

该类有三个方法:一个POST方法,和一个GET方法,和一个get_permissions方法,各自对应的源码如下:

POST:

GET:

get_permissions:

首先对接口进行功能分析,POST方法实现的是:

post提交user、assest、system_user,该方法会设置生成一个token,并将该数组的对应关系缓存,然后返回token。

get方法实现的是,请求提交token,该方法用token去查user,如果存在就把token对应的user返回。

功能上看起来实现的是,用户通过jumpserver使用不同机器时,服务器按照用户提交参数签发一个20s的token,用户使用该token去访问对应的机器。

但我们使用jumpserver会发现,客户端使用机器时,向服务端提交的参数都是id,且ID随机复杂无规律,通过爆破的方式显然是不合理的,而本次未授权漏洞正是因为log中记录了这些敏感信息,并且可以被攻击者未授权访问到,所以产生了一个漏洞利用链。

ws.py文件分析

Compare connect()

如图为v1.5.9的connect函数。

如图为v2.6.2的connect函数

ws文件里对connect函数加了校验,判断只有认证过的org_admin才能访问该函数,证明这里是个未授权的入口。

继续分析函数,我们通过该未授权接口传入的task参数会传入到read_log_file中。

wait_util_log_path_exist

作用: 读取传入的logpath。并返回 read_log_file

通过以上流程分析的很清楚了,我们只需向服务器发起websocket请求,并提交json格式的taskid:logpath即可获取到日志中的敏感信息。

找到该websocket的访问路径

漏洞利用过程

使用chrome插件 Websocket Client发包如下:

找到包含重要字段的日志:

[18/Jan/2021:12:58:23 +0800] \"GET /api/v1/perms/asset-permissions/user/validate/?action_name=connect&asset_id=12037419-570f-4732-b135-a8287f19f463&cache_policy=1&system_user_id=d90fe502-3263-4ed9-9540-9d5a60a39d42&user_id=adfcaf61-85bd-4102-b5a4-6f339d7f1db4

首先根据ID构造post包获取token令牌:

编写python模拟正常请求即可,用github上大佬ska的脚本[参考链接在后面]:

大佬用协程写了个交互式shell(膜).

modsecurity判定规则

在这种场景下,基本上看到这个uri请求我们就可以断言这是一个报警,因此编写规则如下:

SecRule REQUEST_URI "/ws/ops/tasks/log" "id:11111111,phase:1,id:52,t:none,t:urlDecode,t:lowercase,t:normalizePath,msg:'jump-rce'"

可以通过部署的规则起到一定的报警作用,也可以捕获一些告警流量。

参考链接

https://s.tencent.com/research/bsafe/1228.html

https://github.com/Skactor/jumpserver_rce