导语
Apache ShenYu 是一个异步的,高性能的,跨语言的,响应式的 API 网关。近期发现其历史版本(2.3.0-2.4.0)中存在登录认证绕过的风险,CVE编号为CVE-2021-37580
环境搭建
先从官方仓库克隆一份最新代码到本地,然后切换到相关tag,方便分析漏洞。
git clone https://github.com/apache/incubator-shenyu.git
git checkout v2.4.0
git switch -c v2.4.0
然后按照官方手册启动ShenyuAdminBootstrap
和ShenyuBootstrapApplication
即可,(官方手册点我查看)
漏洞复现
根据CVE-2021-37580描述我们可以知道,该漏洞点在jwt认证的过程中,我看到漏洞描述的第一时间以为是jwt秘钥硬编码导致的漏洞,但是后面分析的时候才发现我格局小了。
查看源码可以知道其认证过程使用的是shiro+jwt认证方式。那么直接找到shiro的验证逻辑进行断点即可跟进整个认证登录流程。
在shenyu-admin/src/main/java/org/apache/shenyu/admin/shiro/config/ShiroRealm.java
中
可以看到两个doGetAuthorizationInfo
方法,其中doGetAuthorizationInfo(final PrincipalCollection principalCollection)
是用来鉴权使用的,我们先跳过,主要是下面的doGetAuthenticationInfo(final AuthenticationToken authenticationToken)
,这个方法是用来进行认证的。
从上图我们可以看到整个认证的代码很简单,首先获取到token,然后判断token是否为空,不为空的话接着调用getUserInfoByToken
方法获取用户信息实体,然后返回一个SimpleAuthenticationInfo
信息,整个逻辑结束,那么用户能不能登录,核心点就是在getUserInfoByToken
方法中,下面是getUserInfoByToken
的代码片段.
String userName = JwtUtils.getIssuer(token);
if (StringUtils.isEmpty(userName)) {
throw new AuthenticationException("userName is null");
}
DashboardUserVO dashboardUserVO = dashboardUserService.findByUserName(userName);
if (dashboardUserVO == null) {
throw new AuthenticationException(String.format("userName(%s) can not be found.", userName));
}
return UserInfo.builder()
.userName(userName)
.userId(dashboardUserVO.getId())
.build();
上面的代码逻辑也很简单,就是根据token获取用户名,然后用户名不为空的话就从数据库中查找该用户名相对应的用户实体信息,找到后就返回一个UserInfo
对象。
接着我们看JwtUtils.getIssuer
这个方法,
可以看到就是简单的decode了下token,然后从里面获取userName
相关的信息并且返回。
看到这里其实已经发现问题了,在目前看到的逻辑中,没有对token是否有效进行校验。那么其实就可以直接伪造一个在token带有userName字段信息的token来绕过验证。
打开网站https://jwt.io/#debugger-io,
然后将左侧Encoded中的的字符串复制,我此处为了方便,直接使用chrome控制台新增了个localstorage的数据,Key为token,值为刚才Encoded中的字符串.
然后刷新页面访问http://localhost:9095/#/home
可以发现已经无需认证了。
在最新的代码中看其ShiroRealm.java
中的代码,发现其已对该漏洞进行了修复。
验证截图
漏洞总结
该漏洞是由于仅仅对token进行了解析,但是并没有对token是否有效进行验证而导致的。在官方修改后的最新版本,作者使用了解析token后获取到用户相关的密码作为了jwt的key,然后对token进行校验是否有效,一举两得的解决了该问题以及jwt秘钥硬编码的会导致的一些安全问题。
修复建议
- 升级到2.4.1以上