作者:Seaer@深信服千里目安全實驗室
原文鏈接:https://mp.weixin.qq.com/s/j_gx9C_xL1LyrnuFFPFsfg
1 組件介紹
Apache Shiro是一個功能強大且易于使用的Java安全框架,功能包括身份驗證,授權,加密和會話管理。使用Shiro的API,可以輕松地快速地保護任何應用程序,范圍包括小型的移動應用程序到大型的Web和企業應用程序。以下是Shiro的結構圖。

Shiro提供了應用安全API(被Shiro框架開發團隊成為安全四大基石的Authentication(認證), Authorization(授權), Session Management(會話管理), Cryptography(加密))
- Authentication(認證): 證明用戶身份,通常稱之為“登錄”。
- Authorization(授權) : 訪問控制。
- Cryptography(加密) : 保護或隱藏數據,防止數據被竊取。
- Session Management(會話管理) : 管理每一個用戶的會話狀態。
在概念層,Shiro 架構包含三個主要的理念:Subject,SecurityManager和 Realm。
- Subject:當前用戶,Subject 可以是一個人,但也可以是第三方服務、守護進程帳戶、時鐘守護任務或者其它–當前和軟件交互的任何事件。
- SecurityManager:管理所有Subject,SecurityManager 是 Shiro 架構的核心,配合內部安全組件共同組成安全傘。
- Realms:用于進行權限信息的驗證,我們自己實現。Realm 本質上是一個特定的安全 DAO:它封裝與數據源連接的細節,得到Shiro 所需的相關的數據。在配置 Shiro 的時候,你必須指定至少一個Realm 來實現認證(authentication)和/或授權(authorization)。
2 高危漏洞介紹
| 漏洞名稱 | 漏洞ID | 影響版本 | CVSS |
|---|---|---|---|
| Apache Shiro 1.2.4反序列化遠程代碼執行漏洞 | CVE-2016-4437/SHIRO-550 | Apache Shiro <= 1.2.4 | 8.1 |
| Apache Shiro Padding Oracle Attack 遠程代碼執行漏洞 | CVE-2019-12422/SHIRO-721 | Apache Shiro < 1.4.2 | 7.5 |
| Apache Shiro 身份驗證繞過漏洞 | CVE-2020-1957/SHIRO-682 | Apache Shiro < 1.5.2 | 9.8 |
| Apache Shiro 身份驗證繞過漏洞 | CVE-2020-11989/SHIRO-782 | Apache Shiro < 1.5.3 | 9.8 |
| Apache Shiro 身份驗證繞過漏洞 | CVE-2020-13933 | Apache Shiro < 1.6.0 | 7.5 |
| Apache Shiro 身份驗證繞過漏洞(AJP協議繞過) | SHIRO-760 | 基于Tomcat版本 | 無 |
| Apache Shiro < 1.2.3 身份驗證繞過漏洞 | CVE-2014-0074/SHIRO-460 | Apache Shiro < 1.2.3 | 7.5 |
Shiro組件漏洞主要分為兩種類型,一種是java反序列化造成的遠程代碼執行漏洞,另一種是身份驗證繞過漏洞。在官方對rememberMe字段反序列化進行秘鑰隨機化,AES加密模式更換后,暫無新型反序列化漏洞的出現。但是在身份驗證繞過的補丁修復上,多次出現繞過情況,多是由于補丁修復過程中考慮不周全。Shiro在今年上半年爆出了三個較為嚴重的身份驗證繞過的漏洞,且影響版本較新,用戶需要重點關注。
3 漏洞利用鏈
3.1 組件風險梳理
根據Shiro組件的漏洞,結合敏感配置的梳理,得到如下風險梳理的思維導圖。

3.2 利用鏈總結
基于風險梳理思維導圖,總結出2種可行的漏洞利用鏈。
3.2.1 無權限 -> GetShell

Apache Shiro 1.2.4反序列化遠程代碼執行漏洞單獨使用,即可完成GetShell。此漏洞利用前提是獲取AES加密秘鑰。一般情況,Shiro組件默認沒有被更換,可以直接利用此漏洞。如果出現秘鑰被更換,此漏洞需要結合任意文件讀取或者任意文件下載,先獲取到AES加密秘鑰,再進行漏洞利用。
3.2.2 普通權限 -> GetShell

