freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

XMLDecoder反序列化机制分析
2022-02-02 22:13:24
所属地 天津


前言

XMLDecoder是JDK自带的处理XML文档的类库,和XMLEncoder组合起来可用于生成 JavaBean类的XML文本表示形式,使用方式也是和 ObjectOutputStream与 ObjectInputStream创建Serializable对象类似。

XMLDecoder功能相当丰富,可以对XML文档中表示的对象很方便的在内存反序列化为对象,当XML文档中存在方法调用的标签时,可以在反序列化的过程中以反射的形式自动执行,这个机制也给一些使用不当的程序带来了安全漏洞,如Weblogic相关的CVE( CVE-2017-3506、CVE-2019-2725)。

简单使用

调试环境为jdk7和IDEA 。

以下简单用代码演示XMLDecoder和XMLEncoder的使用。

Person类代码:

public class Person {
    private String name;
    private int age;

    //必须要有一个无参构造方法,要不会在序列化的过程中报错
    public Person() {
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

Main类代码:

public class Main {
    public static Object decode() {
        File file = new File(Main.class.getClassLoader().getResource("").getPath() + "config.xml");
        XMLDecoder xmlDecoder = null;
        try {
            xmlDecoder = new XMLDecoder(new BufferedInputStream(new FileInputStream(file)));
            //反序列化对象
            Object o = xmlDecoder.readObject();
            return o;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            if (xmlDecoder != null) {
                xmlDecoder.close();
            }
        }
    }


    public static void encode(Object o) {
        XMLEncoder xmlEncoder = null;
        try {
            File file = new File(Main.class.getClassLoader().getResource("").getPath() + "config.xml");
            xmlEncoder = new XMLEncoder(new BufferedOutputStream(new FileOutputStream(file)));
            //序列化对象
            xmlEncoder.writeObject(o);
            xmlEncoder.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            xmlEncoder.close();
        }
    }


    public static void main(String[] args) throws FileNotFoundException {
        Person person = new Person("test", 18);
        encode(person);
        Person result = (Person) decode();
        System.out.println(result);
    }
}

在encode序列化方法执行后可以在程序目录下config.xml文件中看到person对象以xml的形式存储在文件中。

1643810762_61fa8fca0420a92b6a49c.png!small?1643810762545

在decode反序列化方法后,控制台成功打印出反序列化的对象。

1643810770_61fa8fd28ea357f38ac8c.png!small?1643810771086


XML标签说明


查看XMLDecoder的官方文档(https://www.oracle.com/technical-resources/articles/java/persistence3.html),可以看到几个主要标签的使用说明。

object标签的使用相当于new一个新对象,其中的string标签为构造方法需要的参数。

<object class="javax.swing.JButton">
  <string>Press me</string>
</object>

new JButton("Press me");

void标签的method属性可以在反序列化的过程中自动执行,其中的string标签为方法调用的参数。

<object class="javax.swing.JButton">
  <string>Press me</string>
  <void method="setName">
    <string>Greeting</string>
  </void>
</object>

JButton button1 = new JButton("Press me");
button1.setName("Greeting");

array标签用来声明数组格式的对象,length属性为数组的长度,index属性为赋值的下标。

<array class="java.lang.String" length="3">
  <void index="1">
    <string>Hello, world</string>
  </void>
</array>


String[] s = new String[3];
s[1] = "Hello, world";

反序列化命令执行过程

使用网上找到POC,其中object反序列化类为java.lang.ProcessBuilder,构造方法参数为calc(windows操作系统上的计算器程序),void标签声明了反序列化过程中调用的方法为start方法。

1643810854_61fa90262ee375f1e0b46.png!small?1643810854748

直接在ProcessBuilder的start方法上打断点,可以看到方法调用栈的调用链。

1643810861_61fa902dd59e3ffb2c2fe.png!small?1643810862449


在XMLDecoder类的readObject方法中进入parsingComplete方法。

1643810870_61fa9036400b2028b679c.png!small?1643810870749

在parsingComplete方法中进入DocumentHandler类的parse方法。

1643810876_61fa903cc01407e0f7fd2.png!small?1643810878701

在DocumentHandler类中声明了各种元素的处理器。


1643810885_61fa9045a51f8715a431f.png!small?1643810886233

之后打调用链大部分是对xml文件的解析和对象的生成,快进到DocumentHandler类的endElement方法,可以看到处理器实际的类型为VoidElementHandler。

1643810891_61fa904b7b498007b88ba.png!small?1643810892046


在ObjectElementHandler类的getValueObject方法中生成了Expression对象并执行getValue方法。

1643810897_61fa90515c9489c88cc4a.png!small?1643810897895

在getValue方法中发现invoke方法调用。

1643810902_61fa9056303c69ed123d8.png!small?1643810902711

一路跟踪到Statement类的invokeInternal方法可以看到其中调用了MethodUtil.invoke,并传入了start方法和ProcessBuilder对象,MethodUtil工具类会发射执行传入的方法。

1643810907_61fa905b299bd8d637bab.png!small?1643810907707


至此POC成功执行,总结下个人认为发生反序列化漏洞本身不是XMLDecoder的责任,它只是提供了这种程序执行的机制,安全措施应该由类库的使用者去负责。

1643810913_61fa90610fa652c85db24.png!small?1643810913716


补充

在类的无参构造方法上下断点,发现反序列化过程会调用类的构造方法反射执行生成目标对象,而不是像ObjectInputStream那样直接生成对象。

1643810919_61fa90679136ff13bf1ed.png!small?1643810920123

1643810925_61fa906dd08541bd93b31.png!small?1643810928093

1643810930_61fa9072d7ed68ae6c3bf.png!small?1643810931381


# web安全 # 漏洞分析
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录