WEB中间件漏洞
Shiro
1.Shiro-550
对应CVE-2016-4437
参考:Shiro 550 漏洞学习(一) (yuque.com)
(1)利用版本
1.2.4及以下
(2)漏洞原理
Apache Shiro框架提供了记住密码功能,登录成功的话会将用户的登录信息进行经过AES加密然后base64编码,放在Cookie的rememberMe字段,大致如下

那么在服务端就会对该rememberMe字段进行base64解码,然后AES解密,最后再反序列化,如果我们构造恶意的rememberMe字段,这样就会导致反序列化的RCE漏洞。同时,由于AES加解密的KEY是硬编码写在源码中的,我们可以直接获得。
(3)漏洞环境
简单搭建
源码构建Shiro
参考(48条消息) shiro debug 调试_scanner010的博客-CSDN博客
下载源码:apache/shiro: Apache Shiro (github.com)
然后换成
1.2.4:git checkout shiro-root-1.2.4jstl版本切换打开
shiro/samples/web/pom.xml,更换jstl版本
之后重新
reload一下即可Tomcat部署
然后配置一下
Web界面
如下添加即可

之后运行或调试即可
(4)漏洞流程分析
rememberMe解析流程
1 | AbstractRememberMeManager.getRememberedPrincipals |
大致图解如下

(5)漏洞代码分析
AbstractRememberMeManager.getRememberedPrincipals当一个涉及到用户信息的请求来时,首先是进入
rememberMe管理函数
CookieRememberMeManager.getRememberedSerializedIdentity在
AbstractRememberMeManager.getRememberedPrincipals函数中调用getRememberedSerializedIdentity尝试获取base64解码后的rememberMe字段
AbstractRememberMeManager.convertBytesToPrincipals随即调用
convertBytesToPrincipals函数进行后续操作
AbstractRememberMeManager.decryptconvertBytesToPrincipals函数中调用到decrypt函数,通过getDecryptionCipherKey获取密钥尝试对base64解码后的Cookie进行AES解密
一层一层撸下来,如下所示,可以在
AbstractRememberMeManager中找到对应的硬编码的key1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22public byte[] getDecryptionCipherKey() {
return decryptionCipherKey;
}
public void setDecryptionCipherKey(byte[] decryptionCipherKey) {
this.decryptionCipherKey = decryptionCipherKey;
}
public void setCipherKey(byte[] cipherKey) {
//Since this method should only be used in symmetric ciphers
//(where the enc and dec keys are the same), set it on both:
setEncryptionCipherKey(cipherKey);
setDecryptionCipherKey(cipherKey);
}
public AbstractRememberMeManager() {
this.serializer = new DefaultSerializer<PrincipalCollection>();
this.cipherService = new AesCipherService();
setCipherKey(DEFAULT_CIPHER_KEY_BYTES);
}
private static final byte[] DEFAULT_CIPHER_KEY_BYTES = Base64.decode("kPH+bIxk5D2deZiIxcaaaA==");AbstractRememberMeManager.deserialize解密完成之后,回到
AbstractRememberMeManager.convertBytesToPrincipals函数中即进行反序列化
调用到了
DefaultSerializer.deserialize来完成最后的反序列化过程
(6)漏洞利用
即将我们的恶意反序列化链进行相关编码加密,在登录时勾选rememberMe,然后抓包设置为rememberMe字段发送到服务器即可,常用的链条为CC1,CC6,CC10,cc11
相关
POC使用
ysoserial生成序列化链条cc1然后编码加密即可。
1 | import org.apache.shiro.crypto.AesCipherService; |
(7)漏洞检测
dnslogkey检测key正确则显示deleteMe,反之则显示deleteMe
(8)漏洞修复
官方修复是key的随机生成
2.Shiro-721
这个就先不复现了,比较没有实用价值
Tomcat
1.内存马
参考:Tomcat 内存马学习(一):Filter型 (yuque.com)
这个不能算漏洞,只能算一种写木马的方式吧
由于Tomcat的机制问题,存在很多可以写内存马的地方,依据[wjlshare师傅博客](Tomcat 内存马学习(一):Filter型 – 天下大木头 (wjlshare.com))介绍主要有如下几种
servlet-api类
- filter型
- servlet型
spring类
- 拦截器
- controller型
Java Instrumentation类
- agent型
servlet-api类
先需要探讨一下该机制,大概流程如下

然后简单构建一下环境
- 创建项目

添加一下
Web框架

导入对应
Tomcat的servlet-api依赖


然后就可以创建
Servlet-Filter
创建之后需要在
web/WEB-INF/web.xml中加入创建的filter
这里设置了
url-pattern为/*,表示所有的url都会经过该filter然后配置一下
Tomcat服务器即可


相关代码

Servlet1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
public class Servlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().write("MyServlet");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}MyFilter1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20import javax.servlet.*;
import javax.servlet.annotation.*;
import java.io.IOException;
public class MyFilter implements Filter {
public void init(FilterConfig config) throws ServletException {
}
public void destroy() {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
response.getWriter().write("MyFilter");
chain.doFilter(request, response);
}
}
运行起来即可看到
任意存在的
URL访问都会执行设置的MyFilter,

当然一般是给对应的Servlet来对应的filter,修改一下web.xml里的url-pattern即可
①机制流程
TIPS
在
IDEA中按两下shift键可以快速搜索项目中的类