Apache Shiro Padding Oracle Attack 遠程代碼執行漏洞單獨使用,即可完成GetShell。此漏洞利用前提需要獲得一個正確的用戶Cookie值,利用此漏洞進行GetShell至少要獲取一個普通用戶權限。
4 高可利用漏洞分析
從高危漏洞列表中,針對部分近年高可利用漏洞進行漏洞深入分析。
Shiro組件漏洞分為兩個種類:java反序列化漏洞、身份驗證繞過漏洞。本章節重點進行兩個類別的漏洞分析。
4.1 java反序列化漏洞
技術背景
Java反序列化漏洞原理:Java 提供了一種對象序列化的機制,該機制中,一個對象可以被表示為一個字節序列,該字節序列包括該對象的數據、有關對象的類型的信息和存儲在對象中數據的類型。Java反序列化則是從一個源輸入流中讀取字節序列,再把它們反序列化為一個對象,并將其返回。Java反序列化漏洞的成因是攻擊者通過序列化函數將自己精心構造的惡意對象序列化,將序列化數據發送到目標服務器的反序列化接口,當服務器在沒有對序列化數據進行有效的安全驗證,直接對序列化數據進行反序列化處理,執行惡意對象中的代碼,造成攻擊。
AES加密算法:使用Rijndael分組密碼算法,屬于對稱加密。
AES加密算法涉及4種操作:字節替代(SubBytes)、行移位(ShiftRows)、列混淆(MixColumns)和輪密鑰加(AddRoundKey)。
總體加密流程:

-
字節替代(SubBytes)
字節代替的主要功能是通過S盒完成一個字節到另外一個字節的映射。
解密使用S盒的逆進行還原。

-
行移位(ShiftRows)
行移位是一個4x4的矩陣內部字節之間的置換,用于提供算法的擴散性。
正向行移位:正向行移位用于加密,其原理圖如下。其中:第一行保持不變,第二行循環左移8比特,第三行循環左移16比特,第四行循環左移24比特。
解密使用逆向行移位:第一行保持不變,第二行循環右移8比特,第三行循環右移16比特,第四行循環右移24比特。 -
列混淆(MixColumns)
將行移位得到的新的4x4矩陣,與另一個矩陣進行左乘運算,更改每一列的數值。
解密時,左乘與之前選取的中間矩陣的互逆矩陣,即可還原。

- 輪密鑰加(AddRoundKey)
加密過程中,每輪的輸入與輪子密鑰異或一次。
解密過程,由于任何數和自身的異或結果為0,因此,解密時再異或上該輪的輪子密鑰即可恢復。
AES CBC模式
- 加密

- 解密

4.1.1 Apache Shiro 1.2.4反序列化遠程代碼執行漏洞
1 漏洞信息
1.1 漏洞簡介
- 漏洞名稱:Apache Shiro 1.2.4 Deserialize Remote Code Execution Vulnerability
- 漏洞編號:CVE-2016-4437
- 漏洞類型:遠程代碼執行
- CVSS評分:【CVSS v2.0:6.8】【CVSS v3.0:8.1】
- 漏洞危害等級:高危
1.2 漏洞概述
Apache Shiro 1.2.5之前的版本在org.apache.shiro.mgt.AbstractRememberMeManager中存在AES默認秘鑰kPH+bIxk5D2deZiIxcaaaA==,開啟RememberMe功能的shiro組件將允許遠程攻擊者構造序列化數據,在目標服務器上執行任意命令。
1.3 漏洞利用條件
- 已知 Shiro AES 解密密鑰。
- 開啟RememberMe功能。
1.4 漏洞影響
影響版本:
Apache Shiro <= 1.2.4
1.5 漏洞修復
獲取Apache Shiro最新版本,下載鏈接:https://shiro.apache.org/download.html
2.漏洞復現
2.1 環境拓撲

2.2 應用協議
8080/HTTP
2.3 復現過程
基于Windows平臺,使用環境目錄下的shirodemo-1.0-SNAPSHOT.jar環境,執行java -jar shirodemo-1.0-SNAPSHOT.jar啟動環境。效果如圖

運行sniper工具箱,填寫表單信息,輸入ipconfig命令,點擊Attack,效果如圖。
有回顯信息的exp插件

