一个最小化的、故意存在漏洞的 Spring Boot 应用程序,用于端到端演示 Seal Security 如何通过将有漏洞的依赖替换为封装的(向后移植、直接替换)版本来修复已知 CVE——而无需更改你声明的依赖坐标或代码。
它被设计为 Seal CLI 在 CI/CD 中的端到端冒烟测试:运行应用,触发真实漏洞利用,运行 Seal,然后观察同样的漏洞利用被阻断。
| 生态系统 | Java / Maven |
| 有漏洞的包 | org.yaml:snakeyaml:1.33 |
| CVE | CVE‑2022‑1471 — SnakeYAML Constructor 反序列化 → 远程代码执行 (CVSS 9.8) |
| 封装(修复)版本 | 来自 Seal Maven 仓库的 snakeyaml 1.33+sp1 |
| 集成方式 | Seal CLI 作为一个构建步骤 — 同时展示了 GitHub Actions 和 Jenkins 的用法 |
该项目还声明了其他常见的有漏洞依赖(jackson-databind 2.13.1、commons-text 1.9、spring-core/web 5.3.26、json-smart 2.4.8、commons-lang3 3.12.0、spring-boot 2.7.18),Seal 同样会将每个依赖修复为封装版本。
欢迎页面接收一个 name 参数,并通过 SnakeYAML 的默认构造函数进行解析:
Yaml yaml = new Yaml();
Object parsed = yaml.load(name); // SnakeYAML 1.33 — CVE-2022-1471
SnakeYAML 的默认 Constructor 会实例化 YAML 中指定的任意 Java 类型——这是 CVE‑2022‑1471 背后的对象注入原语。一个命名为 javax.script.ScriptEngineManager(经典 RCE 小工具链的入口点)的 payload 足以证明解析器会构建攻击者命名的任何类。
Normal request
/?name=alice → Welcome, alice!
Exploit request
/?name=!!javax.script.ScriptEngineManager []
存在漏洞的 SnakeYAML 实例化攻击者指定的类,应用显示一个 “你已被攻破” 页面,然后服务器被终止(几秒后,以便页面先加载)。重新加载后应用消失——通过 ngrok 你会看到一个*“端点离线”*页面。这就是对象注入原语;完整的漏洞利用通过 URLClassLoader 链式调用,加载并运行远程代码。
.
├── pom.xml # 声明有漏洞的依赖
├── src/main/java/… # Spring Boot 应用 (HelloController, DataService)
├── .seal-actions.yml # 可选的本地修复模式映射(有漏洞 → 封装)
├── Jenkinsfile # 包含 Seal 阶段的示例 Jenkins (Groovy) 流水线
└── .github/workflows/
├── build-and-run.yml # 构建并暴露应用以供浏览器测试
└── seal-security.yml # 运行 Seal 修复,然后启动应用
Seal 是 SaaS 模式,由 Seal 托管——你的环境中无需安装任何东西,所有流量仅通过 TCP 443 出站 HTTPS。要运行修复,你需要:
| 密钥 / 凭证 | 用途 | 存放位置 |
|---|---|---|
在 Settings → Secrets and variables → Actions (GitHub) 或 Manage Jenkins → Credentials (Jenkins) 中进行配置。切勿将令牌提交到仓库。
将这些 Seal 主机加入出站 443 白名单:app.sealsecurity.io、authorization.sealsecurity.io、cli.sealsecurity.io,以及——用于封装的 Maven 构件——maven.sealsecurity.io。CLI 二进制文件从 github.com / objects.githubusercontent.com 下载。
mvn clean package -DskipTests
java -jar target/java-example-1.0.0.jar # → http://localhost:8080
打开 http://localhost:8080/?name=alice(正常工作),然后打开 http://localhost:8080/?name=!!javax.script.ScriptEngineManager []——应用显示“你已被攻破”,几秒后服务器被终止。
Seal CLI 作为一个额外步骤运行,在依赖解析之后、打包之前。它扫描已解析的依赖,并使用远程修复模式(策略在 Seal UI 中集中管理)将有漏洞的依赖重写为封装版本。
- uses: seal-community/cli-action@latest
with:
mode: fix
fix_mode: remote
token: ${{ secrets.SEAL_TOKEN }}
target: pom.xml # 该生态系统的主要清单文件
通过 Actions → “Seal Security Remediation” → Run workflow 运行。参见 .github/workflows/seal-security.yml。
一个单独添加的阶段,在依赖解析之后、打包之前。参见 Jenkinsfile:
stage('Seal') {
steps {
sh '''
curl -fsSL https://github.com/seal-community/cli/releases/download/latest/seal-linux-amd64-latest -o seal
chmod +x seal
./seal fix --mode remote "$SEAL_MANIFEST" # SEAL_MANIFEST=pom.xml
'''
}
}
SEAL_TOKEN 来自 Jenkins 凭据 seal-token;将 SEAL_PROJECT 设为你的 Seal 项目 ID。
执行 seal fix 后,有漏洞的依赖将解析为来自 Seal Maven 仓库的封装构建——相同的坐标,安全修复向后移植:
封装版本是相同的构件,只是向后移植了安全修复——直接替换,无需代码更改,也无需升级主版本。
对修复后的应用重新运行漏洞利用。封装的 snakeyaml 拒绝实例化恶意全局类型,因此 payload 无法加载远程 JAR —— 远程代码执行被阻断。
seal fix 指向特定的清单文件——Maven 为 pom.xml。对于多模块构建,每个清单文件运行一次 seal fix。这就是整个集成——一个阶段,仅出站,无需更改应用代码。
| 验证 Seal CLI |
GitHub Actions 密钥 SEAL_TOKEN / Jenkins "Secret text" 凭据 seal-token |
| ngrok token (可选) | 将运行中的应用暴露到浏览器以进行测试 | GitHub Actions 密钥 NGROK_TOKEN |
| 依赖 | 之前 | 之后(封装) |
|---|
| org.yaml:snakeyaml | 1.33 | 1.33+sp1 |
| com.fasterxml.jackson.core:jackson-databind | 2.13.1 | 2.13.1+sp1 |
| org.apache.commons:commons-text | 1.9 | 1.9+sp1 |
| org.springframework:spring-core / spring-web | 5.3.26 | 5.3.26+sp1 |
| net.minidev:json-smart | 2.4.8 | 2.4.8+sp1 |
| org.apache.commons:commons-lang3 | 3.12.0 | 3.12.0+sp1 |
| org.springframework.boot:spring-boot | 2.7.18 | 2.7.18+sp1 |