欢迎来到 黑吧安全网 聚焦网络安全前沿资讯,精华内容,交流技术心得!

浅析Java序列化和反序列化

来源:本站整理 作者:佚名 时间:2019-01-15 TAG: 我要投稿
我们还是先以大家更熟悉的PHP来举个例。在PHP内部,保留了十多个被称为魔术方法的类方法,这些魔术方法一般会伴随着类的生命周期被PHP底层自动调用,用户可以在类中显式定义它们的逻辑。
就拿与反序列化关系最密切的__wakeup()来说,我们回到最初的那个类SerializationDemo,给它加一点东西:
class SerializationDemo {
    public function __wakeup() {
        echo $this->stringField;
    }
}
在反序列化SerializationDemo这个对象时,就会调用__wakeup()执行里面的逻辑。示例中的逻辑只是输出一个字符串,如果改成exec($this->stringField);呢?
实际当然不会这么简单,有可能它是把自己的字段作为值作为参数调用了某个类的方法,而那个方法里对参数做了某些不安全的操作,甚至有可能经过多个类多个方法调用,形成一个调用链。
这就是默认的反序列化逻辑的一个逃逸过程。
到这里你可能已经想到了,Java反序列化中readObject()的作用其实就相当于PHP反序列化中的那些魔术方法,使反序列化过程在一定程度上受控成为可能,但也只是可能而已,是否真的可控,还是需要分析每个对象的readObject()具体是如何实现的 (别急,后面有章节会有详细介绍) 。
接着看第二个问题:
反序列化对象的非Serializable父类无参构造方法是否能像PHP中的__construct()一样被利用?
答案应该是不行的。因为前面已经提到过,我们只能够控制反序列化对象的字段值,而Java与PHP不同的是,JDK底层会先调用无参构造方法实例化,再读取序列化的字段数据赋值,所以我们没有办法将可控的字段值在实例化阶段传入构造方法中对其内部逻辑产生影响。
最后一个:
readResolve()对反序列化漏洞有什么影响?
readResolve()只是替换反序列化结果对象,若是结果对象本身存在安全问题,它有可能让问题中断;若是readObject()存在安全问题,它无法避免。
经典的Apache Commons Collections
好,有了上面的基础,我们也照一回惯例,带大家一起分析一下Java历史上最出名也是最具代表性的Apache Commons Collections反序列化漏洞。
网上很多文章都是以WebLogic为漏洞环境,我们尊重开源,围绕1.637版本的Jenkins来开个头,先简单看看它的Cli组件的反序列化场景 (这里只以CLI-connect协议为例,CLI2-connect会多出来一个SSL加解密的过程,这也是很多公开PoC在模拟Cli握手时选择CLI-connect协议的原因) :
客户端向发送一个UTF8字符串Protocol:CLI-connect,前两位为字符串长度
服务端TcpSlaveAgentListener在接收到数据之后,会创建一个ConnectionHandler对象读取一个UTF8字符串,判断协议版本,交给对应的协议进行处理
CliProtocol响应Welcome字符串,由ChannelBuilder为两端创建一个包含了Connection对象 (IO流对象在里面)的Channel通信通道,并调用negotiate()进行交互
Capability.writePreamble()响应序列化后的Capability对象,其中使用Mode.TEXT.wrap()将输出流包装为BinarySafeStream,它会在写时进行Base64编码
由于ChannelBuilder在build之前,调用了withMode()设置mode为Mode.BINARY,因此还会响应一个0x00000000
等待接收后续数据,判断数据内容前缀为Capability.PREAMBLE () 时,将InputStream传给Capability.read()
Capability同样会对输入流做一次BinarySafeStream包装,保证在读数据时解码得到原始二进制数据,再扔给输入流的readObject()继续读
回看Connection中自定义的readObject(),是一个普普通通的ObjectInputStream反序列化:
public T> T readObject() throws IOException, ClassNotFoundException {
    ObjectInputStream ois = new ObjectInputStream(in);
    return (T)ois.readObject();
}
现在我们假设已知1.637版本的Jenkins引用了存在反序列化漏洞的Commons Collections的版本的Jar包,那么只需要利用它构造一个恶意对象的序列化串,在与Jenkins Cli完成握手之后,将其Base64编码后的字符串发送过去就行了 (当然,千万别忘了前面那串酷酷的前缀) 。
Payload构造
好的,现在让我们聚焦到Commons Collections内部,看看前辈们是如何利用它来让应用『产生』问题的。
我们先预备一个基本知识,在Java中,若想通过其原生JDK提供的接口执行系统命令,最常见的语句如下:
Runtime rt = Runtime.getRuntime();
rt.exec(cmd);
很简单,一个单例模式的方法获取到Runtime的实例,再调用它的exec()执行命令。在表达式注入类RCE漏洞中也可以频繁看到利用各种条件特性来构造这段语句的身影,比如Struts2的OGNL:
@java.lang.Runtime@getRuntime().exec(cmd)
又比如Spring的SpEL:
T(java.lang.Runtime).getRuntime().exec(cmd)
这里替小白问个基础但又和接下来的内容有关的问题:为什么都要使用链式结构?
原因其实很简单,因为无论是表达式解析执行还是反序列化时,底层通过反射技术获取对象调用函数都会存在一个上下文环境,使用链式结构的语句可以保证执行过程中这个上下文是一致的。你也可以换个方式问自己,如果你第一次请求Runtime.getRuntime(),那如何保证第二次请求rt.exec()能够拿到第一次的Runtime对象呢?
了解了这个问题之后,我们就可以开始尝试用Commons Collections先来构造这个链式结构了。
前辈们为我们在Commons Collections中找到了一个用于对象之间转换的Transformer接口,它有几个我们用得着的实现类:
ConstantTransformer
public ConstantTransformer(Object constantToReturn) {
    super();
    iConstant = constantToReturn;
}
public Object transform(Object input) {
    return iConstant;
}
InvokerTransformer
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
    super();
    iMethodName = methodName;

上一页  [1] [2] [3] [4] [5] [6] [7] [8] [9] [10]  下一页

【声明】:黑吧安全网(http://www.myhack58.com)登载此文出于传递更多信息之目的,并不代表本站赞同其观点和对其真实性负责,仅适于网络安全技术爱好者学习研究使用,学习中请遵循国家相关法律法规。如有问题请联系我们,联系邮箱admin@myhack58.com,我们会在最短的时间内进行处理。
  • 最新更新
    • 相关阅读
      • 本类热门
        • 最近下载