HACK TEH BOX - Under Construction(JWT密鑰混淆 + SQL注入)
1. JWT密鑰混淆
JWT的組成一般分為三部分,{算法}.{payload}.{簽名}
,例如加密函數中jwt.sign(data, privateKey, { algorithm:'RS256' })
中,data
為數據,privateKey
為加密密鑰,{ algorithm:'RS256' }
為加密算法,JWT的一般形式和組成如下圖所示。
JWT常用的兩種算法是RSA
和HMAC
,分別是非對稱算法(一對密鑰)和對稱算法(一個密鑰)。RS256
算法在服務端利用私鑰進行加密,利用公鑰來進行解密驗證數據。HS256
算法只擁有一個密鑰,用於加密和解密。考慮一種情況,如果在解密的過程中,解密的算法被偽造,由RS256
更改為HS256
,那么被用於HS256
解密的密鑰就變為RS256
中的公鑰,而由於公鑰的性質,可能會被攻擊者獲取,攻擊者在獲取到RS256
的公鑰后,使用公鑰作為密鑰,利用HS256
算法進行加密,當JWT值發送給服務端時,會被以HS256
的方式進行解密,而解密的密鑰也是RS256
公鑰,這樣攻擊者就可以偽造JWT中的payload
值。
下圖為題目中存在漏洞的代碼,作為示例來介紹該漏洞
示例代碼中,加密過程使用的是RS256
算法,利用私鑰進行加密,但是在解密的函數中,同時提供了兩種算法選項,這就表示如果使用HS256
算法進行解密,依然是使用公鑰作為密鑰,存在該漏洞。
2. 環境
Hack The Box生成 Challenge環境(206.189.121.131:30520)
參考代碼一份,包含內容如下
3. Challenge
首先去環境看一下
一個簡單的登錄頁面,注冊一個admin/123456的賬戶,再使用該賬戶登錄,登陸后頁面跳轉
注冊請求:
登錄請求,返回JWT:
登陸成功,頁面跳轉:
登錄頁面進行簡單的sql注入測試后,未發現注入點,F12瀏覽一下網站內信息,沒有js文件被加載,登錄后被分配一個JWT值
拿去Burpsuite里解密一下看看內容
RS256算法,非對稱加密,但是在payload部分發現了公鑰信息
寫個簡單腳本使用公鑰來解密一下信息
運行成功,即JWT里面的公鑰正確
目前就能獲取到這么多信息了,結合JWT內的公鑰信息,可以大概猜出是利用JWT作為突破點進行攻擊
接下來去看一下Challenge提供的網站源碼,看有沒有什么有用的信息
先去看看/routes/index.js
路由文件
整個路由包含了以下幾個部分
1.GET請求/
2.GET請求/auth
3.GET請求/logout
4.POST請求/auth
這四個部分的功能都非常的簡單,POST請求根據register
字段的值判斷登錄/
注冊,/logout
清除session值,/auth
返回auth.html
頁面,在GET請求/
時,需要先通過JWT內的值判斷用戶是否存在,再進行頁面跳轉或錯誤信息,判斷過程首先要執行AuthMiddleware
函數,返回成功后再執行DBHlper.getUser(username)
函數,如果username
在數據數據庫中存在的話,就會跳轉至index.html
頁面,也就是登錄后看到的頁面了。
順着這個順序,首先先去看看AuthMiddleware.js
文件
大概功能就是返回JWT解碼后的username
值,然后這個值就會被拿去DBHlper.getUser(username)
函數比對
接着去JWTHelper.js
文件
就是一個簡單的JWT加解密,但是在解密的過程中,同時支持了兩種算法RSA256/HS256
,這個地方就暴露出了漏洞——JWT密鑰混淆
在進行完AuthMiddleware
函數的流程后,進行下一步DBHlper.getUser(username)
在這個代碼中,第一個sql語句使用了拼接字符串的形式,后面的sql語句則是傳遞參數的形式,存在sql注入的漏洞
到這里就可以確定整個題目的思路了,首先GET請求/
時,攜帶利用公鑰偽造的JWT,而JWT中的username
字段值為sql注入語句,執行后就可以進入數據庫查找flag
了。
4. Walkthrough
寫腳本利用公鑰作為密鑰進行HS256加密
報錯
查看源碼,發現被禁止使用公鑰來加密
直接去掉
在執行,返回值
拿着這個值去GET請求/
驗證
驗證成功,返回index.html
頁面,此時可以構造username
字段進行sql注入
接下來結合JWT,寫一個sqlmap
的tamper
腳本
大概思路就是cookie
值作為注入點,然后將payload
寫進username
字段再進行加密作為cookie
值傳遞
tamper
腳本實現的過程只需要修改輸入參數payload
,然后輸出retVal
即可,如下圖所示
執行代碼時同樣報錯
改掉
測試代碼輸出成功,接下來進行腳本編寫
寫好
上sqlmap測試
拿到flagHTB{d0n7_3xp053_y0ur_publ1ck3y}