MySQL注入部分知识讲解
Posted 佛大信息安全
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL注入部分知识讲解相关的知识,希望对你有一定的参考价值。
目录
1.万能密码登录
2.猜解注入获取密码
3.UNION注入绕过无密码字段查询
万能密码登陆
首先来一个登陆逻辑来看看:
<?php
header("Content-Type: text/html;charset= utf-8");
const USERNAME = "root";
const PASSWORD = "root";
const HOST = "localhost";
const DB = "test";
if($_POST){
$user = $_POST['user'];
$pwd = md5($_POST['pwd']);
$pdo = new PDO("mysql:host=".HOST.";dbname=".DB,USERNAME,PASSWORD);
$sql="select * from user where user='$user' and pwd='$pwd'";
echo $sql."<br/>"; //为了讲解方便,加入了sql语句的输出
$rs = $pdo -> query($sql);
$row = $rs -> fetch();
if($row){
echo "登陆成功";
}else{
echo "登录失败";
}
die();
}
?>
随便输入用户名和密码
当然不负期望的,登录失败了咯
输入的值拼接成如上sql语句。
这时候要想成功进行登录,就必要$row有数据,select语句where必须为真,那么拼接后的sql语句只需要满足where为真的条件即可,这里pwd字段进行了md5加密,无法构造,所以从user字段入手。
一般网站的密码不会明文存储,都会进行如md5的加密,加盐乱七八糟,不可逆的。
用户名输入 admin’ or ‘1’=’1 ,密码随便。
拼接成这样,因为and优先级大于or,所以where结果是true or (true and false)=true,登录成功。
上面是sql注入的原理入门。
猜解注入获取密码
第一步:先构造出登录成功的语句。(为了方便,使用发包方式)
跟在浏览器输入是一样的,不会发包看得懂就行。
因为#是mysql的注释,所以输入admin’#(假设数据库中有admin这个user),相当于where user=’admin’#,user='admin' 为true登录成功并且井号注释了后面的语句,所以密码可以随意。像刚刚第一个例子,你输入的语句,只要能够正确拼接就行,随便你写什么。
第二步:猜测密码字段
现在我是不知道表有什么字段,要获取密码就要把密码字段猜测出来,但是我知道登录的时候肯定用的是用户表。所以这里不需要猜测账号在什么表里。常见的密码字段有pwd,pass,password,以及与form表单中相同的字段。另外也可以使用字典爆破。
现在输入admin' and pass!=''#
假如pass不是密码字段,不存在这个字段的话,一般就会报错或者本来是登录成功的变成登录失败,因为密码一般不为空,所以字段存在就为true,而user=’admin’也是true的,所以可以利用这个特性判断字段是否正确。
这里直接用pwd字段了。
得出pwd是密码字段
第三步:获取密码长度
Mysql中有一个length函数,用来获取当前字段的值的长度。
因为where是否为true取决于pwd字段length(pwd)>20是为true的,所以密码大于20位。
为了加快效率,用二分法就能获取密码的长度。(二分法就是数字游戏中的猜数字,1-100,猜50,小了50-100猜75这样)
失败,密码在21-40位之间。
以此类推,得到密码位32位。
第四步:猜解密码
Mysql中mid函数MID(字符串,位置,长度)。ASCII函数获取一个字符的ascii值,常用的acsii的值有‘0’为48,‘A’为65,‘a’为97。
因此输入admin' and ascii(mid(pwd,1,1))>65#
发现第一位密码为数字。
小于52。
不大于50。
最终确定是50,对应的字符为’2’,这就是第一位密码,
接下来31位也如此处理。
最后得到结果
21232f297a57a5a743894a0e4a801fc3。
虽然说MD5不可逆,无法解密,但是可以利用彩虹表,就是先把一些密码加密成md5值,然后找到对应的原文。
密码为admin。
登陆成功。
UNION注入绕过无密码字段查询
但是,有点经验的程序员一般用下面这种代码验证。
<?php
header("Content-Type: text/html;charset= utf-8");
const USERNAME = "root";
const PASSWORD = "root";
const HOST = "localhost";
const DB = "test";
if($_POST){
$user = $_POST['user'];
$pwd = md5($_POST['pwd']);
$pdo = new PDO("mysql:host=".HOST.";dbname=".DB,USERNAME,PASSWORD);
$sql="select * from user where user='$user' and pwd='$pwd'";
echo $sql."<br/>"; //为了讲解方便,加入了sql语句的输出
$rs = $pdo -> query($sql);
$row = $rs -> fetch();
if($row['pwd']===$pwd){
echo "登陆成功";
}else{
echo "登录失败";
}
die();
}
?>
这个与(1)的区别就是密码验证不是判断是否有数据返回,而是通过用户查询的这条记录,然后判断传入的密码是否跟数据库中的相同。
如果依旧输入admin'#
发现登陆失败,因为虽然构造sql语句成功,也返回了结果,但是这里的判断还是没有通过,因为密码不正确。
不同第一种只要有数据就判断通过,这种也验证的密码的正确性。那应该怎么才能登录成功?这里能想到,如果能控制返回结果不就行了么?
第一步:判断表中有多少个字段
使用order by可以进行判断表中有多少个字段,order by除了可以按字段名排序外,也可以按字段的列数来排序。如admin' order by 3#
这里表示按第3列排序
但是如果这里写4
就报错了。
证明用户表有3个字段。如果列数超过表中的字段数,mysql会报错,所以能判断字段数。
第二步:使用UNION查询
在mysql里union可以把两条语句查询的结果合并,但两个结果集字段的列数要相等。
union相关知识:
http://www.runoob.com/mysql/mysql-union-operation.html
这里我们先清除语句1的结果(使得语句1结果集为空,where条件为false),合并后的结果就只是语句2了
select null,null,null。上面这条语句返回的就是最终结果
这里有一个问题就是,我不知道密码账号分别在第几个字段,所以这里可以将所有字段都填入密码。
首先将随便一个字符串md5加密
Md5(’123’)=’ 202cb962ac59075b964b07152d234b70’
最后用户名输入:
admin' and 0 union select '202cb962ac59075b964b07152d234b70','202cb962ac59075b964b07152d234b70','202cb962ac59075b964b07152d234b70'#
密码输入:123
为什么这样能登录成功?
这个语句的结果
判断的时候,输入的密码正好是123的md5值,登录成功。
END
文案:Zn风扇
排版:黄浚杰
校队:色鹿
以上是关于MySQL注入部分知识讲解的主要内容,如果未能解决你的问题,请参考以下文章
48-mysql-Navicat数据库查询题目讲解(多表操作)python操作MySQLsql注入问题pymysql模块增删改查数据操作
Mysql DBA 高级运维学习笔记-DQL语句之select知识讲解