CVE-2020-1957 Apache Shiro Servlet未授权访问浅析
字数 1635 2025-08-20 18:17:53
Apache Shiro Servlet未授权访问漏洞(CVE-2020-1957)深度分析与教学文档
漏洞概述
CVE-2020-1957是Apache Shiro与Spring Boot集成时存在的一个权限绕过漏洞。攻击者可以构造特殊的URL,利用Shiro和Spring Boot对URL解析的差异,绕过Shiro的权限控制,实现未授权访问受保护的Servlet。
环境要求
- Java环境:Java(TM) SE Runtime Environment (build 1.8.0_112-b16)
- Apache Shiro版本:1.5.1
- Spring Boot版本:1.5.22.RELEASE
漏洞复现环境搭建
项目依赖配置(pom.xml)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.22.RELEASE</version>
<relativePath/>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cve-2020-1957</artifactId>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>7</source>
<target>7</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.5.1</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.5.1</version>
</dependency>
</dependencies>
</project>
核心代码实现
1. Realm实现类
public class Realm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
throws AuthenticationException {
String username = (String) token.getPrincipal();
if (!"rai4over".equals(username)) {
throw new UnknownAccountException("账户不存在!");
}
return new SimpleAuthenticationInfo(username, "123456", getName());
}
}
2. Shiro配置类
@Configuration
public class ShiroConfig {
@Bean
MyRealm myRealm() {
return new MyRealm();
}
@Bean
SecurityManager securityManager() {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(myRealm());
return manager;
}
@Bean
ShiroFilterFactoryBean shiroFilterFactoryBean() {
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(securityManager());
bean.setLoginUrl("/login");
bean.setSuccessUrl("/index");
bean.setUnauthorizedUrl("/unauthorizedurl");
Map<String, String> map = new LinkedHashMap<>();
map.put("/login", "anon");
map.put("/xxxxx/**", "anon");
map.put("/aaaaa/**", "anon");
map.put("/admin", "authc");
map.put("/admin.*", "authc");
map.put("/admin/**", "authc");
map.put("/**", "authc");
bean.setFilterChainDefinitionMap(map);
return bean;
}
}
3. 测试Controller
@RestController
public class TestController {
@RequestMapping(value = "/login")
public String login(String username, String password) {
Subject subject = SecurityUtils.getSubject();
try {
subject.login(new UsernamePasswordToken(username, password));
return "登录成功!";
} catch (AuthenticationException e) {
e.printStackTrace();
return "登录失败!";
}
}
@RequestMapping(value = "/admin", method = RequestMethod.GET)
public String admin() {
return "admin secret bypass and unauthorized access";
}
@RequestMapping(value = "/xxxxx", method = RequestMethod.GET)
public String xxxxx() {
return "xxxxx";
}
}
漏洞复现步骤
- 正常访问
/xxxxx路径 - 无需认证即可访问 - 直接访问
/admin路径 - 会被重定向到登录页面 - 构造恶意URL访问
/xxxxx/..;/admin- 成功绕过认证访问admin内容
漏洞原理深度分析
Shiro处理流程
-
请求拦截:恶意请求
/xxxxx/..;/admin首先经过Shiro的PathMatchingFilterChainResolver#getChain方法处理 -
路径解析:
getPathWithinApplication方法负责解析请求路径WebUtils#getRequestUri获取原始URI/xxxxx/..;/admindecodeAndCleanUriString方法将;后面的内容截断,变为/xxxxx/..normalize方法对路径进行规范化处理,结果仍为/xxxxx/..
-
权限校验:
- 规范化后的路径
/xxxxx/..与过滤器规则/xxxxx/**匹配成功 - 由于
/xxxxx/**配置为anon(允许匿名访问),请求通过Shiro校验
- 规范化后的路径
Spring Boot处理流程
-
请求路由:通过Shiro校验的请求进入Spring Boot的
UrlPathHelper#getPathWithinServletMapping -
Servlet路径解析:
getServletPath方法从请求上下文中获取Servlet路径- Spring Boot的解析器将
/xxxxx/..;/admin解析为/admin - 最终路由到
/admin对应的Controller方法
关键差异点
-
Shiro的路径解析:
- 将
;作为分隔符,截断后面的内容 - 对
..的处理不够彻底,保留了路径遍历的痕迹 - 最终匹配的是
/xxxxx/..与/xxxxx/**
- 将
-
Spring Boot的路径解析:
- 能够正确处理
;和路径遍历符.. - 最终解析出真实的请求路径
/admin
- 能够正确处理
漏洞修复方案
Apache Shiro在后续版本中修复了此漏洞,主要修改了requestURI的获取方式:
- 修复commit: https://github.com/apache/shiro/commit/3708d7907016bf2fa12691dff6ff0def1249b8ce
- 关键修改:改进了路径解析逻辑,确保更准确地获取请求URI
防御建议
- 升级Apache Shiro到最新版本
- 在Shiro配置中,对敏感路径使用更严格的匹配规则
- 避免使用过于宽松的通配符(如
/**) - 实施多层防御机制,不单纯依赖Shiro的URL过滤
总结
CVE-2020-1957漏洞揭示了在安全框架集成时可能存在的解析差异问题。通过深入分析Shiro和Spring Boot对URL处理的不同方式,我们可以更好地理解这类权限绕过漏洞的本质。开发人员在集成多个安全组件时,应当特别注意各组件对请求处理的细微差异,避免形成安全盲点。