什么是SQL注入
01
SQL注入原理
当访问动态网页时,以MVC框架为例,浏览器提交查询到控制器(①),如是动态请求,控制器将对应sql查询送到对应模型(②),由模型和数据库交互得到查询结果返回给控制器(③),最后返回给浏览器(④)。
现行的网页、程序多是基于以上这样的设计模式。正常情况下,这种网站内部直接发送的SQL请求不会有危险,但实际情况是很多时候需要结合用户的输入数据动态构造 SQL语句,如果用户输入的数据被构造成恶意 SQL 代码,Web 应用又未对动态构造的 SQL语句使用的参数进行审查,则会造成一种很常见的漏洞——SQL注入。
SQL注入是现在最常见最简单的漏洞,SQL注入就是通过把恶意SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令的目的。简单一点说就是将原本输入的查询变量的地方插入了SQL查询语句,破坏原SQL语句从而来实现自己的SQL查询。
SQL注入与其他常见Web漏洞一样,均是由外部可控的参数引起的。由于程序没有经过任何过滤就将外部可控的参数拼接进入SQL语句,直接放入数据库执行,达到了欺骗服务器执行黑客恶意SQL命令的目的。
02
SQL注入带来的威胁
03
SQL注入形式
SQL注入可以简单分为普通注入和编码注入两种。
a. 普通注入
●数字型SQL注入
当程序的变量没有做处理而直接拼接在SQL注入语句中,没有单引号的保护,就容易造成SQL注入。
$id = $_GET['id'];
$query = "select username,password from user where id=$id";
攻击者可通过构造以下输入来达到SQL注入
输入:1 or 1=1
“select username,password from user where id=1 or 1=1”
●字符型SQL注入
当程序的变量没有做处理而直接拼接在SQL注入语句中,虽然有单引号的保护,但我们如果能闭合SQL,也就产生了SQL注入漏洞。
$id = $_GET['id'];
$query = "select username,password from user where id='$id'";
攻击者可通过构造以下输入来达到SQL注入
输入:1' or 1=1 --
"select username,password from user where id='1' or 1=1 -- "
b. 编码注入
编码注入包括宽字节注入、URLdecode注入等,利用程序的编码规则缺陷,输入与转码函数不兼容的特殊字符,导致输入的字符拼接成为了恶意的SQL语句。
●宽字节注入
宽字节注入是利用mysql的特性,即不同编码方式造成的转换错误。例如 PHP的编码方式为UTF-8,而 mysql的被设置了使用GBK编码时,由于mysql在使用GBK编码的时候,会产生宽字节自主漏洞,即将两个ascii字符误认为是一个宽字节字符(如汉字)。
假设网站对输入使用addshlashes()函数,即对GET、POST、COOKIE、REQUSET 提交的参数中的单引号(')、双引号(")、反斜杠(\)与 NUL(NULL 字符)会有以下转义操作:
单引号(')= (\') 双引号(") = (\") 反斜杠(\) = (\\)
这时输入'时网页的处理将会是:
' --> \' --> %5C%27
当在前面引入一个ASCII大于128的字符(比如%df),url编码变为:
%df '--> %df \ ' --> %df%5c%27
若MYSQL使用gbk编码的话,%df%5C会被当作一个汉字处理,从而使%27(单引号)成功绕过
%df '--> %df \ ' --> %df%5c%27--(GBK)-->運'
● 二次URLdecode注入
二次URLdecode注入即对用户提交的变量进行二次URL解码而导致的SQL注入。现在绝大多数基于PHP的Web程序都会使用addslashes()等过滤函数对用户提交的变量等进行过滤,如果某处同时又采用了urldecode()函数进行了url解码,那么将会大概率的导致URLdecode注入。
PHP本身在处理提交的数据之前会进行一次解码,而urldecode()函数是相对应地对已编码的URL进行解码。
如果我们构建一个输入:
%27--(php编码)-->%2527-->%2527
php第一次解码
%2527-->%27
URLdecode二次解码,成功注入'
%27-->'
审计方法
01
敏感函数回溯法
a. 审计流程:
b. 审计重点函数
函数类型 | 举例函数或敏感关键词 |
---|---|
SQL操作类 | Select……, mysql_query… |
文件操作类 | Move_uploaded_file,copy,/upload/等 |
命令执行类 | System,popen等常见的系统命令做关键词 |
代码执行类 | eval,preg_replace等 |
引起XSS类 | echo等 |
…… | …… |
下面重点列出在PHP中可能设计SQL注入的相关函数。
c. PHP中常见的数据库扩展
Mysql,Mysqli extension, PDO(Php Data Objects)
①mysql扩展:
从PHP5.5.0起此扩展已被废弃,并且从PHP7.0.0开始被废除,使用mysqli或者pdo-mysql进行替代。
mysql常用函数:
Mysql_connect | 连接数据库 |
---|---|
Mysql_query | 执行SQL语句 |
Mysql_fetch_array/mysql_fetch_assoc | 返回从结果集取得的行生成的数组 |
Mysql_db_query | 发送一条mysql查询 |
②Mysqli扩展:
mysqli扩展允许我们访问mysql4.1及以上版本提供的功能,是目前PHP开发中常用的数据库操作扩展之一。
mysqli常用函数:
Mysqli(hostname,username,password,db_name) | 实例化mysqli对象 |
---|---|
Mysqli::connect_error | 检测连接是否成功 |
mysqli::query | 执行SQL查询 |
Mysqli::result::fetch_assoc/mysqli_result::fetch_array | 返回从结果集取得的行生产的数组 |
Mysqli::prepare | 预编译 防止SQL注入 |
③PDO扩展:
PHP数据对象(PDO)扩展为PHP访问数据库定义了一个轻量级的一致接口。
PDO提供了一个数据访问抽象层,即不管是用那种数据库,都可以用相同的函数(方法)来查询和获取数据。
P DO随PHP5.1发行,在PHP5.0中的PECL扩展中也可以使用,无法运行于之前的PHP版本。PDO扩展也为开发者经常使用的扩展,例如thinkphp框架就是使用的PDO扩展。
PDO常用函数:
PDO(dsn,username,password) | 数据库连接 |
---|---|
PDO::query | 执行SQL语句 |
PDO::statement::fetch/fetchAll | 取出结果集中数据 |
PDO::prepare | 预处理 |
PDO::statement::execute | 执行预处理语句 |
PHD::exec | 执行一条SQL语句并返回受影响的行数 |
02
功能点定向审计
a. 审计流程
b.审计重点功能点
功能 | 出现漏洞类型 |
---|---|
文件上传功能 | 任意文件上传 |
查询/文章功能 | SQL注入 |
密码找回功能 | 逻辑漏洞 |
登陆认证功能 | SQL注入,逻辑漏洞 |
评论功能 | XSS漏洞 |
…… | …… |
SQL注入审计方法总结
1. 快速定位函数,关键字如下:
Function:
query
fetch_assoc
select|update|delete|insert|mysql_query|mysql_db_query|
query|execute|exec|get_one
Mysql_query
Mysql_fetch_assoc
Wide:
mysql_query
mysql_set_charset
mysqli_set_charset
iconv("GBK","UTF-8")
2. 正则快速查询
通过一些查询语句的特征,用正则匹配源代码中的SQL语句所在位置
3. 辅助工具
使用Seay源代码审计系统的自动审计功能来辅助我们快速找到SQL注入可能存在的位置。
4. 功能点寻找输入参数
在前段页面中查找各种功能点是否存在可控输入参数,例如文章、用户资料、登陆处等等。
审计实例
01
实验准备
CMS:MetInfo 6.0.0
Php:5.4
Mysql:5.4
02
分析过程
1.定位函数
使用phpstorm中的ctrl + shift + F 选择Regex正则搜索,快速定位敏感函数
(update|select|insert|delete|).*?where.*=\{
下图中,id参数直接进行了拼接,有可能存在SQL注入的,这里是两处sql执行操作一处是select,一处是update。
$query="select * from {$_M[table][feedback]} where id={$_M[form][id]}";
id直接拼接变量名
$query="update {$_M[table][feedback]} set useinfo='$useinfo' where id={$_M[form][id]}";
只对useinfo变量做了单引号的包裹,对_M[form][id]没有包裹处理。
通过MetInfo官方文档可知_M[form][id]是通过GET,POST,COOKIE 提交而来,且文档中说_M[form][id]是经过过滤的数组。
看一下对$_M[form][id]的过滤处理,发现只是对其进行了addslashes转义
结合使用来看对_M[form][id]的处理,发现虽然对传入的参数进行了过滤但是此处的SQL语句是直接拼接了参数并没有‘’或者“”的包裹,所以_M[form][id]存在数字型注入,而用于防范字符型注入的过滤函数addslashes失效。
03
实验验证
尝试注入,根据dosentemail函数所在的文件夹来构造:
n: 目录 c: controller a: action id:通过get传递的参数
Sleep()时间注入
?n=feedback&c=feedback_admin&a=dosentemail&id=1 and sleep(5)
页面延时5s,注入成功。
有关SQL注入的代码审计的内容到此就告一段落了,下一课将给大家带来更多漏洞的代码审计讲解,敬请期待~
待续