简单来说,SQL注入就是attackers通过对客户端发出的HTTP请求的修改,在服务端执行他们想要的SQL操作。
(本篇用于基础概念的归纳总结,可能不太适合新手学习)
SQL注入产生的原因
- Missing or improper input validation (用户输入检查的缺乏或者不够完善)
- Application-generated queries containing user-fed input (应用进行SQL查询,其结果能够反馈用户之前的不恰当输入)
SQL注入的注入点(Sinks)和目标(targets)
2.1 Sinks
SQLi sinks 表示可能存在的注入点
GET/POST parameters
Every HTTP header
Cookies
Database itself
以上所有的这些内容,都可以轻松的被用户所修改,他们也都有可能被用于服务端的数据库查询操作中,因此都需要严肃对待
2.2 Targets
Target | Description |
---|---|
识别可注入的变量 | 查找注入点(以上全是) |
数据库信息 | 什么数据库?什么版本的数据库 |
数据库结构 | 库名,表名,列名,列类型,读写权限等 |
获取数据 | 爆库 |
数据修改 | insert, update, delete操作 |
DOS | LOCK,DELETE 操作来破坏数据库的Availability阻止用户正常访问应用 |
绕过验证 | 绕过登陆注册等一系列检查 |
远程指令注入 | 远程控制数据库 |
注入的技巧
-
判断注入点 : 输入的内容,能否让页面产生异常的行为
- SQL报错信息
- 页面内容的非正常显示 (e.g. 没有内容显示)
- 页面响应时间变化
-
闭合异常 : 如果说第一步的目的是为了让服务端的SQL查询语句出错,那么第二步就是为了在此基础上让SQL语句可以再次被正常解析
-
判断注入的类型 : 尝试不同类型的注入payload,观察存在哪种注入
-
修改SQL语句语义 : 修改语义,让数据库执行我们想要的操作
-
获取数据 : 爆库获得数据
这些操作也就对应了SQL注入的三要素:
- 所有HTTP请求的内容是否进行了严格的校验
- 是否可以重新闭合注入点的语句错误,使其可以被重新正常解析
- 语句是否可以被恶意修改
SQL注入对于安全的影响(CIA)
- Confidentiality
- 查看其他人的隐私信息
- 无授权地访问他人的账号
- Integrity
- 修改数据库的数据:工资,成绩等
- Availabity
- 删库跑路
- 修改账户信息:密码
- 删除日志
如何防御SQL注入
- i. 使用预编译语句 Prepared statement
- 顾名思义,就是将SQL语句提前编译,即其最终要执行的操作已经定了,唯一缺少的就是参数,其
语义
不会再受影响,这也是防御SQL注入的最佳手段; - ii. 检查数据类型
- 对于需要获取的
参数
进行严格的类型/格式检查
, 可以从很大程度上防御SQL注入 (数据类型,邮箱格式,日期格式等); - iii. 使用安全函数
- 随着安全得到越来越多的重视,编程语言也在不断更新更为安全的函数和机制用于抵御一些常见的攻击;
常见的注入类型
String SQLi
注入点原本需要的是字符串
SELECT *
FROM user_data
WHERE first_name = 'John'
AND last_name = '' OR '1' = '1'
Numberic SQLi
注入点原本需要的是数字
SELECT *
FROM user_data
WHERE Login_Count = 1
AND userid = 1 OR 1 = 1
Union query SQLi
利用SQL Union
关键词来实现额外的语义拼接
<?php
$q = "SELECT id, name, price, description".
"FROM product WHERE category=".$_GET['cat'];
?>
拼接一条查询语句:
<?php
$cat = "1 UNION SELECT 1, user, 1, pass FROM users";
?>
最终执行的SQL语句就是:
SELECT id, name, price, dexcription
FROM product
WHERE category = 1
UNION
SELECT 1, user, 1, pass
FROM users
要注意的是,拼接的查询语句,所查询的column数量必须与原本的相同,比如原本需要查询usname, passwd两列,那么拼接的查询语句也需要包含两列内容
可以利用 order by
关键词来实现对于列数的测试,同时使用二分法的技巧,直到不再报错为止
?category=1 order by 100
Second-order injection 二次注入
Second-order injection就像名字所指示的那样,注入的语句会在服务端的SQL代码中,出现两次,因此称为二次注入。
与先前我们提到的注入方法相比,二次注入并不是直接发起注入,而是先将注入的语句存入数据库,再调用该数据的查询,从而完成拼接,实现注入
比如我们想要修改admin用户的密码:
第一次注入:
比如在注册的时候,将想要注入的代码放入用户名中
<?php $user = "admin'--";?>
也许服务端对于注册部分的变量有严格的审查机制,却忽略了其他SQL语句的审查,那我们此时就得到了一个 admin'--
的用户
<?php
$q = "UPDATE users SET pass='".$_POST['newPass'].
"'WHERE user='".$row['user']."'";
?>
接下来触发 UPDATE
操作,这里却没有对数据库中查询到的变量做严格的过滤,导致注入语句拼接成功,我们就可以修改 admin
的密码了
UPDATE users
SET pass='我们想要的值'
WHERE user=admin'--'
Piggy-backed/Chained 堆叠注入
与 UNION
类似, Piggy-backed/Chained
也是为了执行额外的语句
利用 ;
来增加额外的语句实现
<?php
$q = "SELECT id FROM users WHERE user='".$user.
"' AND pass='".$pass."'";
?>
<?php
$user = "'; DROP TABLE users −− ";
?>
最终变成
SELECT id
FROM utente
WHERE user='';
DROP TABLE users --"' AND pass=''
Blind injection
详见 SQL注入:盲注
布尔盲注
布尔Bool,意味着我们在将注入一些布尔逻辑判断,根据逻辑判断的结构来触发页面发生变化,进而判断语句是否成功执行;
例如:如果成功,则页面正常显示;否则就什么都不显示
报错注入
报错注入是将有价值的信息通过报错的方式显示输出。
这里需要与普通的注入区分一下:
- 普通的注入是将查询到的信息正常得输出到页面上来
- 报错注入则是构造特定的MYSQL函数语法错误,同时将所需要的信息拼接到错误返回信息中,然后再返回到页面上来
延时注入
时间盲注是一种边信道攻击(Side-channel attack SCA),这原本是密码学的一个概念,这种攻击用于加密算法的破解,它基于密码系统的物理实现而不是加密算法其本身,比如加密所需要的时间,所消耗的电子设备的一切资源,甚至电磁泄露或者声音这些信息来反过来分析算法本身。
个人认为SCA的一个特点,就是要刻意放大一个正常的操作的影响,以此制造明显的特征。
那么在时间盲注中,我们无法像普通注入那样,得到查询的数据,也无法通过布尔盲注或者报错盲注在页面上显示的获得任何信息,而是通过通过判断语句,同时增加成功注入所使用的时间,最后通过判断响应时间的变化来判断是否成功执行。
文件读写注入
详见 SQL注入:文件读写
读
利用Mysql内置的用于读取文件的函数来读取文件内容
写
一般的利用就是将一句话木马注入到服务端,然后再用蚁剑等工具连接