前言
接下来的三关就是盲注题了,盲注是SQL注入中最重要的技能,当页面中的数据无法回显到前端时,就需要借助盲注手段来判断。同时,盲注往往也带有着工具或脚本的使用,我们也可以借此机会进行学习。
ps.整理了关于sqli-labs靶场的博客,将分散的几篇整合了起来。
关于盲注
盲注的使用要基于用户自己可以对输入进行控制,且要能接入进执行代码中。盲注分为两种:布尔盲注和时间盲注。
- 布尔盲注:通过返回true与false的不同,导致页面的回显不同进行判断,之前使用and 1=1与and 1=2判断注入点也是用此原理
- 时间盲注:通过返回true与false的不同,导致页面的加载时间不同进行判断,一般使用if语句
less-8
标题就是基于单引号的布尔盲注,那我们先将id和单引号一起输入进去,然后发现这是一点报错都没有啊。我们可以查看源码,发现报错这一行(下列源码的第三行)被注释了:
1 | echo '<font size="5" color="#FFFF00">'; |
那么这种情况下我们就需要使用盲注了。先用and 1=1和and 1=2判断,发现前者会回显一个“You are in….”,而后者依然是什么都没有,那么我们就知道了,当返回true时,页面会回显一句话,反之则什么都不回显。
既然掌握了原理,那么我们就可以开始准备注入了。下面我们来介绍我们需要用到的函数:
- length():返回字符串的长度
- substr(str,start,len):截取“str”字符串从第“start”位开始的“len”长度的字符
- ascii():返回字符的ascii码值,关于ascii码值可以参考下图:
正式开始注入,我们使用and或者or来对代码进行连接,一定要注意,and的true返回条件是左右两边都为真,or的true返回条件只需要有一边为真就行了,因此在这里前面id如果存在于数据库之中要用and,不存在则用or。由于刷POST题时犯了个蠢,在这里先提一下。
由于我们已经知道数据库的名称为security了,所以我们就不从猜数据库开始了,可以用length(database())
先确定数据库名称的长度,再一位一位进行猜测。这里我们直接开始猜表。首先是表的个数:
1 | n=(select count(table_name) from information_schema.tables where table_schema='数据库名') |
n可以替换为任何数,后面的查询语句也可以替换来查询表的列数等等。当返回true时,也就是我们看到“You are in….”时,代表着我们猜对了,我们就可以进行下一步猜表名了:
1 | ascii(substr((select table_name from information_schema.tables where table_schema='security' limit 0,1),m,1))=n |
这里我们用limit限制了查询的表为数据库中的第一个表,这个函数验证的是第一个表的第m个字段的ascii码是否为n,这个等号也可以替换为大于号或者小于号来帮助我们缩小范围。通过这种方法我们可以逐步推出第一个表的名字为emali。但是一个个试的效率是十分低的,我们可以用二分法来提高效率,或者学习如何使用脚本或者工具来帮助我们提高效率,这里就不展开了。
得到表的名称后,我们可以更改以上代码的查询语句一步步查询出我们想要的数据了。
less-9
这关根据标题要使用时间盲注,但我先用了布尔盲注进行测试,结果发现无论返回true还是false,页面都会回显“You are in….”,查看源码就可以发现确实如此,因此这个靶场我们就要使用时间盲注了。
使用时间盲注时我们会用到if语句和sleep()函数进行配合:
- if(expr1,expr2,expr3):当语句1返回的结果为true时,执行语句2,反之则执行语句3
- sleep(x):延迟执行脚本x秒
时间盲注有个很显著的优点,不需要通过页面回显来判断,在页面没有回显内容的时候,时间盲注就可以很好的帮助我们进行判断。例如我们同样来判断数据库中表的个数:
1 | if(n=(select count(table_name) from information_schema.tables where table_schema='数据库名'),sleep(3),1) |
显而易见,当数据库中表的个数为n时,脚本将暂停3秒再执行,否则什么都不会发生,这样我们就可以通过观察网页的加载时间来判断返回值是true还是false。
这样一来,我们只要将if()中的语句1进行更改,就可以像less-8一样逐步获得数据库中的信息了。
less-10
把单引号换成双引号,然后把less-9的过程走一遍,然后你就成功又做出了一关,这真是令人感到高兴呢(bushi)。
less11-16
不说废话,直接上代码,以less-12的代码为例,下面也以less-12为例进行分析,其他的关卡都可以凭借题目进行注入,因此不再详解:
1 | if(isset($_POST['uname']) && isset($_POST['passwd'])) |
我们可以发现从本质上来说,POST与GET的差别不是很大,作为HTTP请求的两种基本方法,他们最大的区别就在于GET是把参数写进了URL中,从指定的资源请求数据,而POST是向指定的资源提交要被处理的数据,是要我们上传数据的。两者具体的区别可以通过下面这篇文章学习:
HTTP:GET对比POST
进入靶场我们可以发现多出了一个输入框,现在我们可以输入一个账号密码了,而不是在域名中输入一个id。通过上面的代码,比如当我们输入账号为1,密码为1后,就相当于POST了一个$uname=1&$passwd=1
,并且传入sql中进行查找,不过显然数据库中没有这一组账号密码,因此提示我们登录失败了。
注入的关键还是那行查询代码。我们发现查询代码中首先对我们POST的两个变量添加了双引号并且打上了括号,接着可以发现$uname在$passwd的前面,这就意味着我们从输入账户的那一栏添加注释可以屏蔽掉密码栏。知道这些后,我们就可以开始进行注入了
注意:在使用注释“–+”时用空格代替“+”号,不然会报错,或者直接使用其他注释方法。
接下来就是使用order by爆字段,可以发现这次的字段数为2:
这里也推荐使用火狐中的插件hackbar,可以去网上自行搜寻旧版本免费试用,新版本需要收费。
到此为止,剩下的事情就是熟悉的爆库了。熟练运用之前靶场中学会的查询、盲注等多种方法。特别提示,如果在做第十四关时发现标题和第十三关一样,那请你千万不要相信标题。
less-17
第十七关它又不一样了。首先,当你在进行初始测试时,它会对你进行无情嘲讽,让你上头,但接着你发现无论怎样在账户中注入,都只有对你的嘲讽出现。你不得不冷静下来,翻开源码:
1 | function check_input($value) |
我们发现了这段check_input函数,并且还发现了$uname这个变量进行了一次check_input函数。接下来我们详细分析一下这个函数:
第一个判断,如果输入的值不为空,截取最多15个字符,这意味着我们想进行注入的话只能保证字符在15个以内,现在对于我们来说这几乎是不可能的。
接着判断当前设置中get_magic_quotes_gpc是否开启了,如果开启了的话将会为通过get、post与cookie传来的数据中的引号前加入反斜杠,相当于自动为数据进行了一次addslashes(),因此如果开启,则通过stripslashes()函数删去其中的反斜杠。
最后判断输入的值是否为十进制纯数字,如果不是则将值中的特殊字符转义,否则取整。以下这些字符会被mysql_real_escape_string()转义:\x00(空值),\n(换行),\r(回车) ,反斜杠,单引号,双引号,\x1a(十六进制)
这段函数对用户名实行了保护,因此我们不能从用户名中入手了,但是翻看源码,它居然没有保护密码。因此我们只要获得一个合法的用户名,就可以从密码栏入手了:
1 | $uname=admin&$passwd=' order by 3# |
如此一来,我们就可以开始爆库了。