無回顯信息的exp插件
3.漏洞分析
3.1 詳細分析
3.1.1 漏洞利用過程
使用ysoserial生成存在惡意命令的反序列化payload。使用AES默認秘鑰,生成的payload進行AES/CBC/PKCS5Padding模式加密,將加密后的結果傳入到HTTP頭部Cookie字段的rememberMe參數,通過HTTP協議發起請求。
注:由于Shiro重寫了resolveClass方法,將原生方法中的forName方法替換為loadClass方法,由于loadClass無法加載數組類型的類,因此存在Transformer[]類的CommonCollections gadget無法成功利用此漏洞,(例如ysoserial CommonCollections1、CommonCollections3)

3.1.2 代碼分析
傳入的payload首先被服務器接收,并傳送給Shiro攔截器處理(org.apache.shiro.web.servlet.OncePerRequestFilter#doFilter方法作為入口)

調用createSubject方法創建Subject

在org.apache.shiro.mgt.DefaultSecurityManager#getRememberedIdentity方法調用getRememberedSerializedIdentity方法獲取rememberMe認證的序列化數據。

在此方法中可以獲取傳入的HTTP請求,響應數據,并解析獲取http請求中的cookie字段值(payload所在字段)(base64形式)。

并在后續的程序中進行base64解碼,將解碼后的byte流存儲在decoded變量中,作為后續進行AES解密的密文。

返回的密文數據流將會在org.apache.shiro.crypto.JcaCipherService#decrypt方法中完成AES解密操作。

獲得的AES解密后的明文將在org.apache.shiro.web.mgt.AbstractRememberMeManager#deserialize方法中反序列化,執行其中的惡意代碼。

3.1.3 漏洞觸發過程

3.1.4補丁分析
對比Shiro 1.2.4與1.2.5版本的改動,在org.apache.shiro.mgt.AbstractRememberMeManager類中聲明秘鑰的方式,從原來的硬編碼更改為自動生成秘鑰。

新版本中調用generateNewKey方法進行秘鑰生成,通過傳入秘鑰長度,根據傳入的長度參數,返回對應長度的隨機秘鑰。

由于秘鑰的隨機性,使攻擊者無法輕易的通過使用秘鑰加密惡意序列化數據的方式進行攻擊,從而修復了此漏洞。
4.1.2 Apache Shiro Padding Oracle Attack 遠程代碼執行漏洞
1.漏洞信息
1.1 漏洞簡介
- 漏洞名稱:Apache Shiro Padding Oracle Attack Remote Code Execution Vulnerability
- 漏洞編號:CVE-2019-12422
- 漏洞類型:遠程代碼執行
- CVSS評分:【CVSS v2.0:5.0】【CVSS v3.1:7.5】
- 漏洞危害等級:高危
1.2 漏洞概述
Apache Shiro 1.4.2之前的版本默認使用AES/CBC/PKCS5Padding模式加密,開啟RememberMe功能的Shiro組件將允許遠程攻擊者構造序列化數據,通過Padding Oracle Attack進行爆破,即使在秘鑰未知的條件下,也可以在目標服務器上執行任意命令。
1.3 漏洞利用條件
- 使用AES CBC模式。
- 開啟RememberMe功能。
- 密文可控。
- 獲取到正常Cookie。
1.4 漏洞影響
影響版本:
Apache Shiro < 1.4.2
1.5 漏洞修復
獲取Apache Shiro最新版本,下載鏈接:https://shiro.apache.org/download.html
2.漏洞復現
2.1 環境拓撲

2.2 應用協議
8080/HTTP
2.3 復現過程
基于Windows平臺,使用環境目錄下的apache-tomcat-8.5.2環境,進入bin目錄下運行startup.bat,linux環境運行startup.sh啟動環境。效果如圖

運行sniper工具箱,填寫表單信息,點擊Attack,效果如圖。
![]
3.漏洞分析
3.1 詳細分析
3.1.1 漏洞利用過程
使用ysoserial生成存在惡意命令的反序列化payload。獲取一個用戶的Cookie字段值(可以獲取普通用戶的Cookie)。取出rememberMe字段值,通過逐位爆破,構造正常padding數據,最終將每一塊構造的block拼接在一起,形成任意構造的明文gadget加密后的密文。
注:由于Shiro重寫了resolveClass方法,將原生方法中的forName方法替換為loadClass方法,由于loadClass無法加載數組類型的類,因此存在Transformer[]類的CommonCollections gadget無法成功利用此漏洞,(例如ysoserial CommonCollections1、CommonCollections3)

3.1.2 代碼分析
CVE-2019-12422進行反序列化攻擊的原理與CVE-2016-4437漏洞原理一致,而由于Shiro在1.2.4以上的版本已經將硬編碼秘鑰更換為隨機生成的秘鑰,因此無法獲取到AES加密秘鑰。CVE-2019-12422漏洞利用了AES CBC模式加密的漏洞,繞過了使用AES秘鑰加密明文gadget的前提條件。具體原理如下:
在進行AES解密時,會對生成的明文進行padding校驗,在com.sun.crypto.provider.CipherCore#doFinal中獲取padding長度

com.sun.crypto.provider.PKCS5Padding#unpad方法進行padding檢測,如果檢測失敗,返回-1狀態碼

當校驗失敗時,com.sun.crypto.provider.CipherCore#doFinal方法拋出異常
最終程序走到org.apache.shiro.web.servlet#removeFrom方法中,構造響應信息,進行回顯。在代碼中可以看見padding失敗后會回顯rememberMe deleteMe等特征字符串。可以作為padding oracle attack回顯特征進行爆破。

攻擊者可以憑借此特征,分組爆破。由于AES CBC模式解密時會將前一塊的密文作為本輪解密的IV值,當密文可控時,可以逐位爆破前一塊密文,根據padding error的回顯不同,獲取padding字符為\x10*16的IV'值。
IV ^ middle = 16 * \x10
IV' ^ middle = 構造gadget密文塊
IV' = 構造gadget密文塊 ^ 16 * \x10 ^IV
根據公式,循環獲取所有密文塊。通過這種方式,獲取任意明文對應的密文。
3.1.3 漏洞觸發過程

3.1.4補丁分析
對比Shiro 1.4.1與1.4.2版本的改動,在org.apache.shiro.crypto.AesCipherService類中聲明AES加密的模式,從原來的CBC模式更改為GCM模式。

由于AES加密模式更換,攻擊者無法使用padding oracle attack進行攻擊,從而修復了此漏洞。
4.2 身份驗證繞過漏洞
技術背景
攔截器和過濾器的詳細區別
攔截器和過濾器的執行順序

Shiro攔截器:

NameableFilter: NameableFilter給Filter起個名字,如果沒有設置默認就是FilterName
當我們組裝攔截器鏈時會根據這個名字找到相應的攔截器實例
OncePerRequestFilter: 用于防止多次執行Filter,一次請求只會走一次Filter鏈。enabled屬性:是否開啟該攔截器,默認enabled=true表示開啟
ShiroFilter: 整個Shiro的入口點,用于攔截需要安全控制的請求進行處理。
AdviceFilter: 提供AOP風格的支持,類似于SpringMVC中的Interceptor。
PathMatchingFilter: 提供基于Ant風格的請求路徑匹配功能及攔截器參數解析的功能,如roles[admin,user]自動根據,分割解析到一個路徑參數配置并綁定到相應的路徑。
AccessControlFilter: 提供訪問控制的基礎功能,比如是否允許訪問/當訪問拒絕時如何處理等。isAccessAllowed表示是否允許訪問;mappedValue就是[urls]配置中攔截器參數部分,如果允許訪問返回true,否則false。onAccessDenied 表當訪問拒絕時是否已經處理,如果返回true表示需要繼續處理,如果返回false表示該攔截器實例已經處理了,直接返回即可。

4.2.1 Apache Shiro 身份驗證繞過漏洞
1.漏洞信息
1.1 漏洞簡介
- 漏洞名稱:Apache Shiro Authentication Bypass Vulnerability
- 漏洞編號:CVE-2020-1957
- 漏洞類型:Authentication Bypass
- CVSS評分:【CVSS v2.0:7.5】【CVSS v3.1:9.8】
- 漏洞危害等級:高危
1.2 漏洞概述
Apache Shiro 1.5.2之前的版本,由于Shiro攔截器與requestURI的匹配流程與Web框架的攔截器的匹配流程有差異,攻擊者構造一個特殊的http請求,可以繞過Shiro的認證,未授權訪問敏感路徑。此漏洞有兩種攻擊方式,第一種攻擊方式適用于Shiro < 1.5.0版本,由于Shiro 1.5.0版本修復補丁考慮不全面,導致補丁繞過,出現了第二種攻擊方式,適用于Shiro < 1.5.2版本。
1.3 漏洞利用條件
- 無
1.4 漏洞影響
影響版本:
Apache Shiro < 1.6.0
1.5 漏洞修復
獲取Apache Shiro最新版本,下載鏈接:https://shiro.apache.org/download.html
2.漏洞復現
2.1 環境拓撲

2.2 應用協議
8080/HTTP
2.3 復現過程
基于Windows平臺,使用環境目錄下的shiro-basic.zip環境,解壓后,用Idea打開shiro-basic文件夾,下載maven資源,運行ShiroBasicApplication類,即可啟動環境。效果如圖。

Shiro < 1.5.0
構造HTTP請求,發送到服務器,完成身份驗證繞過,效果如圖

Shiro < 1.5.2

3.漏洞分析
3.1 詳細分析
3.1.1 漏洞利用過程
Shiro < 1.5.0
獲取到無權限訪問的敏感路徑,在路徑的結尾添加/進行身份驗證繞過。
Shiro < 1.5.2
獲取到無權限訪問的敏感路徑,在auth認證路徑中的添加;進行身份驗證繞過。
3.1.2 代碼分析
Shiro < 1.5.0
傳入的payload首先被服務器接收,并傳送給Shiro攔截器處理(org.apache.shiro.web.servlet.OncePerRequestFilter#doFilter方法作為入口)。

調用createSubject方法創建Subject,并調用execute方法進入Shiro FilterChain中。

進入org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver#getChain方法中,首先獲取請求uri路徑,之后迭代獲取攔截器的表達式。

這里我們重點關注/hello/*表達式。代碼進入pathMatches方法,最終調用org.apache.shiro.util.AntPathMatcher#doMatch方法進行傳入的requestURI與攔截器表達式進行匹配。
匹配過程中,分別將攔截器表達式與requestURI以/作為分隔符進行字符串到數組的轉換,通過循環匹配數組中對應的元素,判斷requestURI是否符合攔截器表達式匹配形式。

如果表達式中存在通配符*,會將containsStar標志位賦值為true,進入 else if (patIdxEnd == 0)判斷條件,返回true。

繼續跟進代碼,在requestURI與攔截器表達式匹配結束后,還會進行一次判斷,而漏洞產生的原因也是由于判斷的條件。如果Shiro攔截器表達式不以/結尾,且requestURI以/結尾,判斷代碼將返回false表示匹配失敗,從而繞過Shiro認證。

跟進到Spring處理URI的代碼,進入org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal方法,獲取requestURI。

進入lookupHandlerMethod方法,調用addMatchingMappings方法,獲取Spring攔截器。

進入org.springframework.web.servlet.mvc.condition.PatternsRequestCondition#getMatchingCondition方法調用doMatch方法進行requestURI和攔截器表達式的匹配。

Spring攔截器匹配流程和Shiro大致相同,都是將字符串轉換為數組進行匹配。

由于Spring多了一個環節,在檢測攔截器表達式與requestURI結尾是否為/之后,并沒有直接返回false。而是將攔截器表達式結尾添加/,并繼續進行path.startsWith(this.pathSeparator) != pattern.startsWith(this.pathSeparator)測試,從而完成了攔截器表達式與requestURI的匹配。
上述攻擊方式在Shiro 1.5.0版本中修復,但是被二次繞過,繞過分析如下。
Shiro < 1.5.2
Shiro 1.5.0 - 1.5.1在認證過程中基本沒有變化,主要分析一下二次繞過的利用點。還是以org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver#getChain作為起點。
在獲取requestURI時,依舊會在getPathWithinApplication方法中調用getRequestUri方法進行requestURI的解析并獲取,但是在URI正規化處理時,先調用decodeAndCleanUriString方法進行路徑的解碼,并清理URI。

進入decodeAndCleanUriString方法,發現此方法會以分號將傳入的URI進行截斷,并將分號以及分號后面的數據進行清空,返回分號前面的URI數據,從而讓/a/b;/c變為/a/b。
繼續跟進到Spring攔截器的decodeAndCleanUriString方法中

從代碼中可以發現,Spring對于分號處理的方式與Shiro不同,Spring會先獲取分號的位置,并檢測分號后是否存在/,如果有,將/的位置記錄在slashIndex變量中,并將分號前的數據與/之后的數據進行拼接,從而讓/a/b;/c變為/a/b/c。返回處理后的requestURI。
由于Spring與Shiro的decodeAndCleanUriString方法不同,攻擊者可以使用分號構造路徑,繞過Shiro認證,并可以匹配Spring的動態控制器。
3.1.3 漏洞觸發過程
Shiro < 1.5.0

Shiro < 1.5.2

3.1.4補丁分析
對比Shiro 1.4.2與Shiro 1.5.0版本的改動,在org.apache.shiro.web.filter.PathMatchingFilter類中添加了刪除requestURI結尾的/的代碼。

由于requestURI和攔截器表達式結尾的/都會被清除,因此防御了此漏洞。
對比Shiro 1.5.1與Shiro 1.5.2版本的改動
由于Shiro 1.5.2版本中,在進行decodeAndCleanUriString方法之前會先進行uri解析,調用request.getServletPath()和request.getPathInfo()獲取ServletPath 和PathInfo 并進行路徑拼接,避開了decodeAndCleanUriString對于分號的處理,從而修復了此漏洞。
4.2.2 Apache Shiro 身份驗證繞過漏洞
1.漏洞信息
1.1 漏洞簡介
- 漏洞名稱:Apache Shiro Authentication Bypass Vulnerability
- 漏洞編號:CVE-2020-11989
- 漏洞類型:Authentication Bypass
- CVSS評分:【CVSS v2.0:7.5】【CVSS v3.1:9.8】
- 漏洞危害等級:高危
1.2 漏洞概述
Apache Shiro 1.5.3之前的版本,由于Shiro攔截器與requestURI的匹配流程與Web框架的攔截器的匹配流程有差異,攻擊者構造一個特殊的http請求,可以繞過Shiro的認證,未授權訪問敏感路徑。此漏洞存在兩種攻擊方式。
1.3 漏洞利用條件
First Attack
- 無
Second Attack
- 應用不能部署在根目錄(1.5.1 < Shiro < 1.5.3)
1.4 漏洞影響
影響版本:
Apache Shiro < 1.5.3
1.5 漏洞修復
獲取Apache Shiro最新版本,下載鏈接:https://shiro.apache.org/download.html
2.漏洞復現
2.1 環境拓撲

2.2 應用協議
8080/HTTP
2.3 復現過程
基于Windows平臺,使用環境目錄下的shiro-basic.zip環境,解壓后,用Idea打開shiro-basic文件夾,下載maven資源,運行ShiroBasicApplication類,即可啟動環境。效果如圖。

First Attack
構造HTTP請求,發送到服務器,完成身份驗證繞過,效果如圖

Second Attack
構造HTTP請求,發送到服務器,完成身份驗證繞過,效果如圖

3.漏洞分析
3.1 詳細分析
3.1.1 漏洞利用過程
First Attack
獲取到無權限訪問的敏感路徑,在authc認證路徑后添加%25%32%66進行身份驗證繞過。
Second Attack
獲取到無權限訪問的敏感路徑,在路徑的頭部添加/;/進行身份驗證繞過。
3.1.2 代碼分析
First Attack
傳入的payload首先被服務器接收,并傳送給Shiro攔截器處理(org.apache.shiro.web.servlet.OncePerRequestFilter#doFilter方法作為入口)。

調用createSubject方法創建Subject,并調用execute方法進入Shiro FilterChain中。

進入org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver#getChain方法中,首先獲取請求uri路徑。
在Shiro1.5.2版本中,對于requestURI處理的方式存在一些不同,此處也是漏洞觸發點所在。Shiro1.5.2使用的是request.getContextPath(),request.getServletPath(),request.getPathInfo()拼接的方式。由于getServletPath()方法會對requestURI進行一次url解碼,在之后的decodeAndCleanUriString方法中進行第二次url解碼。

回到getChain方法中,迭代獲取攔截器的表達式。

這里重點關注/hello/*表達式。代碼進入pathMatches方法,最終調用org.apache.shiro.util.AntPathMatcher#doMatch方法進行傳入的requestURI與攔截器表達式進行匹配。
匹配過程中,分別將攔截器表達式與requestURI以/作為分隔符進行字符串到數組的轉換,通過循環匹配數組中對應的元素,判斷requestURI是否符合攔截器表達式匹配形式。

如果表達式中存在通配符*,會將containsStar標志位賦值為true,進入 else if (patIdxEnd == 0)判斷條件,返回true。

最終回到doMatch方法中,通過判斷表達式數組的元素個數與requestURI的元素個數,以及表達式中是否包含**,完成后續的匹配。
跟進到Spring處理URI的代碼,進入org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal方法,獲取requestURI。由于Spring獲取requestURI時使用getRequestURI()方法,此方法不會進行URL解碼。只會在decodeAndCleanUriString完成一次url解碼。
進入lookupHandlerMethod方法,調用addMatchingMappings方法,獲取Spring攔截器。

進入org.springframework.web.servlet.mvc.condition.PatternsRequestCondition#getMatchingCondition方法調用doMatch方法進行requestURI和攔截器表達式的匹配。

Spring攔截器匹配流程和Shiro大致相同,同樣是將字符串轉換為數組進行匹配。

由于Spring只進行了一次URL解碼,所以將未完全解碼的部分作為一個整體,從而完成了攔截器表達式與requestURI的匹配。

Second Attack
漏洞觸發點同樣是Shiro在修復CVE-2020-1957漏洞時,使用request.getContextPath(),request.getServletPath(),request.getPathInfo()拼接的方式,進行requestURI的獲取。
直接跟蹤到uri = valueOrEmpty(request.getContextPath()) + "/" + valueOrEmpty(request.getServletPath()) + valueOrEmpty(request.getPathInfo());

在調用getContextPath()方法獲取context-path時,會調用removePathParameter方法清除掉分號以及分號到下一個/中間的數據。

接下來進入for循環中匹配candidate與conotext-path是否相同
如果不同,則從傳入的URL中繼續讀取下一級目錄,直到condidate與context-path相同,返回從URL截取的目錄作為contextPath。由于context-path獲取方式和removePathparameters方法對URL的處理,攻擊者可以請求,讓contextPath變量獲取到帶有分號的非預期值。
在進行requestURI拼接時,構造出根路徑帶有分號的requestURI。利用CVE-2020-1957漏洞原理,經過decodeAndCleanUriString方法時,截斷reqeustURI中分號后的數據,并返回。從而繞過了shiro權限控制。
3.1.3 漏洞觸發過程
First Attack

Second Attack

3.2.4補丁分析
對比Shiro 1.5.2與Shiro 1.5.3版本的改動,在org.apache.shiro.web.util.WebUtils類中添加了刪除requestURI結尾的/的代碼。

補丁主要優化了getPathWithinApplication方法,并單獨定義了getServletPath方法,getPathInfo方法。補丁修復后,調用getPathWithinApplication方法獲取requestURI只會在進行getServletPath方法中進行一次url解碼,保持與Spring獲取requestURI過程中相同的url解碼次數。防御了雙重url編碼繞過。
獲取requestURI直接調用getServletPath方法和getPathInfo方法進行拼接,由于不需要與contextpath拼接,從而防御了First Attack攻擊。
4.2.3 Apache Shiro 身份驗證繞過漏洞
1.漏洞信息
1.1 漏洞簡介
- 漏洞名稱:Apache Shiro Authentication Bypass Vulnerability
- 漏洞編號:CVE-2020-13933
- 漏洞類型:身份驗證繞過
- CVSS評分:【CVSS v2.0:5.0】【CVSS v3.1:7.5】
- 漏洞危害等級:高危
1.2 漏洞概述
Apache Shiro 1.6.0之前的版本,由于Shiro攔截器與requestURI的匹配流程與Web框架的攔截器的匹配流程有差異,攻擊者構造一個特殊的http請求,可以繞過Shiro的認證,未授權訪問敏感路徑。
1.3 漏洞利用條件
- 無
1.4 漏洞影響
影響版本:
Apache Shiro < 1.6.0
1.5 漏洞修復
獲取Apache Shiro最新版本,下載鏈接:https://shiro.apache.org/download.html
2.漏洞復現
2.1 環境拓撲

2.2 應用協議
8080/HTTP
2.3 復現過程
基于Windows平臺,使用環境目錄下的shiro-basic.zip環境,解壓后,用Idea打開shiro-basic文件夾,下載maven資源,運行ShiroBasicApplication類,即可啟動環境。效果如圖。
構造HTTP請求,發送到服務器,完成身份驗證繞過,效果如圖

3.漏洞分析
3.1 詳細分析
3.1.1 漏洞利用過程
獲取到無權限訪問的敏感路徑,在authc認證路徑后添加%3b進行身份驗證繞過。
3.1.2 代碼分析
傳入的payload首先被服務器接收,并傳送給Shiro攔截器處理(org.apache.shiro.web.servlet.OncePerRequestFilter#doFilter方法作為入口)。

調用createSubject方法創建Subject,并調用execute方法進入Shiro FilterChain中。

進入org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver#getChain方法中,首先獲取請求uri路徑。

在Shiro1.5.3版本中,對于requestURI處理的方式存在一些不同,雖然Shiro官方在此處做了很多優化,但是依然存在與Spring處理方式不一致的請求,導致漏洞的產生。在Shiro1.5.3版本中,requestURI直接調用getServletPath方法和getPathInfo方法進行拼接。并清除解碼后的requestURI中分號之后的數據。

回到getChain方法中,迭代獲取攔截器的表達式。

這里重點關注/hello/*表達式。代碼進入pathMatches方法,最終調用org.apache.shiro.util.AntPathMatcher#doMatch方法進行傳入的requestURI與攔截器表達式進行匹配。

匹配過程中,分別將攔截器表達式與requestURI以/作為分隔符進行字符串到數組的轉換,通過循環匹配數組中對應的元素,判斷requestURI是否符合攔截器表達式匹配形式。

由于解碼后的requestURI被分號切割,導致pathDirs數組的元素個數少于pattDirs數組的元素個數。導致攔截器表達式與requestURI匹配失敗,繞過認證

跟進到Spring處理URI的代碼,進入org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal方法,獲取requestURI。由于Spring獲取requestURI時使用getRequestURI()方法,此方法不會進行URL解碼。

進入decodeAndCleanUriString方法,對未進行url解碼的requestURI調用removeSemicolonContent方法進行分號截斷操作。由于此時的requestURI處于未解碼狀態,因此編碼后的分號是無法被解析的。

進入lookupHandlerMethod方法,調用addMatchingMappings方法,獲取Spring攔截器。

進入org.springframework.web.servlet.mvc.condition.PatternsRequestCondition#getMatchingCondition方法調用doMatch方法進行requestURI和攔截器表達式的匹配。

Spring攔截器匹配流程和Shiro大致相同,同樣是將字符串轉換為數組進行匹配。

由于Spring對requestURI的先進行分號分割操作,再進行了一次URL解碼。所以編碼后分號之后的數據并不能被有效的分割,進行URL解碼后,分號以及分號之后的數據成為了一個新的目錄,從而完成了攔截器表達式與requestURI的匹配。

總結:Shiro對于requestURI的處理,先進行URL解碼,再進行分號分割。Spring對于requestURI的處理,先進行分號分割,再進行URL解碼。二者在解析requestURI時存在差異,因此導致漏洞出現。
3.1.3 漏洞觸發過程
3.2.4補丁分析
對比Shiro 1.5.3與Shiro 1.6.0版本的改動,在Shiro 1.6.0版本中新增org.apache.shiro.web.filter.InvalidRequestFilter類。

在InvalidRequestFilter類中定義了SEMICOLON和BACKSLASH變量分別匹配路徑中的;和\以及URL編碼特征。調用isAccessAllowed方法,分別調用containsSemicolon,containsBackslash,containsNonAsciiCharacters方法進行;和\和不可見字符的檢測。如果上述三個特征存在任意一個,則返回400狀態。

在org.apache.shiro.web.filter.mgt.DefaultFilter類中添加InvalidRequestFilter攔截器。在org.apache.shiro.web.config.IniFilterChainResolverFactory類中添加/**攔截器表達式,并為所有攔截器表達式賦予invalidRequest,目的是讓所有傳入的路徑都可以經過InvalidRequestFilter檢測。

參考鏈接
1.https://www.infoq.com/articles/apache-shiro/
2.https://zhuanlan.zhihu.com/p/54176956
3.https://github.com/apache/shiro
4.https://www.cnblogs.com/luop/p/4334160.html
5.http://blog.orange.tw/2018/03/pwn-ctf-platform-with-java-jrmp-gadget.html<br
6.https://cloud.tencent.com/developer/article/1367702
7.https://www.cnblogs.com/geektcp/p/10061908.html
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/1378/
暫無評論