spring boot 整合shiro 实践(顺便复现下CVE-2020-13933)
以前遇到过 apache shiro的反序列化漏洞,虽然利用poc都能执行,但是 apache shiro 到底是干嘛用的一直也没研究。今天学习下这个框架的使用,为之后渗透有apache shiro的服务做准备。
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
shiro主要有三大功能模块:
1. Subject:主体,一般指用户。
2. SecurityManager:安全管理器,管理所有Subject,可以配合内部安全组件。(类似于SpringMVC中的DispatcherServlet)
3. Realms:用于进行权限信息的验证,一般需要自己实现。
简单理解就是做用户认证和权限管理的
首先创建一个spring boot项目,引入spring web依赖
pom.xml引入相关依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.5.3</version>
</dependency>
application.properties配置shiro的基本信息
shiro.sessionManager.sessionIdCookieEnabled=true shiro.sessionManager.sessionIdUrlRewritingEnabled=true shiro.unauthorizedUrl=/unauthorizedurl shiro.web.enabled=true shiro.successUrl=/index shiro.loginUrl=/login
创建realm,实现简单用户认证
public class MyRealm extends AuthorizingRealm { @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { return null; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { String username = (String) token.getPrincipal(); if (!"test".equals(username)) { throw new UnknownAccountException("账户不存在!"); } return new SimpleAuthenticationInfo(username, "test123", getName()); } }
在配置ShiroConfig
@Configuration public class ShiroConfig { @Bean MyRealm myRealm() { return new MyRealm(); } @Bean DefaultWebSecurityManager securityManager() { DefaultWebSecurityManager manager = new DefaultWebSecurityManager(); manager.setRealm(myRealm()); return manager; } @Bean ShiroFilterChainDefinition shiroFilterChainDefinition() { DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition(); definition.addPathDefinition("/doLogin", "anon"); definition.addPathDefinition("/hello/*", "authc"); return definition; } }
controller:
@RestController public class LoginController {
@PostMapping("/doLogin")
public void doLogin(String username, String password) {
Subject subject = SecurityUtils.getSubject();
try {
subject.login(new UsernamePasswordToken(username, password));
System.out.println("登录成功!");
} catch (AuthenticationException e) {
e.printStackTrace();
System.out.println("登录失败!");
}
}
@GetMapping("/hello")
public String hello() {
return "hello";
}
@GetMapping("/hello/{name}")
public String hellotest(@PathVariable("name") String name) {
return name;
}
@GetMapping("/test3/{name}")
public String test3(@PathVariable("name") String name) {
return name;
}
@GetMapping("/login")
public String login() {
return "please login!";
}
}
运行程序,访问http://127.0.0.1:8080/hello/test都会跳转到登录http://127.0.0.1:8080/login。
post请求:
POST http://127.0.0.1:8080/doLogin HTTP/1.1 Content-Type: application/x-www-form-urlencoded cache-control: no-cache Postman-Token: 7e5a8744-9a42-4c9b-aae5-2cc1484c2927 User-Agent: PostmanRuntime/7.4.0 Accept: */* Host: 127.0.0.1:8080 cookie: JSESSIONID=91AE16DC971D88083E6396B8CF521CE1 accept-encoding: gzip, deflate content-length: 30 Connection: keep-alive username=test&password=test123
返回登录成功。
之后的
http://127.0.0.1:8080/hello
http://127.0.0.1:8080/hello/test
就都可以正常返回了。不得不说这样做权限管控确实很方便。不用担心会有未授权访问漏洞了。
但是出了
shiro < 1.6.0的认证绕过漏洞(CVE-2020-13933)
通过一些特殊字符编码可以绕过认证
正常访问http://127.0.0.1:8080/hello/test 需要跳转
127.0.0.1:8080/hello/%3btest 即可绕过权限验证
测试发现1.4.0没有这个问题,应该也是特定版本有这样的问题。那么可以去找一些使用了shiro的系统,通过js提取相关的url地址,如果做了认证访问限制的,即可加入这样特殊字符查看能否绕过