一、时间注入。
测试地址:自建靶场 http://192.168.57.128:1237/time/time.php?id=1
1.测试过程。
当访问该地址时,页面返回yes,在网址的后面加上单引号,再次访问,页面返回no。这与Boolean注入非常相似,但是我们将采用另外一种注入方法解决——时间盲注。
时间盲注是利用sleep()或benchmark()等函数让Mysql的执行时间边长,它多与IF(expr1,expr2,expr3)结合使用,此if语句含义是:如果expr1是TRUE,则if()的返回值为expr2;否则返回值则为expr3。所以判断数据库库名长度的语句则为:
if (length(database())>1,sleep(5),1)
上面这行语句的意思是,如果数据库库名的长度大于1,则MySQL查询休眠5秒,否则查询1。查询1的结果,大约只有几十毫秒,根据burp中页面的响应时间,可以判断条件是否正确,我们构造如下SQL语句,查询结果如下图所示:
'and if (length(database())>1,sleep(5),1)--+
由以上的返回结果可知,页面的响应时间超过了5s,表明页面成功执行了sleep(5),所以长度是大于1的,接下来将长度改为10,结果如下所示:
可以看出,执行的时间是0.404秒,表明页面没有执行sleep(5),而是执行了select 1,所以数据库库名长度大于10是错误的。通过多次测试,就可以得到数据库库名的长度。得出数据库库名长度后,我们开始查询数据库库名的第一位字母。,使用substr函数,这时的语句修改为:
if (substr (database(),1,1)='s',sleep(5),1)
页面5秒之后才返回了,说明数据库库名第一位字母是s,以此方式可以推断出数据库的库名、表名、字段名和具体数据。
2.代码分析。
<?php
$con=mysqli_connect("192.168.57.128","root","123","security");
if (mysqli_connect_errno())
{
echo "连接失败: " . mysqli_connect_error();
}
$id = $_GET['id'];
if (preg_match("/union/i", $id)) {
exit("<htm><body>no</body></html>");
}
$result = mysqli_query($con,"select * from users where `id`='".$id."'");
$row = mysqli_fetch_array($result);
if ($row) {
exit("<htm><body>yes</body></html>");
}else{
exit("<htm><body>no</body></html>");
}
?>
在时间注意注入页面中,程序获取GET参数ID,通过preg_match判断参数ID中是否存在Union危险字符,然后将参数ID拼接到SQL语句中。从数据库中查询SQL语句,如果有结果,则返回yes,否则返回no。当访问该页面时,代码根据数据库查询结果返回yes或no,而不返回数据库中的任何数据,所以页面上只会显示yes或no,和Boolean注入不同的是,此处没有过滤sleep等字符。
二、堆叠查询注入。
测试地址:自建靶场 http://192.168.57.128:1237/sql/dd.php?id=1
1.测试过程。
堆叠查询可以执行多条语句,多语句之间以分号隔开。堆叠查询就是注入就是利用这个特点,在第二个SQL语句中构造自己要执行的语句。
首先访问id = 1’,页面返回Mysql错误,如下图所示。
再访问id = 1’%23,页面返回正常结果。
在此我们尝试堆叠注入的方式来验证数据库名。我们构造如下的SQL语句,其实这里使用依然是时间盲注的语法结构,如下所示:
';select if(substr(database(),1,1)='s',sleep(5),1)%23
因为该数据库名的第一个字母是s,所以页面返回的时间等于5s,如下图所示:
后面获取数据的操作与时间注入的一样,通过构造不同的时间注入语句,可以得到完整的数据库的库名、表名、字段名和具体数据。执行以下语句,就可以获取数据库的表名。
';select if(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)='e',sleep(5),1)%23
2.代码分析。
<?php
try {
$conn = new PDO("mysql:host=192.168.57.128;dbname=security", "root", "123");
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $conn->query("SELECT * FROM users where `id` = '" . $_GET['id'] . "'");
$result = $stmt->setFetchMode(PDO::FETCH_ASSOC);
foreach($stmt->fetchAll() as $k=>$v) {
foreach ($v as $key => $value) {
echo $value;
}
}
$dsn = null;
}
catch(PDOException $e)
{
echo "error";
}
$conn = null;
?>
在堆叠注入页面中,程序获取GET参数ID,使用PDO的方式进行数据查询,但仍然将参数ID拼接到查询语句,导致PDO没起到预编译的效果,程序仍然存在SQL注入漏洞。使用PDO执行SQL语句时,可以执行多语句,不过这样通常不能直接得到注入结果,因为PDO只会返回第一条SQL语句执行的结果,所以在第二条语句中可以用update更新数据或者使用时间盲注获取数据。访问dd.php?id=1";select if (ord(substring (user () , 1,1) ) =114, sleep (3), 1) ;%23时,执行的SQL语句为:
select * from users where 'id' = '1';select if(ord(substring(user(),1,1))=114,sleep(5),1);#
此时SQL语句分为了两条,第一条Select * from users where ‘id’ = '1’是代码自己的select查询,而select if(ord(substring(user(),1,1))=114,sleep(3),1);#则是我们构造的时间盲注的语句。