前言
提示:这里可以添加本文要记录的大概内容:
本文开始一系列的web渗透入门系列,帮助新手快速入门,其实最开始应该先介绍信息收集,信息收集-渗透测试的灵魂,这里的话我打算放到系列最后讲。
学习之前需要有php编程基础,sql基础,需要知道怎么从php执行sql语句,本系列教程重点会在细节上把原理讲的很仔细。也就是会经常扯得比较远,会把涉及到的很多概念(主要是一些自己刚开始学习的时候没懂或者被人经常问到的概念)都仔细说明一下。
php: 变量,$_GET,$_POST 这些传参,再加上php连接数据库执行数据库代码这些知识
学习目标: 知道sql注入中为什么用户输入的数据能够被当做sql代码执行
进阶目标: 在总结中发的靶场地址里能够通过sql注入得到当前数据库库名,库名是 maoshe
一、Sql注入是什么?
一句话,sql注入的本质就是:将用户输入的代码当做sql代码执行。
这里可以理解为:我们就是用户,把我们提交给服务器的数据,在服务器的数据库中被当做了数据库代码执行。
二、注入流程分析
1.为什么用户的代码能够被当做sql代码执行?
代码如下(示例):
<?php
$id = $_GET['id'];
$sql = "select * from article where id = $id";
$conn->query($sql);
?>
分析一下上面这段代码
首先 $conn 没有定义,这段代码直接复制粘贴肯定报错
但是这个不是我们要分析的问题的关键,$conn是一个 mysqli_connect() 的连接对象
用来进行数据库连接的。自己写代码的时候一定要写上。
第一行代码 从 get 传参中获取了 id 的值赋值给 $id
get传参
get传参怎么传?
在url后面加问号
http://url/?id=123
这样的形式
那么想要传两个怎么办?
http://url/?id=123&name=qwe
这样就传了两个,也就是用&符号连接
回到正题
假设这里我们get传参传了id=123
那么对于$id变量,$id=$_GET['id']=123
这样的写法在代码中可不可行我没试过,感兴趣的可以自己试试。
再下一句
$sql = "select * from article where id = $id ";
双引号变量解析
众所周知,在php中 双引号("") 里面的内容中的变量会被解析
也就是说,比如
$a = "吃饭";
$b = '我在$a';
echo $b;
这段代码的运行结果会是: 我在$a
大家可以自己试试
$a = "吃饭";
$b = "我在$a";
echo $b;
上面这段代码的运行结果会是: 我在吃饭
因为是用双引号包起来的,所以变量会被解析
好的,回到正题
那么上面我们的代码中
$sql最后的结果就应该是:
select * from acticle where id = 123
大家可以把这段代码放到 sql 中去执行一下
执行的时候记得加分号
当然,如果你真的执行了,你会发现要么报错,要么没结果
什么情况下会报错呢?
1 没有创建 article 这个表
2 创建了这个表,但是表里没有id字段
那什么情况下什么结果都没有?
当然是表里面没有数据或者没有id等于123的数据的时候。
那么我们换一种思路
id=123是我们传上去的吧
那么我们可不可以传一些其他东西呢?
例如说,我们传上去的是这么一串
id=1 union select 1,2,database()
那么最后执行的sql语句会是什么?
我们来拼接一下
select * from article where id = 1 union select 1,2,database()
最后就变成了这样
这时候可能会有小伙伴好奇
database() 我知道,会输出当前库名嘛
那前面加个1,2是干啥?
这就要说到联合查询的特性了
联合查询
联合查询可以用作查询两个表的内容联合输出
注意!!! 我这里只弄内容,字段名啥的我就不写了
假设表1的内容是
1 | 2 | 3 |
a | b | c |
表2 的内容是
吃 | 喝 | 拉 |
撒 | 5 | 6 |
如果联合查询这两个表
最后的结果,显而易见,就是把他们联合起来输出
那么哪个在前面呢?
我们来看下联合查询的语法
select 字段 from 表 union select 字段 from 表
哪个表写在前面,哪个表的内容先输出
那么我们继续
联合查询有一个必要的要求
就是前后两个查询的字段数必须要相同
我们一般写sql怎么写
select * from 表;
那这里的 * 是什么
这里的 * 代表所有字段
也就是根据后面要查询的表来判断的
那个表里的字段是什么,这里的*就是什么
这样的话可以省略
也就是说假设有一个表名字叫 user ,长这样
那么对于它而言
select * from user
等价于
select id,name,age,sex from user
这二者是等价的
那么假设我要用这个表联合查询查询出来当前库名
我直接这样可以吗?
select * from user union select database()
当然不可以
因为前后字段数不一致
可以自己试一下,对了,记得加分号
那么我们就需要补位
user表一共四个字段
我们要查database() 有一个字段了
那就再加个1,2,3用来补一下就可以了
也就是
select * from user union select 1,2,3,database()
诶,database()我一定要写在最后吗?
那肯定不是,在平常的联合查询中
这四个位置我想写在哪都行
但是如果在SQL注入中呢?
在sql注入中要根据回显点在哪里去判断
回显点又是什么鬼东西?我们后面讲到注入的时候会提到
2.注入原理
那么根据上面的知识,我们是不是可以查询到库名了?
当然不太行
再回顾一下原本的用来注入的代码
<?php
$id = $_GET['id'];
$sql = "select * from article where id = $id";
$conn->query($sql);
?>
这里我们想要联合查询,是不是必须要知道 article 表的字段才可以?
两种方法
第一种方法
id=1 union select database()
id=1 union select 1,database()
id=1 union select 1,2,database()
一个一个测试
这样效率很显然太慢了
我们可以借助: order by 语法
order by 语法可以这么用
比如说
select * from user order by 1
如果不报错,说明至少有一个字段
select * from user order by 10
如果报错说明没有10个字段
这话说的可能有点别扭,我们来做一个小实验
这个表一共是4个字段,当我们测试到 order by 5的时候报错了
4 不报错,5报错,这样就可以得到一共就5个字段了
这时候可能有小伙伴要问了,你这不是还跟上面那个一个一个试差不多吗?
别急,上面的方法,不管少了,还是多了,都会报错
但是这个方法,少了不会报错
我们可以利用2分法嘛
先 oder by 1
order by 10
oder by 50
直到它报错
测试出来范围
然后减半缩小去进行测试
用算法中的思想来看
前一个需要测试 n 次(n代表字段数)
这个只需要 logn次(底是2)
总结
以上就是今天要讲的内容,本文仅仅简单介绍了sql注入的原理,没有进行实战,这里的话放一个掌控安全的公开靶场 宠物猫 异国短毛猫 纯种猫 加菲猫 波斯猫 辛巴猫舍 纯种双色/梵文波斯、异国小猫 CFA注册猫舍
就上面这个连接,大家可以试试自己根据上面的原理去尝试注入一下得到库名