SpringBoot框架SpEL表達式注入漏洞復現與原理分析

前言這是2016年的一個洞 , 利用條件是至少知道一個觸發 springboot 默認錯誤頁面的接口及參數名 。
影響版本:1.1.0-1.1.121.2.0-1.2.71.3.0
修復方案:升級版本
環境搭建下載鏈接:https://github.com/LandGrey/SpringBootVulExploit/tree/master/repository/springboot-spel-rce
用idea打開之后配置一下,如下圖:

SpringBoot框架SpEL表達式注入漏洞復現與原理分析

文章插圖
然后啟動訪問出現如下頁面 , 代表搭建成功 。
SpringBoot框架SpEL表達式注入漏洞復現與原理分析

文章插圖
漏洞復現訪問:http://localhost:9091/article?id=${9*9}  , 可以發現${9*9}的SpEL表達式進行了解析 , 隨后將該表達式的運行的結果進行了返回,如下圖 。
SpringBoot框架SpEL表達式注入漏洞復現與原理分析

文章插圖
現在嘗試彈出計算器,訪問:http://localhost:9091/article?id=${T(java.lang.Runtime).getRuntime().exec(new String(new byte[]{0x63,0x61,0x6c,0x63}))}
成功彈出,如下圖:
SpringBoot框架SpEL表達式注入漏洞復現與原理分析

文章插圖
調試分析為什么會出現這情況呢,這是因為springboot返回錯誤頁面的時候提供了詳細信息,這些信息包括
錯誤status("status"->500)、時間戳("timestamp"->"Fri Dec.....")、錯誤信息("error"->"Internal Server Error")、和用戶輸入的參數("message"->"test"),然后后端渲染視圖時,會解析錯誤模板中的參數名 。然后拿到對應的參數值,通過函數檢查參數值中是否存在${} , 如果存在則去除,然后傳入SpEL引擎進行解析 。模板內容如下所示:
<html><body><h1>Whitelabel Error Page</h1><p>This application has no explicit mapping for /error, so you are seeing this as a fallback.</p><div id='created'>${timestamp}</div><div>There was an unexpected error (type=${error}, status=${status})</div><div>${message}</div></body></html>程序會判斷模板中每個${}的位置,然后將參數名一個一個取出來后傳入spel引擎,解析參數名對應的值 。這里就是漏洞的觸發點,假如我輸入${payload},spel取出來payload后進行解析,然后觸發漏洞 。觸發點如下:
SpringBoot框架SpEL表達式注入漏洞復現與原理分析

文章插圖
瀏覽器訪問http://localhost:9091/article?id=${T(java.lang.Runtime).getRuntime().exec(new%20String(new%20byte[]{0x63,0x61,0x6c,0x63}))},現在開始調試,首先會將map的值傳入,context的rootObject中 , 之后以this.templatethis.resolver為參數調用replacePlaceholders方法,如下圖:
SpringBoot框架SpEL表達式注入漏洞復現與原理分析

文章插圖
this.template的內容就是上文的錯誤模板,跟進replacePlaceholders方法 ,進入PropertyPlaceholderHelper文件 。
SpringBoot框架SpEL表達式注入漏洞復現與原理分析

文章插圖
繼續跟進parseStringValue方法
【SpringBoot框架SpEL表達式注入漏洞復現與原理分析】
SpringBoot框架SpEL表達式注入漏洞復現與原理分析

文章插圖

SpringBoot框架SpEL表達式注入漏洞復現與原理分析

文章插圖
分析一下代碼,首先StringBuilder將strVal轉為字符串,并賦值給result,接著判斷result中${}位置,結果為157、168,然后通過substring截取157和168的中間值,并賦值給placeholder,本次的值為"timestamp" , 然后將placeholder作為第一個參數,再次調用本方法 。結果如下圖:
SpringBoot框架SpEL表達式注入漏洞復現與原理分析

文章插圖
strVal的值變為timestamp , 所以在indexOf判斷時,由于沒出現${,所以變為了-1 , 跳過了while循環,直接執行下邊的return result.toString(); 。
繼續跟進,下一步是調用resolvePlaceholder方法,此函數的作用是查找this.context中對應參數的值并返回,如下圖:
SpringBoot框架SpEL表達式注入漏洞復現與原理分析

文章插圖
發現拿到了時間戳"timestamp" -> "Wed Oct 19 00:38:36 CST 2022",然后賦值給propVal,此時不為空,進入下一個if循環 , 再次調用parseStringValue 。
SpringBoot框架SpEL表達式注入漏洞復現與原理分析

推薦閱讀